可观测性与监控实践:从Spring Boot到Prometheus+Grafana链路搭建
2026/8/31 3:10:51 网站建设 项目流程

可观测性这个词最近几年在运维和研发领域出现得越来越频繁,但它在工程落地时并不是一个一次性安装完的“产品”,而是一套需要持续建设的数据体系。可观测性强调的是,通过系统对外暴露的指标、日志和链路数据,推断出系统内部正在发生什么。而 Monitor 通常负责其中一大部分工作:指标采集、状态检查、告警触发、可视化和故障跟踪。很多人把可观测性等同于完善了告警,或者等同于搭好了 Grafana 大屏,结果系统真正出问题时依然要靠一个个日志文件去翻,这种状态离可观测性还差得很远。

这篇文章从可观测性与 Monitor 的关系开始,以一个 Spring Boot 应用作为最小示例,走通指标暴露、Prometheus 采集、Grafana 展示的监控链路,再接入日志链路追踪,最后整理生产环境落地时的高频问题与检查清单。整个流程不需要大型微服务架构,也不依赖多台服务器,在一台开发机上就能完成。

接下来,先理清概念。否则后面每一步都会容易混淆。

1. 可观测性与 Monitor:先理解概念,再谈工具选型

在搭建监控体系之前,如果对可观测性和 Monitor 的边界不清楚,很容易出现“工具装了一大堆,故障还是没少查”的结果。这里先区分几个核心概念。

1.1 监控和可观测性不是同一个层次的概念

监控(Monitoring)通常解决“已知问题”的发现。比如 CPU 使用率超过 85%,接口失败率超过 5%,磁盘空间低于 10%,这些指标和阈值都来自过往经验。运维团队知道什么情况代表异常,于是通过固定规则做采集和告警。监控的前提是“我们知道需要看什么”。

可观测性(Observability)解决的是“未知问题”的判断。服务能启动、接口也能返回 200,但某个用户反馈订单状态不对,或者流量高峰时响应时间慢慢变长,这些问题往往没有一个现成指标能直接给出答案。可观测性要求系统把内部运行状态尽量通过结构化数据暴露出来,让开发者在异常发生时,能通过指标、日志和链路组合还原现场。

这个区别直接决定了建设顺序:

  • 监控体系适合从运维视角出发,先把已知指标采集完整,再设置告警。
  • 可观测性体系需要开发、测试、运维共同参与,先定义数据模型和传递链路,再考虑展示和告警。

换句话说,Monitor 是手段,可观测性是目标。把两者画等号,会让团队只关注“有没有告警”,而不关注“数据能不能回答未知问题”。

1.2 Monitor 在可观测性体系中的具体角色

Monitor 在可观测性体系里不是一个单点工具,而是一条数据链路。按职责可以拆成四个角色:

角色职责常见组件
采集器从应用、中间件、基础设施或硬件设备中读取状态数据Prometheus Exporter、OpenTelemetry Collector、Telegraf
存储对时序数据、日志、链路进行持久化,支持查询Prometheus、VictoriaMetrics、Elasticsearch、ClickHouse
告警根据规则判断异常并通知相关人员Alertmanager、Grafana Alerting
展示将数据和追踪结果以图表、拓扑、链路视图呈现Grafana、Jaeger UI、SkyWalking UI

“Monitor”这个词如果出现在具体产品名里,可能指硬件监控、显示器端设备或系统监控服务;在可观测性语境下,它泛指上面这条链路中的采集、告警和展示能力。理解这一点后,选型就不会只盯着某一个面板好不好看,而是先看数据能不能完整流通。

比如,一个服务如果没有暴露指标接口,Grafana 再漂亮也没有数据来源;如果日志没有结构化,只靠人工 grep,traceId 再多也串不起调用关系。可观测性建设的核心,是确保这条 Monitor 链路每一段都打通。

1.3 指标、日志、追踪三个支柱为什么缺一不可

可观测性领域常提到三支柱:指标(Metrics)、日志(Logs)、追踪(Traces)。三者的数据形态和回答的问题不一样。

数据类型回答的问题数据特点典型场景
指标系统是否异常数值型、聚合型、占用空间小判断 CPU、请求量、错误率是否超过阈值
日志系统为什么异常文本型、事件型、信息量大定位具体报错、参数输入、程序执行到哪一步
追踪请求经过了哪些服务链路型、包含 span、时间、依赖关系定位慢请求的瓶颈在哪个服务、哪个方法

