纲要
- 背景与目标
- 整体架构概览
- 基于
ELK的日志收集与Kibana可视化- 数据准备与索引模式创建
- 日志数据推送链路
go-zero服务日志配置Kibana可视化实战:饼图与柱状图
- 基于
Prometheus的服务性能监控Prometheus与ELK的功能对比Prometheus核心组件与架构go-zero服务集成Prometheus指标暴露Alertmanager告警规则配置示例
- 总结与最佳实践
背景与目标
在微服务架构下,服务实例往往分散在多台机器上,传统的单机监控手段已无法满足需求。我们需要一套能够聚合日志、可视化分析,同时又能实时监控服务性能指标的方案。本文将基于 go-zero 框架,结合 ELK(Elasticsearch、Logstash、Kibana)与 Prometheus,演示如何从零构建日志可视化仪表盘,并对各个服务进行性能监控。
整体架构概览
下图展示了 go-zero 微服务中日志与监控的收集链路:
#mermaid-svg-fzwx8GOZVHqXk2TH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fzwx8GOZVHqXk2TH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fzwx8GOZVHqXk2TH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fzwx8GOZVHqXk2TH .error-icon{fill:#552222;}#mermaid-svg-fzwx8GOZVHqXk2TH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fzwx8GOZVHqXk2TH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fzwx8GOZVHqXk2TH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fzwx8GOZVHqXk2TH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fzwx8GOZVHqXk2TH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fzwx8GOZVHqXk2TH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fzwx8GOZVHqXk2TH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fzwx8GOZVHqXk2TH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fzwx8GOZVHqXk2TH .marker.cross{stroke:#333333;}#mermaid-svg-fzwx8GOZVHqXk2TH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fzwx8GOZVHqXk2TH p{margin:0;}#mermaid-svg-fzwx8GOZVHqXk2TH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fzwx8GOZVHqXk2TH .cluster-label text{fill:#333;}#mermaid-svg-fzwx8GOZVHqXk2TH .cluster-label span{color:#333;}#mermaid-svg-fzwx8GOZVHqXk2TH .cluster-label span p{background-color:transparent;}#mermaid-svg-fzwx8GOZVHqXk2TH .label text,#mermaid-svg-fzwx8GOZVHqXk2TH span{fill:#333;color:#333;}#mermaid-svg-fzwx8GOZVHqXk2TH .node rect,#mermaid-svg-fzwx8GOZVHqXk2TH .node circle,#mermaid-svg-fzwx8GOZVHqXk2TH .node ellipse,#mermaid-svg-fzwx8GOZVHqXk2TH .node polygon,#mermaid-svg-fzwx8GOZVHqXk2TH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fzwx8GOZVHqXk2TH .rough-node .label text,#mermaid-svg-fzwx8GOZVHqXk2TH .node .label text,#mermaid-svg-fzwx8GOZVHqXk2TH .image-shape .label,#mermaid-svg-fzwx8GOZVHqXk2TH .icon-shape .label{text-anchor:middle;}#mermaid-svg-fzwx8GOZVHqXk2TH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fzwx8GOZVHqXk2TH .rough-node .label,#mermaid-svg-fzwx8GOZVHqXk2TH .node .label,#mermaid-svg-fzwx8GOZVHqXk2TH .image-shape .label,#mermaid-svg-fzwx8GOZVHqXk2TH .icon-shape .label{text-align:center;}#mermaid-svg-fzwx8GOZVHqXk2TH .node.clickable{cursor:pointer;}#mermaid-svg-fzwx8GOZVHqXk2TH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fzwx8GOZVHqXk2TH .arrowheadPath{fill:#333333;}#mermaid-svg-fzwx8GOZVHqXk2TH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fzwx8GOZVHqXk2TH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fzwx8GOZVHqXk2TH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fzwx8GOZVHqXk2TH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fzwx8GOZVHqXk2TH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fzwx8GOZVHqXk2TH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fzwx8GOZVHqXk2TH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fzwx8GOZVHqXk2TH .cluster text{fill:#333;}#mermaid-svg-fzwx8GOZVHqXk2TH .cluster span{color:#333;}#mermaid-svg-fzwx8GOZVHqXk2TH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fzwx8GOZVHqXk2TH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fzwx8GOZVHqXk2TH rect.text{fill:none;stroke-width:0;}#mermaid-svg-fzwx8GOZVHqXk2TH .icon-shape,#mermaid-svg-fzwx8GOZVHqXk2TH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fzwx8GOZVHqXk2TH .icon-shape p,#mermaid-svg-fzwx8GOZVHqXk2TH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fzwx8GOZVHqXk2TH .icon-shape .label rect,#mermaid-svg-fzwx8GOZVHqXk2TH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fzwx8GOZVHqXk2TH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fzwx8GOZVHqXk2TH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fzwx8GOZVHqXk2TH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 监控通道
日志通道
服务层
写日志文件
写日志文件
推送
暴露 /metrics
暴露 /metrics
拉取指标
查询与展示
触发告警
go-zero 服务 A
go-zero 服务 B
Filebeat
Elasticsearch
Kibana
Prometheus Exporter
Prometheus Server
Alertmanager
Grafana
日志通过 Filebeat 采集并送入 Elasticsearch,再经由 Kibana 完成可视化;性能指标则由 Prometheus 主动从各服务暴露的 /metrics 端点拉取,最终在 Grafana 中展示,同时可通过 Alertmanager 发送告警。
基于 ELK 的日志收集与 Kibana 可视化
ELK 架构天然擅长日志的集中存储与检索。借助 Filebeat 可以将 go-zero 服务产生的日志近乎实时地推送到 Elasticsearch,再使用 Kibana 创建丰富的可视化仪表盘。
数据准备与索引模式创建
在进行可视化之前,需要确保 Elasticsearch 中已有日志数据。通常先清空旧数据,再重新导入,以保证分析结果的准确性。本文直接沿用已有数据,实际操作流程如下:
- 确认
Filebeat已正确采集各服务的日志文件,并输出到Elasticsearch。 - 进入
Kibana管理界面,点击左侧菜单的 "Stack Management" → "Index Patterns"。 - 点击 "Create index pattern",输入索引名称(例如
filebeat-*),选择@timestamp作为时间字段。 - 创建完成后,即可在 "Discover" 中看到日志明细。
日志数据推送链路
以 go-zero 开发的 api 服务为例,我们将其运行日志输出到文件,再由 Filebeat 进行采集。
yaml
# filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/go-zero/*.log
fields:
service: go-zero-api
output.elasticsearch:
hosts: ["http://elasticsearch:9200"]
go-zero 的日志配置建议使用 zap 日志库并输出到文件,以下为 api 服务的日志初始化示例:
go
package main
import (
"os"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
func initLogger() *zap.Logger {
writer := zapcore.AddSync(&lumberjack.Logger{
Filename: "/var/log/go-zero/api.log",
MaxSize: 100, // MB
MaxBackups: 3,
MaxAge: 7, // days
})
encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
core := zapcore.NewCore(encoder, writer, zapcore.InfoLevel)
return zap.New(core)
}
在实际 go-zero 项目中,可以在 main 函数中替换默认的 logx 实现,确保所有框架日志也写入文件,方便 Filebeat 统一采集。
Kibana 可视化实战
日志数据就绪后,即可在 Kibana 中创建可视化面板。
饼图:分析高频 SQL 语句
假设我们记录了各服务的 SQL 执行日志,现在希望找出执行频率最高的 SQL 模板。
- 进入 "Visualize Library",点击 "Create visualization"。
- 选择 "Pie" 图表。
- 选择索引模式(如
filebeat-*)。 - 在 "Buckets" 中选择 "Split slices",聚合方式为 "Terms",字段选择
sql.keyword(需确保日志中输出规范化的 SQL 语句)。 - 点击 "Update",饼图会展示不同 SQL 的出现次数占比。
通过饼图可以立刻识别出例如 SELECT * FROM users WHERE status = ? 这类高频查询,进而进行针对性的优化。
柱状图:按服务统计错误日志数
- 新建可视化,选择 "Vertical bar"。
- X 轴选择 "Date Histogram",按时间聚合。
- Y 轴选择 "Count"。
- 添加子聚合 "Split series",按
service.keyword字段分桶。 - 添加过滤器,只统计
level: error的日志。
最终得到按时间轴分布的、区分服务的错误日志柱状图,可以直观监控哪些服务在什么时段出现异常。
完成的可视化可以保存为仪表盘,方便随时查看整体日志趋势。
基于 Prometheus 的服务性能监控
Prometheus 作为云原生时代的监控标准,专注于时序指标的采集与告警。与 ELK 相比,两者定位不同:Prometheus 擅长数值型指标(如 CPU、内存、请求延迟),而 ELK 则长于日志文本的全文检索与分析。在微服务项目中,二者通常配合使用。
Prometheus 核心组件与架构
Prometheus 的架构可以概括为以下几个部分:
#mermaid-svg-eeJKHn18Q64pRMVp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eeJKHn18Q64pRMVp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eeJKHn18Q64pRMVp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eeJKHn18Q64pRMVp .error-icon{fill:#552222;}#mermaid-svg-eeJKHn18Q64pRMVp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eeJKHn18Q64pRMVp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eeJKHn18Q64pRMVp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eeJKHn18Q64pRMVp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eeJKHn18Q64pRMVp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eeJKHn18Q64pRMVp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eeJKHn18Q64pRMVp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eeJKHn18Q64pRMVp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eeJKHn18Q64pRMVp .marker.cross{stroke:#333333;}#mermaid-svg-eeJKHn18Q64pRMVp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eeJKHn18Q64pRMVp p{margin:0;}#mermaid-svg-eeJKHn18Q64pRMVp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eeJKHn18Q64pRMVp .cluster-label text{fill:#333;}#mermaid-svg-eeJKHn18Q64pRMVp .cluster-label span{color:#333;}#mermaid-svg-eeJKHn18Q64pRMVp .cluster-label span p{background-color:transparent;}#mermaid-svg-eeJKHn18Q64pRMVp .label text,#mermaid-svg-eeJKHn18Q64pRMVp span{fill:#333;color:#333;}#mermaid-svg-eeJKHn18Q64pRMVp .node rect,#mermaid-svg-eeJKHn18Q64pRMVp .node circle,#mermaid-svg-eeJKHn18Q64pRMVp .node ellipse,#mermaid-svg-eeJKHn18Q64pRMVp .node polygon,#mermaid-svg-eeJKHn18Q64pRMVp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eeJKHn18Q64pRMVp .rough-node .label text,#mermaid-svg-eeJKHn18Q64pRMVp .node .label text,#mermaid-svg-eeJKHn18Q64pRMVp .image-shape .label,#mermaid-svg-eeJKHn18Q64pRMVp .icon-shape .label{text-anchor:middle;}#mermaid-svg-eeJKHn18Q64pRMVp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eeJKHn18Q64pRMVp .rough-node .label,#mermaid-svg-eeJKHn18Q64pRMVp .node .label,#mermaid-svg-eeJKHn18Q64pRMVp .image-shape .label,#mermaid-svg-eeJKHn18Q64pRMVp .icon-shape .label{text-align:center;}#mermaid-svg-eeJKHn18Q64pRMVp .node.clickable{cursor:pointer;}#mermaid-svg-eeJKHn18Q64pRMVp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eeJKHn18Q64pRMVp .arrowheadPath{fill:#333333;}#mermaid-svg-eeJKHn18Q64pRMVp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eeJKHn18Q64pRMVp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eeJKHn18Q64pRMVp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eeJKHn18Q64pRMVp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eeJKHn18Q64pRMVp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eeJKHn18Q64pRMVp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eeJKHn18Q64pRMVp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eeJKHn18Q64pRMVp .cluster text{fill:#333;}#mermaid-svg-eeJKHn18Q64pRMVp .cluster span{color:#333;}#mermaid-svg-eeJKHn18Q64pRMVp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eeJKHn18Q64pRMVp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eeJKHn18Q64pRMVp rect.text{fill:none;stroke-width:0;}#mermaid-svg-eeJKHn18Q64pRMVp .icon-shape,#mermaid-svg-eeJKHn18Q64pRMVp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eeJKHn18Q64pRMVp .icon-shape p,#mermaid-svg-eeJKHn18Q64pRMVp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eeJKHn18Q64pRMVp .icon-shape .label rect,#mermaid-svg-eeJKHn18Q64pRMVp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eeJKHn18Q64pRMVp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eeJKHn18Q64pRMVp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eeJKHn18Q64pRMVp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Targets
拉取指标
拉取指标
拉取指标
存储
查询
告警规则
邮件/钉钉/Webhook
短期任务推送
go-zero 服务 /metrics
MySQL Exporter
Redis Exporter
Prometheus Server
TSDB 时序数据库
Prometheus Web UI
Alertmanager
通知渠道
Pushgateway
- Prometheus Server:核心服务,负责服务发现、指标拉取、存储与查询。
- Exporters :采集器,将第三方系统的指标转换为
Prometheus格式。对于原生支持的应用,可直接暴露/metrics;对于MySQL等传统软件,则使用对应的Exporter。 - Pushgateway :用于处理短暂的批处理任务,由任务主动推送指标,再被
Server拉取。 - Alertmanager:处理告警,支持分组、抑制、静默以及多种通知方式。
- Web UI / Grafana :内置简单查询界面,生产环境建议结合
Grafana。
go-zero 服务集成 Prometheus 指标暴露
go-zero 框架内置了对 Prometheus 的支持,只需简单配置即可将服务的运行指标暴露出来。
以下是一个 go-zero api 服务集成 Prometheus 的完整示例:
go
package main
import (
"flag"
"net/http"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zeromicro/go-zero/core/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var configFile = flag.String("f", "etc/api.yaml", "the config file")
type Config struct {
rest.RestConf
Prometheus prometheus.Config
}
func main() {
flag.Parse()
var c Config
conf.MustLoad(*configFile, &c)
// 初始化 Prometheus 指标
prometheus.StartAgent(c.Prometheus)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
// 注册 /metrics 端点
server.AddRoute(rest.Route{
Method: http.MethodGet,
Path: "/metrics",
Handler: func(w http.ResponseWriter, r *http.Request) {
promhttp.Handler().ServeHTTP(w, r)
},
})
// 业务路由...
logx.Info("Server started")
server.Start()
}
配置文件 api.yaml 示例:
yaml
Name: api-service
Host: 0.0.0.0
Port: 8888
Prometheus:
Host: 0.0.0.0
Port: 9100
Path: /metrics
go-zero 的 prometheus.StartAgent 会在独立端口(例如 9100)上启动一个内置的 HTTP 服务器,暴露各种 go 运行时指标以及框架内部的请求计数、延迟分布等指标。也可以复用主服务端口,直接挂载 /metrics 路由,如上所示。
Alertmanager 告警规则配置示例
在 Prometheus 中定义告警规则,当服务响应延迟超过阈值或错误率上升时,主动通知开发人员。
yaml
# prometheus.yml 中的 rule_files 部分
rule_files:
- "alerts.yml"
alerts.yml 内容:
yaml
groups:
- name: go-zero-alerts
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{job="api-service",code=~"5.."}[5m]) > 0.05
for: 2m
labels:
severity: critical
annotations:
summary: "High error rate on {{ $labels.instance }}"
description: "Error rate is above 5% for 2 minutes."
- alert: SlowRequests
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket{job="api-service"}[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "P99 latency is above 1s"
description: "Instance {{ $labels.instance }} has high latency."
配置完成后,重启 Prometheus,当触发条件满足时,Alertmanager 将通过邮件或 Webhook 发送通知。
总结与最佳实践
将 ELK 与 Prometheus 结合使用,可以覆盖微服务可观测性的两大支柱:日志与指标。go-zero 框架通过简洁的配置即可接入这两个体系,开发者无需关心底层采集细节,只需关注业务逻辑。在实际项目中建议:
- 日志输出结构化为 JSON,方便
Elasticsearch索引与检索。 - 关键业务指标必须暴露给
Prometheus,并配置合理的告警阈值。 - 使用
Grafana统一展示Kibana(日志)与Prometheus(指标),提升排障效率。 - 定期清理索引与调整采集频率,避免存储成本过高。
通过上述实践,我们能够实时掌握 go-zero 微服务的运行状态,从日志可视化到性能监控形成完整闭环。