news 2026/8/13 13:12:49

Micrometer 系列【49】统一观测:Micrometer Observation 模块

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Micrometer 系列【49】统一观测:Micrometer Observation 模块

文章目录

  • 前言
  • 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 ObservationMicrometer官方推出的统一观测API,核心设计思想:一次埋点,多端受益

仅编写一次业务埋点代码,通过注册不同处理器,即可同时生成监控指标、分布式追踪链路、业务事件日志,无需重复编写多套埋点逻辑。

Observation作为Micrometer全新顶层抽象,统一所有可观测能力编程模型,底层自动适配、联动生成三类数据:

  • Metrics指标(timer/counter等)
  • Trace链路Span
  • 结构化日志关联(注入traceIdspanId、自定义观测上下文)

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跨度

状态流转:

  1. Observation生命周期:CreatedStartedStopped
  2. Scope上下文生命周期:Scope StartedScope Finished

1.2 标签基数区分

观测支持两种标签,底层指标存储策略完全不同:

  1. 低基数标签:取值范围有限(接口模板/user/{id}、环境、业务类型),可作为指标维度持久化,不会造成指标爆炸。
  2. 高基数标签:取值无限(真实请求URL、用户ID、订单号),禁止作为指标维度,仅用于链路追踪、日志检索。

1.3 解决痛点

Micrometer早期阶段,指标、链路追踪是两套独立体系,日志无统一关联入口。

三套埋点代码割裂、语义不统一、无法一次埋点输出三类可观测数据,代码冗余、维护成本高。

Observation统一观测模型,彻底统一了Micrometer所有可观测能力的顶层抽象,实现一次埋点,同时生成指标、链路、日志三类可观测数据。

1.4 Spring 生态集成现状

官方主推Observation,但并不是「所有埋点全都基于Observation」,存在两套埋点并存。

Spring官方原生组件自动创建Observation

  • Spring WebMVC/WebFlux(服务端请求)
  • RestClientWebClientRestTemplate(客户端HTTP
  • Spring DataJDBC/JPAMongoDB
  • SpringKafkaSpringRabbitMQ
  • @Scheduled定时任务
  • Spring CloudGateway
  • SpringAI
  • Feign(新版适配)

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观测元数据规范,统一指标名、高低基数标签

完整执行流程:

  1. 通过ObservationRegistry创建Observation,绑定可变Context
  2. 执行ObservationPredicate判断是否跳过观测;
  3. 可传入ObservationConvention统一配置观测名称与标签;
  4. 执行start(),触发所有ObservationHandler#onStart
  5. 手动/自动打开Scope,绑定线程上下文;
  6. 业务执行中可抛出异常、自定义事件;
  7. 执行stop()前,先执行所有ObservationFilter修改上下文;
  8. 触发ObservationHandler#onStop,完成指标统计、链路上报。

2.1 ObservationRegistry 观测工程 + 配置中心

ObservationRegistryMicrometer 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接口,自定义标签值的生成逻辑
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/13 13:11:16

技术社区生态变革与开发者内容创作新趋势

1. 技术社区生态变革的底层逻辑 技术社区正在经历一场前所未有的结构性变革。过去十年间&#xff0c;全球开发者数量增长了近300%&#xff0c;而中国技术从业者规模更是呈现指数级扩张。CSDN作为国内头部技术社区&#xff0c;其注册用户突破一亿大关标志着技术内容消费进入新纪…

作者头像 李华
网站建设 2026/8/13 13:07:30

HarmonyOS分布式计算在植树路线规划中的应用实践

1. HarmonyOS应用开发实战&#xff1a;植树问题路线规划方案设计 最近在HarmonyOS应用开发社区看到一个很有意思的题目——"植树问题&#xff1a;路线规划师"。这个题目看似简单&#xff0c;但结合HarmonyOS的分布式能力&#xff0c;可以开发出很有实用价值的应用。作…

作者头像 李华
网站建设 2026/8/13 13:07:27

Wand-Enhancer 实战教程:自建补丁工具,手机远程控场一次上手

Wand-Enhancer 实战教程&#xff1a;自建补丁工具&#xff0c;手机远程控场一次上手 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer 想在本地优化 …

作者头像 李华
网站建设 2026/8/13 13:05:54

从单体脚本到分布式爬虫:MediaCrawler-new架构设计与性能优化实战

1. 项目概述&#xff1a;从单体脚本到分布式爬虫的演进 在数据驱动的时代&#xff0c;获取多平台媒体内容&#xff08;如视频、图文、音频&#xff09;是许多业务场景的刚需。几年前&#xff0c;一个典型的做法是写一个针对单一平台的Python脚本&#xff0c;用 requests 和 …

作者头像 李华
网站建设 2026/8/13 13:04:57

动态规划斜率优化:从暴力O(n²)到O(n)的几何降维打击

1. 从“暴力”到“优雅”&#xff1a;斜率优化的核心动机 如果你刷过一些动态规划的题目&#xff0c;尤其是那些状态转移方程里带着 (i - j) * (i - j) 或者 (a[i] - b[j])^2 这类项&#xff0c;然后需要你求一个序列上的最优分割点 j 的问题&#xff0c;你大概率会写出一…

作者头像 李华