只采集指标,能第一时间发现问题,但很难解释根因;只保存日志,定位时很直观,但无法快速判断整体状态;只做追踪,能看清一条请求的路由,却不知道全局流量变化。因此生产环境的 Monitor 体系,最终都是把三类数据放在同一份时间上下文中,靠 traceId 或 requestId 把请求串起来。

有了这个基础,下面从最小案例开始搭建。

2. 搭建 Monitor 最小体系:从指标暴露到趋势展示

这里不会直接引入微服务和容器编排,而是用一个 Spring Boot 应用跑通“业务指标 -> Prometheus -> Grafana”的最小闭环。跑通之后,再往日志和链路方向扩展。

2.1 环境准备与依赖选择

先确认本机环境。示例以 Spring Boot 3 为例,实际项目请以当前稳定版本为准。

依赖版本建议用途
JDK17 或更高Spring Boot 3 需要 Java 17
Maven3.6 以上管理依赖和打包
Spring Boot3.xWeb 应用和 Actuator
Prometheus2.x抓取和存储指标
Grafana10.x可视化展示

MacOS 和 Linux 环境下,可以直接用 Docker 启动 Prometheus 和 Grafana;Windows 环境建议优先考虑 WSL2,避免容器网络和本机端口映射出现额外问题。学习环境可以不需要 Kubernetes,也不需要安装完整监控全家桶,先把本地链路跑通,理解每一段配置的含义,再迁移到生产。

如果是在团队内部落地,建议提前确认组件版本。Spring Boot 3 对 Actuator、Micrometer 的版本有隐含依赖,直接引入二进制 JAR 容易踩版本不一致问题,使用 Maven 的 starter 和管理依赖即可。

2.2 一个带业务指标的最小 Spring Boot 项目

先创建一个 Maven 项目,项目名可以叫demo-observability。整体目录结构如下:

demo-observability ├── pom.xml └── src/main ├── java/com/example/demo │ ├── DemoApplication.java │ ├── OrderController.java │ └── OrderMetrics.java └── resources └── application.yml

pom.xml中至少需要 Web、Actuator 和 Prometheus 注册器这三个依赖:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.5</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency> </dependencies>

说明一下为什么需要这三个依赖:Web 负责提供 HTTP 接口;Actuator 暴露应用运行状态,并把 Micrometer 的指标端点注册到/actuator下;micrometer-registry-prometheus则负责把指标转换成 Prometheus 能够抓取的文本格式。如果少了第三个依赖,访问/actuator/prometheus会返回 404。

application.yml中要显式暴露prometheus端点。Spring Boot 3 默认只暴露health,其他端点即使存在也不会出现在 HTTP 接口上:

server: port: 8080 spring: application: name: demo-observability management: endpoints: web: exposure: include: health,info,prometheus

这里比较容易被忽略的是management.endpoints.web.exposure.include。它决定了哪些端点能通过 HTTP 访问。如果只加依赖不修改 expose,/actuator/prometheus还是会 404。

接下来定义业务指标。下面用一个订单创建计数器作为示例,说明如何在业务代码里埋点:

package com.example.demo; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Service; @Service public class OrderMetrics { private final Counter orderCreateCounter; public OrderMetrics(MeterRegistry registry) { this.orderCreateCounter = Counter.builder("demo_order_create_total") .description("Total number of orders created") .tag("module", "order") .register(registry); } public void recordCreate() { orderCreateCounter.increment(); } }

这里有一个常见误区:OrderMetrics本身不存储一份独立数据,它只是把计数器的注册信息交给MeterRegistryMeterRegistry是 Micrometer 的核心对象,最终会把指标输出到 Prometheus。tag("module", "order")的目的是给指标增加一个维度,这样后续可以按模块过滤。

再写一个接口触发计数:

package com.example.demo; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class OrderController { private final OrderMetrics orderMetrics; public OrderController(OrderMetrics orderMetrics) { this.orderMetrics = orderMetrics; } @PostMapping("/orders") public String createOrder() { orderMetrics.recordCreate(); return "created"; } }

启动应用后,先验证自定义指标是否出现在 Actuator 端点中。执行下面的命令,返回体中应该能找到demo_order_create_total

mvn spring-boot:run curl http://localhost:8080/actuator/prometheus

正常输出类似:

# HELP demo_order_create_total Total number of orders created # TYPE demo_order_create_total counter demo_order_create_total{module="order",application="demo-observability"} 0.0

调用一次订单接口后再查:

