文章目录
- 前言
- 1. 基础概念
- 1.1 生命周期与回调事件
- 1.2 标签基数区分
- 1.3 解决痛点
- 1.4 Spring 生态集成现状
- 2. 核心组件
- 2.1 ObservationRegistry 观测工程 + 配置中心
- 2.2 ObservationConvention 约定规范
- 2.3 Observation.Context 上下文
- 2.4 ObservationHandler 自定义处理器
- 2.5 ObservationPredicate 全局观测开关
- 2.6 ObservationFilter 统一修改上下文
- 3. 注解支持
- 3.1 @Observed
- 3.2 @ObservationKeyValue
前言
Micrometer Observation是Micrometer官方推出的统一观测API,核心设计思想:一次埋点,多端受益。
仅编写一次业务埋点代码,通过注册不同处理器,即可同时生成监控指标、分布式追踪链路、业务事件日志,无需重复编写多套埋点逻辑。
Observation作为Micrometer全新顶层抽象,统一所有可观测能力编程模型,底层自动适配、联动生成三类数据:
Metrics指标(timer/counter等)Trace链路Span- 结构化日志关联(注入
traceId、spanId、自定义观测上下文)
Maven坐标:
<dependency><groupId>io.micrometer</groupId><artifactId>micrometer-observation</artifactId></dependency>1. 基础概念
1.1 生命周期与回调事件
所有观测逻辑依托ObservationRegistry注册ObservationHandler,处理器监听观测完整生命周期事件:
| 生命周期方法 | 触发时机 | 用途 |
|---|---|---|
| start() | 观测开始 | 初始化链路、长耗时监控、记录起始时间 |
| openScope() | 创建线程上下文Scope | 绑定ThreadLocal,传递观测上下文 |
| error(Exception) | 业务抛出异常 | 标记错误、记录异常堆栈 |
| event(Event) | 自定义业务事件 | 记录自定义打点事件(缓存命中、重试等) |
| Scope.close() | 关闭上下文 | 清理ThreadLocal资源 |
| stop() | 观测结束 | 统计耗时、生成指标、结束Trace跨度 |
状态流转:
Observation生命周期:Created→Started→StoppedScope上下文生命周期:Scope Started→Scope Finished
1.2 标签基数区分
观测支持两种标签,底层指标存储策略完全不同:
- 低基数标签:取值范围有限(接口模板
/user/{id}、环境、业务类型),可作为指标维度持久化,不会造成指标爆炸。 - 高基数标签:取值无限(真实请求
URL、用户ID、订单号),禁止作为指标维度,仅用于链路追踪、日志检索。
1.3 解决痛点
在Micrometer早期阶段,指标、链路追踪是两套独立体系,日志无统一关联入口。
三套埋点代码割裂、语义不统一、无法一次埋点输出三类可观测数据,代码冗余、维护成本高。
Observation统一观测模型,彻底统一了Micrometer所有可观测能力的顶层抽象,实现一次埋点,同时生成指标、链路、日志三类可观测数据。
1.4 Spring 生态集成现状
官方主推Observation,但并不是「所有埋点全都基于Observation」,存在两套埋点并存。
Spring官方原生组件自动创建Observation:
Spring WebMVC/WebFlux(服务端请求)RestClient、WebClient、RestTemplate(客户端HTTP)Spring DataJDBC/JPA、MongoDBSpringKafka、SpringRabbitMQ@Scheduled定时任务Spring CloudGatewaySpringAIFeign(新版适配)
JVM、容器、线程池、连接池等基础指标,依旧直接注册Meter,不走Observation。典型:
JVM内存、GC、线程、类加载指标Tomcat/Jetty线程池、连接指标Lettuce连接池原生指标、HikariCP连接池指标- 各类内置
Gauge(内存、队列长度)
2. 核心组件
术语表:
| 组件 | 作用 |
|---|---|
| ObservationRegistry | 观测注册中心,统一管理Handler、过滤器、断言、全局约定 |
| ObservationHandler | 生命周期处理器,监听start/stop/error/event,生成指标、Trace、日志 |
| Observation.Context | 可变上下文容器,Map结构,跨Handler传递业务数据 |
| ObservationFilter | 观测停止前修改上下文,统一追加全局标签(机房、实例ID) |
| ObservationPredicate | 观测开关断言,满足条件则忽略本次观测,生成空操作NoOp |
| ObservationConvention | 观测元数据规范,统一指标名、高低基数标签 |
完整执行流程:
- 通过
ObservationRegistry创建Observation,绑定可变Context; - 执行
ObservationPredicate判断是否跳过观测; - 可传入
ObservationConvention统一配置观测名称与标签; - 执行
start(),触发所有ObservationHandler#onStart; - 手动/自动打开
Scope,绑定线程上下文; - 业务执行中可抛出异常、自定义事件;
- 执行
stop()前,先执行所有ObservationFilter修改上下文; - 触发
ObservationHandler#onStop,完成指标统计、链路上报。
2.1 ObservationRegistry 观测工程 + 配置中心
ObservationRegistry是Micrometer Observation观测体系的全局入口、工厂、配置中心、上下文管理器。整个框架所有观测能力,均由该类统一调度与管控,是Micrometer可观测体系的基石组件。
它的核心定位可总结为双重角色:
观测工厂:根据全局配置,动态创建真实观测实例或空操作(
NOOP)观测,实现监控动态启停。全局配置中心:统一注册、管理所有观测扩展组件,驱动观测全生命周期逻辑。
2.2 ObservationConvention 约定规范
解耦埋点业务代码与观测元数据(名称、标签),统一全局观测命名规则:
- 业务埋点只关注业务逻辑,不硬编码指标名、标签;
- 通过
Convention统一配置名称、高低基数标签,全局统一修改无需改动埋点代码; - 优先级:自定义传入
Convention> 全局GlobalConvention> 默认Convention。
2.3 Observation.Context 上下文
类似透传容器,存储业务数据、异常、自定义标签,所有Handler共享同一份上下文,替代零散ThreadLocal传递数据。
Observation.Contextcontext=newObservation.Context().put(String.class,"自定义业务数据").addLowCardinalityKeyValue("region","shanghai").addHighCardinalityKeyValue("traceId","xxx");2.4 ObservationHandler 自定义处理器
扩展观测能力的核心扩展点,一套埋点可挂载多个Handler(指标、链路、自定义日志)。
自定义打印Handler示例:
staticclassSimpleHandlerimplementsObservationHandler<Observation.Context>{@OverridepublicvoidonStart(Observation.Contextcontext){System.out.println("观测开始:"+context.get(String.class));}@OverridepublicvoidonError(Observation.Contextcontext){System.out.println("观测异常:"+context.getError().getMessage());}@OverridepublicvoidonEvent(Observation.Eventevent,Observation.Contextcontext){System.out.println("自定义事件:"+event.getName());}@OverridepublicvoidonStop(Observation.Contextcontext){System.out.println("观测结束");}// 控制当前Handler是否处理该上下文@OverridepublicbooleansupportsContext(Observation.ContexthandlerContext){returntrue;}}注册到Registry:
ObservationRegistryregistry=ObservationRegistry.create();registry.observationConfig().observationHandler(newSimpleHandler());团队里不同人创建Observation时命名不一致:
// 张三Observation.createNotStarted("order.placeOrder",registry)// 李四Observation.createNotStarted("order-place-order",registry)// 王五Observation.createNotStarted("order/placeOrder",registry)// 混用分隔符ObservationConvention强制统一命名和tag:
publicinterfaceObservationConvention<TextendsObservation.Context>{// ★ 默认 namedefaultStringgetName(){return"";}// ★ 默认 contextualNamedefaultStringgetContextualName(){return"";}// ★ 默认 lowCardinality key-valuedefaultKeyValuesgetLowCardinalityKeyValues(Tcontext){returnKeyValues.empty();}// ★ 默认 highCardinality key-valuedefaultKeyValuesgetHighCardinalityKeyValues(Tcontext){returnKeyValues.empty();}}2.5 ObservationPredicate 全局观测开关
动态过滤不需要采集的观测,返回false生成NoOp空观测,无性能损耗:
registry.observationConfig().observationPredicate((name,ctx)->{// 过滤指定名称观测if("health.check".equals(name))returnfalse;// 过滤指定用户上下文if(ctxinstanceofMyContext&&"test_user".equals(((MyContext)ctx).getUsername())){returnfalse;}returntrue;});2.6 ObservationFilter 统一修改上下文
观测停止前统一追加、删除、修改标签,全局统一元数据:
registry.observationConfig().observationFilter(context->{// 全局追加低基数机房标签context.addLowCardinalityKeyValue("cloud.zone","hz");// 移除高基数大流量标签context.removeHighCardinalityKeyValue("raw_url");returncontext;});3. 注解支持
3.1 @Observed
若项目中已开启面向切面编程(AOP)(例如引入org.aspectj:aspectjweaver依赖),即可通过@Observed注解快速生成观测链路。该注解可直接标注在方法上(仅观测当前方法)或类上(观测类内所有方法)。
以下示例展示了在方法上添加@Observed注解的业务服务类:
staticclassObservedService{@Observed(name="test.call",contextualName="test#call",lowCardinalityKeyValues={"abc","123","test","42"})voidcall(){System.out.println("call");}}3.2 @ObservationKeyValue
除此之外,可通过@ObservationKeyValue注解,基于方法入参动态添加观测键值对标签。
以下示例展示了带方法参数、配置@ObservationKeyValue注解的服务类:
staticclassObservedServiceWithParameter{@Observed(name="test.call")@ObservationKeyValue(key="key4",cardinality=Cardinality.LOW)Stringcall(@ObservationKeyValues({@ObservationKeyValue(key="key0",cardinality=Cardinality.HIGH),@ObservationKeyValue(key="key1"),@ObservationKeyValue(key="key2",expression="'key2: ' + toUpperCase"),@ObservationKeyValue(key="key3",resolver=ValueResolver.class)})Stringparam){returnparam;}}核心注解参数释义
lowCardinalityKeyValues:低基数键值对,适用于取值固定、枚举类、数量有限的业务标签,用于指标聚合、分组统计cardinality = Cardinality.HIGH:高基数键值对,适用于取值不固定、唯一、动态变化的参数(如请求ID、自定义入参),仅用于链路明细排查,不适合聚合expression:支持SpEL表达式,可对入参进行格式化、运算、转换后生成标签值resolver:自定义值解析器,通过实现ValueResolver接口,自定义标签值的生成逻辑