curl -X POST http://localhost:8080/orders curl http://localhost:8080/actuator/prometheus

此时计数应该变为 1.0。这一步说明两件事:业务代码已经生效,指标端点可以访问。如果这一步没有通过,后面的 Prometheus 抓取一定不会成功。

2.3 用 Prometheus 抓取指标

Prometheus 的作用是按照固定间隔去拉取目标暴露的指标。它通过配置文件里的scrape_configs知道自己要去哪台机器的哪个端口拉数据。

创建一个prometheus.yml

global: scrape_interval: 15s scrape_configs: - job_name: demo-observability metrics_path: /actuator/prometheus static_configs: - targets: ['localhost:8080']

scrape_interval表示采集频率,生产环境常用的范围是 10 到 30 秒。设置越小越容易发现瞬时抖动,但会带来更多存储和网络开销。

使用 Docker 启动 Prometheus:

docker run -d --name prometheus \ -p 9090:9090 \ -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \ prom/prometheus

如果应用在宿主机上运行、Prometheus 在容器里运行,targets中不能写localhost,因为容器内的localhost指向容器自身。需要改成宿主机可访问的地址,比如host.docker.internal

static_configs: - targets: ['host.docker.internal:8080']

这是一个非常典型的本地环境坑。确认抓取是否成功,可以打开http://localhost:9090/targets,看到demo-observability这一行的StateUP

2.4 用 Grafana 展示趋势

指标只有被可视化后,监控价值才会体现。启动 Grafana:

docker run -d --name grafana \ -p 3000:3000 \ grafana/grafana

访问http://localhost:3000,默认账号密码都是admin,首次登录会要求修改密码。

在 Grafana 中添加 Prometheus 数据源:

配置项
NamePrometheus
URLhttp://host.docker.internal:9090 或 http://localhost:9090
AccessServer

如果 Grafana 也在 Docker 中,URL 不能直接写localhost:9090,要写宿主机地址,或者把 Prometheus 和 Grafana 放到同一个 Docker 网络中。否则数据源测试会失败。

数据源配置好后,创建一个 Dashboard,新增 Panel,查询表达式可以这样写:

rate(demo_order_create_total[5m])

rate计算指标在 5 分钟内的每秒平均增长率。如果看到从 0 到 0 的平线,先回到命令行动几次curl -X POST http://localhost:8080/orders,再刷新图表。这个最小闭环跑通后,Monitor 体系的第一段链路就建立了。

3. 从指标走向关联:接入日志与分布式追踪

指标能发现问题,但不足以定位根因。例如demo_order_create_total突然下降,可能是因为数据库连接失败,也可能是因为消息队列积压,还可能是因为订单接口入参发生变更。需要把日志和追踪接进来,才能把“现象”和“原因”对应起来。

3.1 为什么指标告警不能替代日志排查

一个典型的监控告警链路是这样的:

  1. Prometheus 发现请求错误率升高;
  2. Alertmanager 发出告警通知;
  3. 值班人员打开 Grafana,看到错误集中在某个服务;
  4. 进入日志系统,按时间范围搜索该服务的错误日志;
  5. 从日志里找到具体异常堆栈或报错请求参数;
  6. 如果需要排查跨服务调用,再从日志中取出 traceId,查询整条调用链。

如果没有日志,第 4 步就无法进行。如果没有追踪,即使知道服务报错,也无法确定是自身问题还是下游拖慢。因此,指标负责“报警”,日志和追踪负责“还原现场”。

3.2 用 Micrometer Tracing 接入链路数据

Spring Boot 3 之后,官方将链路追踪能力整合到 Micrometer Tracing 中,旧版 Spring Cloud Sleuth 逐渐退出。接入时,先增加依赖:

<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-tracing-bridge-otel</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> </dependency>

前者提供追踪抽象和 OpenTelemetry 桥接,后者负责把追踪数据通过 OTLP 协议导出到采集端。示例使用 OTLP HTTP 导出,地址需要根据实际环境调整:

management: tracing: sampling: probability: 1.0 otlp: tracing: endpoint: http://localhost:4318/v1/traces

sampling.probability表示采样率。学习环境可以设置成 1.0,表示全量采样;生产环境要结合流量评估,通常采用比例采样或根据接口重要性定制采样策略,避免存储压力过大。

接入后,需要在日志输出中把 traceId 和 spanId 打印出来。Logback 的 pattern 可以这样配置:

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [%X{traceId:-

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询