Micrometer 系列【52】统一观测:Observation | 观测载体

文章目录

  • [1. 生命周期](#1. 生命周期)
    • [1.1 Handler 回调](#1.1 Handler 回调)
    • [1.2 生命周期事件](#1.2 生命周期事件)
      • [1.2.1 start 观测启动事件](#1.2.1 start 观测启动事件)
      • [1.2.2 stop 观测终止事件](#1.2.2 stop 观测终止事件)
      • [1.2.3 error 异常事件](#1.2.3 error 异常事件)
      • [1.2.4 event 自定义事件](#1.2.4 event 自定义事件)
      • [1.2.5 scope started 作用域开启事件](#1.2.5 scope started 作用域开启事件)
      • [1.2.6 scope stopped 作用域关闭事件](#1.2.6 scope stopped 作用域关闭事件)
    • [1.3 执行流程](#1.3 执行流程)
  • [2. ObservationView 接口](#2. ObservationView 接口)
    • [2.1 基础定位](#2.1 基础定位)
    • [2.2 三个方法](#2.2 三个方法)
      • [2.2.1 etContextView()](#2.2.1 etContextView())
      • [2.2.2 getObservationRegistry()](#2.2.2 getObservationRegistry())
      • [2.2.3 getEnclosingScope()](#2.2.3 getEnclosingScope())
  • [3. Observation 接口](#3. Observation 接口)
    • [3.1 基础定位](#3.1 基础定位)
    • [3.2 生命周期方法](#3.2 生命周期方法)
      • [3.2.1 start()](#3.2.1 start())
      • [3.2.2 openScope ()](#3.2.2 openScope ())
      • [3.2.3 error (Throwable error)](#3.2.3 error (Throwable error))
      • [3.2.4 event (Event event)](#3.2.4 event (Event event))
      • [3.2.5 stop ()](#3.2.5 stop ())
    • [3.3 链式配置方法](#3.3 链式配置方法)
      • [3.3.1 ntextualName(String contextualName)](#3.3.1 ntextualName(String contextualName))
      • [3.3.2 parentObservation(Observation parentObservation)](#3.3.2 parentObservation(Observation parentObservation))
      • [3.3.3 lowCardinalityKeyValue / lowCardinalityKeyValues](#3.3.3 lowCardinalityKeyValue / lowCardinalityKeyValues)
      • [3.3.4 highCardinalityKeyValue / highCardinalityKeyValues](#3.3.4 highCardinalityKeyValue / highCardinalityKeyValues)
      • [3.3.4 observationConvention(ObservationConvention<?> observationConvention)](#3.3.4 observationConvention(ObservationConvention<?> observationConvention))
      • [3.3.4 getContext()](#3.3.4 getContext())
      • [3.3.4 isNoop()](#3.3.4 isNoop())
    • [3.4 静态工厂方法](#3.4 静态工厂方法)
      • [3.4.1 createNotStarted()](#3.4.1 createNotStarted())
      • [3.4.2 start () 静态重载](#3.4.2 start () 静态重载)
    • [3.5 封装模板工具方法](#3.5 封装模板工具方法)
      • [3.4.1 observe () 系列(Runnable / Supplier / Checked)](#3.4.1 observe () 系列(Runnable / Supplier / Checked))
      • [3.5.2 scoped () 系列](#3.5.2 scoped () 系列)
  • [4. SimpleObservation](#4. SimpleObservation)
    • [4.1 核心成员](#4.1 核心成员)
    • [4.2 构造函数](#4.2 构造函数)
      • [4.2.1 构造函数 1:基于名称构建](#4.2.1 构造函数 1:基于名称构建)
      • [4.2.2 构造函数2:基于 ObservationConvention 构建](#4.2.2 构造函数2:基于 ObservationConvention 构建)
      • [4.2.3 内部辅助静态方法](#4.2.3 内部辅助静态方法)
    • [4.3 生命周期核心方法](#4.3 生命周期核心方法)
      • [4.3.1 start()](#4.3.1 start())
      • [4.3.2 openScope()](#4.3.2 openScope())
      • [4.3.3 event()](#4.3.3 event())
      • [4.3.4 error()](#4.3.4 error())
      • [4.3.5 stop()](#4.3.5 stop())

1. 生命周期

生命周期 描述了 Observationstart() 正式启动观测开始,直到 stop() 结束观测为止的整个运行流程。

只有吃透完整生命周期与事件触发时机,才能写出规范、无泄漏、链路完整的埋点代码,否则极易出现 Span 丢失、标签缺失、上下文错乱、内存泄漏等线上疑难问题。


1.1 Handler 回调

想要执行观测,需要通过 ObservationRegistry 注册若干 ObservationHandler 对象。Handler 的全部能力,依托 Observation 生命周期事件驱动。

ObservationHandler 仅对自身支持的 Observation.Context 实现作出响应,它可以通过响应观测的生命周期事件,创建计时器(Timer)、追踪跨度(Span)以及关联日志。

只需使用 Observation 对代码完成一次埋点,注册多少个 ObservationHandler,就能获得对应数量的可观测能力。


1.2 生命周期事件

重要区分:

  1. start / stop / error / event观测主体生命周期事件 ,管控一次观测(Timer/Span)的起止;
  2. scope started / scope stopped作用域事件,仅控制追踪上下文在线程内是否生效,不影响观测本身是否运行。

观测状态流转图:

text 复制代码
Observation Context
Created(已创建) ----------> Started(已启动) ----------> Stopped(已终止)

观测作用域状态流转图:

javascript 复制代码
Observation Context
Scope Started(作用域开启) ----------> Scope Finished(作用域结束)

1.2.1 start 观测启动事件

触发时机 :调用 Observation.start()

执行逻辑:

  1. 执行 ObservationConvention,填充观测名称、高低基数标签;
  2. 正向遍历所有通过 supportsContext 校验的 ObservationHandler,执行 onStart(Context)

典型 Handler 行为 :

  • 追踪处理器:创建分布式追踪 Span
  • 指标处理器:记录起始时间戳,用于后续耗时计算。

规范约束:所有自定义标签、父观测、观测策略配置,建议在 start() 调用之前完成;观测启动后新增标签,存在无法被处理器捕获的风险。

1.2.2 stop 观测终止事件

触发时机 :调用 Observation.stop()

执行逻辑:

  1. 再次执行 ObservationConvention 补充标签;
  2. 依次执行所有 ObservationFilter,允许运行时修改上下文;
  3. 逆序遍历 匹配的 ObservationHandler,执行 onStop(Context)

典型 Handler 行为 :

  • 追踪处理器:结束 Span,上报追踪数据;
  • 指标处理器:计算执行耗时,生成并上报 Timer 指标。

⚠️ 关键约束:

  • start()stop() 必须成对出现;遗漏 stop() 将造成 Span 永久悬挂、指标缺失、内存泄漏。
  • stop() 不会自动关闭 Scope,必须手动执行 scope.close()

1.2.3 error 异常事件

触发时机 :调用 Observation.error(Throwable)

执行逻辑:

  1. 将异常存入 Context
  2. 正向遍历匹配的 ObservationHandler,执行 onError(Context)

典型 Handler 行为 :

  • 追踪处理器:为 Span 标记异常、设置错误状态码,写入异常堆栈信息。

⚠️ 常见误区:

  • 调用 error() 仅仅标记异常,不会终止观测生命周期 ,依旧必须调用 stop()

1.2.4 event 自定义事件

触发时机 :调用 Observation.event(Event)

执行逻辑:正向遍历匹配的 ObservationHandler,执行 onEvent(Event, Context)

适用场景:在观测运行期间记录业务关键节点,例如「开始调用工具」「收到第三方响应」等,事件会作为日志节点嵌入追踪 Span 中。

约束:仅允许在 start() 之后、stop() 之前调用;观测终止后再添加事件会被处理器忽略。

1.2.5 scope started 作用域开启事件

触发时机 :调用 Observation.openScope()

执行逻辑:创建 Scope 对象,正向遍历处理器执行 onScopeOpened(Context),并将观测上下文绑定到当前线程。

典型 Handler 行为 :

  • traceIdspanIdBaggage 等信息写入线程本地变量(ThreadLocal/MDC),保证同线程下游代码自动继承追踪上下文。

1.2.6 scope stopped 作用域关闭事件

触发时机 :调用 Observation.Scope.close()

执行逻辑:遍历处理器执行 onScopeClosed(Context),清理线程本地追踪上下文,Scope 栈向上回退。

⚠️ 核心边界:

  • close()Scope 实例自身的方法,不属于 Observation 接口,它只负责生产 Scope 对象;
  • scope.close() 只会清理线程上下文,不会停止观测、不会触发 stop 事件。观测依然处于运行状态,计时持续进行;
  • 规范写法优先使用 try-with-resources,保证作用域一定会关闭,避免上下文栈错乱。

1.3 执行流程

复制代码
createNotStarted() // 创建对象,无事件触发【不属于生命周期事件】
    ↓ 预配置标签
start()             // 触发 start 事件
    ↓
openScope()         // 触发 scope started 事件
    ↓
执行业务代码
    ├─ observation.event() // 触发 event 事件
    └─ observation.error() // 触发 error 事件(异常场景)
    ↓
scope.close()       // 触发 scope stopped 事件
    ↓
stop()              // 触发 stop 事件

完整时序图:


2. ObservationView 接口

2.1 基础定位

包路径:io.micrometer.observation.ObservationView

版本:Micrometer 1.10.0 引入

核心作用: 对外暴露只读能力,禁止修改观测生命周期与上下文

ObservationViewObservation只读视图接口 ,实现读写分离、最小权限控制。只提供查询能力,屏蔽所有观测生命周期修改操作,保证分布式追踪、metrics 埋点链路不会被外部代码随意破坏。

java 复制代码
public interface ObservationView {
    default ObservationRegistry getObservationRegistry() {
        return ObservationRegistry.NOOP;
    }
    ContextView getContextView();
    @Nullable
    default Observation.Scope getEnclosingScope() {
        return Observation.Scope.NOOP;
    }
}

很多 ObservationHandler、回调只需要读取上下文,不需要调用 start()/stop()/lowCardinalityKeyValue(),对外只传递 ObservationView,避免外部代码错误篡改观测状态。


2.2 三个方法

2.2.1 etContextView()

获取只读上下文视图

  • 无法调用 addLowCardinalityKeyValueputsetError 等修改方法;
  • 只能读取 nametagserrorparentObservation
  • 底层实现:SimpleObservation 直接返回自身 ContextContext 同时实现 ContextView )。
java 复制代码
    /**
     * Returns the {@link ContextView} attached to this observation.
     * @return corresponding context
     */
    ContextView getContextView();

2.2.2 getObservationRegistry()

默认实现返回 NOOP 注册表;

  • SimpleObservation 重写该方法,返回绑定真实 ObservationRegistry
  • 用途:Handler 在回调内可以拿到当前观测归属的 registry
java 复制代码
    /**
     * Returns the {@link ObservationRegistry} attached to this observation.
     * @return corresponding observation registry
     * @since 1.10.10
     */
    default ObservationRegistry getObservationRegistry() {
        return ObservationRegistry.NOOP;
    }

2.2.3 getEnclosingScope()

获取当前线程上一层嵌套 Scope ,默认返回 NOOPSimpleObservation 重写:从 lastScopeThreadScope map)读取当前线程绑定的外层 Scope

适用场景:

  • 判断当前观测是否被嵌套执行、区分父子 Scope

3. Observation 接口

3.1 基础定位

这是 Micrometer Observation 顶层接口,定义观测生命周期、作用域模型、上下文、异步工具方法SimpleObservation 是唯一标准实现,NOOP 是空实现(关闭观测时生效)。

顶层设计思想:一次埋点,多观测输出 。业务代码只编写一套 Observation 埋点;通过注册不同 ObservationHandler,可以同时产出:MetricsTimer/Counter)、Tracing Span、日志MDC 注入、事件告警等。

内部类型:

内部类型 说明
Context 可变数据容器,ConcurrentHashMap + name + error + parent + KeyValues
ContextView Context 的只读视图接口
Scope AutoCloseable,代表 observation 在线程上的活跃期
Event 任意自定义事件,Event.of(name, contextualName)
CheckedRunnable<E> / CheckedCallable<T,E> / CheckedFunction<T,R,E> 支持受检异常的函数式接口

关键边界总结(可直接放进文档总结)

两套生命周期严格分离

Observation 主体生命周期:start() → stop()

Scope 作用域生命周期:openScope() → Scope.close()

Scope.close() 是 Scope 接口方法,Observation不存在 close () 方法;

配置方法(标签、parent、convention)必须放在 start() 之前;

observe() 全自动生命周期;scoped() 只管理作用域,不启停观测;

error() 仅标记异常,不能替代 stop();

createNotStarted 属于实例创建阶段,不属于观测生命周期。

3.2 生命周期方法

Observation(观测)生命周期:从 start() 正式启动观测开始,直到 stop() 结束观测为止的整个运行时间段。

3.2.1 start()

作用 :正式启动观测;初始化计时、执行 ObservationConvention 填充标签、正向依次触发所有 ObservationHandler#onStart()

配套约束:

  • 必须在 createNotStarted() 之后执行;
  • 禁止多次调用 start ()
  • start() 必须在 stop() 之前调用;
  • NOOP Observation 调用无副作用。
java 复制代码
/**
 * Starts the observation. Remember to call this method, otherwise timing calculations
 * will not take place.
 * @return this
 */
Observation start();

3.2.2 openScope ()

作用 :创建线程作用域 Scope,将当前观测绑定到线程本地ThreadLocal);让 MDCTracing 上下文、Baggage 在当前线程可见。

配套约束:

  • Scope 必须 close (),优先 try-with-resources
  • 不要手动缓存 Scope 在全局变量;Scope 绑定线程
  • 跨线程不能直接传递 Scope 对象,需要使用 scope.makeCurrent ()
  • openScope 可以多次调用(嵌套场景),close 顺序必须逆序
java 复制代码
/**
 * When put in scope, additional operations can take place by the
 * {@link ObservationHandler}s such as putting entries in thread local.
 * @return new scope
 */
Scope openScope();

Scope 内置三个关键方法(属于生命周期配套):

java 复制代码
interface Scope extends AutoCloseable {
    void close();        // 关闭作用域,回退上层scope,触发onScopeClosed
    void reset();        // 清空当前线程所有Observation ThreadLocal,不维护栈
    void makeCurrent();  // 异步恢复上下文专用,重建Scope栈
}

3.2.3 error (Throwable error)

作用 :标记观测发生异常,向所有 Handler 推送异常信息;不会自动停止观测

java 复制代码
/**
 * Signals an error.
 * @param error error
 * @return this
 */
Observation error(Throwable error);

❌ 误区:调用 error () ≠ stop (),依然必须手动 stop ()

3.2.4 event (Event event)

作用 :在观测生命周期内添加自定义事件(Span 内部事件日志),用于记录关键节点。

java 复制代码
/**
 * Signals an arbitrary {@link Event}.
 * @param event event
 * @return this
 */
Observation event(Event event);

区分:event 是自定义节点日志;error 专门用于异常;不要用 event 替代 error

3.2.5 stop ()

作用:观测生命周期终点,代表本次观测正式结束。

java 复制代码
/**
 * Stop the observation. Remember to call this method, otherwise timing calculations
 * won't be finished.
 */
void stop();

3.3 链式配置方法

作用 :预配置观测元数据;启动后再设置,存在标签无法被捕获的风险,必须在 start () 之前调用。

3.3.1 ntextualName(String contextualName)

设置上下文名称(动态 Span 名称)。区别于固定观测名称,可根据运行时信息动态覆盖 Span 展示名称,例如 HTTP 请求路径。

java 复制代码
/**
 * Sets the name that can be defined from the contents of the context. E.g. a span
 * name should not be the default observation name but one coming from an HTTP
 * request.
 * @param contextualName contextual name
 * @return this
 */
Observation contextualName(@Nullable String contextualName);

3.3.2 parentObservation(Observation parentObservation)

手动指定父观测,构建父子追踪链路。异步场景无法依靠 Scope 自动继承父链路时,手动传入父 Observation。正常同步代码打开 Scope 后,框架自动回填父观测,无需手动调用。

java 复制代码
/**
 * If you have access to a previously created {@link Observation} you can manually set
 * the parent {@link Observation} using this method - that way you won't need to open
 * scopes just to create a child observation.
 *
 * If you're using the {@link #openScope()} method then the parent observation will be
 * automatically set, and you don't have to call this method.
 * @param parentObservation parent observation to set
 * @return this
 */
Observation parentObservation(@Nullable Observation parentObservation);

3.3.3 lowCardinalityKeyValue / lowCardinalityKeyValues

添加低基数标签。标签取值范围有限(模板路由、状态码、操作名称);可用于指标聚合分组。

java 复制代码
Observation lowCardinalityKeyValue(KeyValue keyValue);
default Observation lowCardinalityKeyValue(String key, String value);
default Observation lowCardinalityKeyValues(KeyValues keyValues);

3.3.4 highCardinalityKeyValue / highCardinalityKeyValues

添加高基数标签。取值无限(原始 URL、用户 ID、traceId);一般仅用于分布式追踪,禁止大量用于指标分组,防止指标爆炸。

java 复制代码
Observation lowCardinalityKeyValue(KeyValue keyValue);
default Observation lowCardinalityKeyValue(String key, String value);
default Observation lowCardinalityKeyValues(KeyValues keyValues);

3.3.4 observationConvention(ObservationConvention<?> observationConvention)

为当前观测绑定自定义 Convention。必须在 start () 之前设置;Convention 负责统一生成名称、标签,实现埋点代码与元数据解耦。

java 复制代码
/**
 * Sets an observation convention that can be used to attach key values to the
 * observation. Implementations should only set the convention when it supports (see:
 * {@link ObservationConvention#supportsContext(Context)}) the current
 * {@link Context}. WARNING: You must set the ObservationConvention to the Observation
 * before it is started.
 * @param observationConvention observation convention
 * @return this
 */
Observation observationConvention(ObservationConvention<?> observationConvention);

3.3.4 getContext()

定位:获取观测可变上下文 Context。

说明:Context 是 Handler 之间传递自定义数据的容器;getContextView() 返回只读视图。

java 复制代码
/**
 * Returns the context attached to this observation.
 * @return corresponding context
 */

3.3.4 isNoop()

定位:判断当前是否为空操作观测(NOOP)。

场景:当观测被 ObservationPredicate 禁用、registry 为 null 时,返回 NOOP 实例,所有生命周期方法无副作用。

java 复制代码
default boolean isNoop() {
    return this == NOOP;
}

3.4 静态工厂方法

3.4.1 createNotStarted()

作用:创建观测实例,不会启动观测。

创建观测实例,不属于生命周期:

java 复制代码
static Observation createNotStarted(String name, @Nullable ObservationRegistry registry);
static <T extends Context> Observation createNotStarted(String name, Supplier<T> contextSupplier,
        @Nullable ObservationRegistry registry);
static <T extends Context> Observation createNotStarted(@Nullable ObservationConvention<T> customConvention,
        ObservationConvention<T> defaultConvention, Supplier<T> contextSupplier,
        @Nullable ObservationRegistry registry);

3.4.2 start () 静态重载

定位 :内部等价 createNotStarted(...).start();创建并立刻启动观测。观测一旦创建已经启动,无法预先链式配置标签。

java 复制代码
static Observation start(String name, @Nullable ObservationRegistry registry);
static <T extends Context> Observation start(String name, Supplier<T> contextSupplier,
        @Nullable ObservationRegistry registry);

3.5 封装模板工具方法

重点区分 observe () vs scoped ()

observe():自动执行 start() + stop();完整管理观测生命周期;

scoped() 不会调用 start () /stop (),仅管理 Scope;

适用场景:外层已经启动观测,内层代码临时开启线程上下文。

3.4.1 observe () 系列(Runnable / Supplier / Checked)

全自动管理完整生命周期,自动打开、关闭 Scope,自动 start/stop,适合同步简单场景,减少模板代码。

内部标准流程

java 复制代码
start()
try (Scope scope = openScope()) {
    执行业务逻辑
} catch (Throwable error) {
    error(error);
    throw error;
} finally {
    stop();
}
java 复制代码
default void observe(Runnable runnable);
default <T> T observe(Supplier<T> supplier);
default <E extends Throwable> void observeChecked(CheckedRunnable<E> checkedRunnable);
default <T,E extends Throwable> T observeChecked(CheckedCallable<T,E> checkedCallable);

3.5.2 scoped () 系列

java 复制代码
default void scoped(Runnable runnable);
default <T> T scoped(Supplier<T> supplier);

内部流程

go 复制代码
try (Scope scope = openScope()) {
    执行业务
} catch (Exception exception) {
    error(exception);
    throw exception;
}

wrap() / wrapChecked()

把原始函数包装为带观测的函数,用于线程池任务包装。

tryScoped () 静态工具

java

static void tryScoped(@Nullable Observation parent, Runnable action);

定位:父观测可能为 null 的安全封装;如果 parent 不为 null 执行 scoped;否则直接运行业务。常用于异步回调、拦截器通用逻辑。

4. SimpleObservation

这是 Micrometer Observation 默认实现类,承担整个观测生命周期管理、作用域(Scope)栈、Handler 回调调度,也是链路追踪上下文传递最关键的实现。

java 复制代码
class SimpleObservation implements Observation { ... }  // package-private

4.1 核心成员

核心成员变量:

handlers:Deque 双端队列,保存所有通过 supportsContext 匹配成功的处理器

context:观测上下文,存放标签、异常、父观测、自定义数据

convention:观测命名与标签策略

filters:观测结束前执行的过滤器

java 复制代码
final ObservationRegistry registry;
private final Context context; // 观测上下文(tags、error、parentObservation)
private ObservationConvention convention; // 命名、自动填充KV策略
private final Deque<ObservationHandler> handlers; // 生命周期回调处理器(Tracing、Metrics)
private final Collection<ObservationFilter> filters; // stop阶段上下文过滤
final Map<Thread, Scope> lastScope = new ConcurrentHashMap<>(); // 线程 -> 当前Scope

4.2 构造函数

SimpleObservation 提供两组构造方法,对应两种实例创建模式。

构造执行时机:Observation.createNotStarted() 内部,满足启用条件后 new SimpleObservation(...)。

相关说明:

构造阶段属于实例创建阶段(前置阶段,不属于观测生命周期)。

构造阶段不会触发任何 Handler 回调、不会启动观测;调用 start() 才正式进入生命周期。

4.2.1 构造函数 1:基于名称构建

调用 Observation.createNotStarted(name, registry) 最终进入此构造。

执行流程

  1. 赋值 registrycontext;把传入名称设置到 Context;
  2. 遍历注册中心内所有全局 ObservationConvention,找到第一个 supportsContext(context)=true 的策略;
  3. 遍历所有 ObservationHandler,执行 handler.supportsContext(context),筛选有效处理器存入 Deque
  4. 加载全部 ObservationFilter
  5. 此时观测未启动,不会执行 onStart
java 复制代码
SimpleObservation(@Nullable String name, ObservationRegistry registry, Context context) {
    this.registry = registry;
    this.context = context;
    // 将观测名称写入上下文
    this.context.setName(name);
    // 从 Registry 全局配置匹配可用 ObservationConvention
    this.convention = getConventionFromConfig(registry, context);
    // 筛选所有支持当前 Context 的 ObservationHandler,存入队列
    this.handlers = getHandlersFromConfig(registry, context);
    // 加载全局过滤器
    this.filters = registry.observationConfig().getObservationFilters();
}

4.2.2 构造函数2:基于 ObservationConvention 构建

执行流程

  1. 赋值注册中心、上下文;筛选匹配的 Handler、加载 Filter;
  2. 强制校验传入 convention 是否适配当前 Context;不匹配直接抛异常;

和构造1区别:不再从全局配置搜寻 Convention,直接使用传入自定义 Convention。

  1. 观测名称不在构造阶段设置 ,延后在 start() 方法中由 convention.getName() 填充。

调用携带 customConvention 参数的 createNotStarted() 重载方法。

java 复制代码
SimpleObservation(ObservationConvention<? extends Context> convention, ObservationRegistry registry,
        Context context) {
    this.registry = registry;
    this.context = context;
    this.handlers = getHandlersFromConfig(registry, context);
    this.filters = registry.observationConfig().getObservationFilters();
    // 校验 convention 是否支持当前上下文,不支持直接抛出异常
    if (convention.supportsContext(context)) {
        this.convention = convention;
    }
    else {
        throw new IllegalStateException(
                "Convention [" + convention + "] doesn't support context [" + context + "]");
    }
}

4.2.3 内部辅助静态方法

getConventionFromConfig:遍历注册中心全局 Convention,返回第一个匹配上下文的策略;无匹配返回 null

java 复制代码
@Nullable
private static ObservationConvention getConventionFromConfig(ObservationRegistry registry, Context context) {
    for (ObservationConvention<?> convention : registry.observationConfig().getObservationConventions()) {
        if (convention.supportsContext(context)) {
            return convention;
        }
    }
    return null;
}

getHandlersFromConfig遍历所有注册的处理器,通过 supportsContext 过滤,存入双端队列。

队列顺序决定:onStart/onScopeOpened 正序执行;onStop/onScopeClosed 通过 descendingIterator() 逆序执行。

java 复制代码
private static Deque<ObservationHandler> getHandlersFromConfig(ObservationRegistry registry, Context context) {
    Collection<ObservationHandler<?>> handlers = registry.observationConfig().getObservationHandlers();
    Deque<ObservationHandler> deque = new ArrayDeque<>(handlers.size());
    for (ObservationHandler handler : handlers) {
        if (handler.supportsContext(context)) {
            deque.add(handler);
        }
    }
    return deque;
}

4.3 生命周期核心方法

4.3.1 start()

关键结论

  1. Conventionstart() 执行第一次标签填充;
  2. Handler 正序执行 onStart
  3. start() 内部不会创建 ScopeScope 需要主动调用 openScope()
java 复制代码
@Override
public Observation start() {
    if (this.convention != null) {
        // 1. 从 Convention 读取标签写入上下文
        this.context.addLowCardinalityKeyValues(convention.getLowCardinalityKeyValues(context));
        this.context.addHighCardinalityKeyValues(convention.getHighCardinalityKeyValues(context));
        // 2. Convention 可以覆盖观测名称
        String newName = convention.getName();
        if (StringUtils.isNotBlank(newName)) {
            this.context.setName(newName);
        }
    }
    // 3. 正序触发 onStart
    notifyOnObservationStarted();
    return this;
}

void notifyOnObservationStarted() {
    for (ObservationHandler handler : this.handlers) {
        handler.onStart(this.context);
    }
}

4.3.2 openScope()

  1. 创建 SimpleScope 时自动记录上一级 Scope(previousObservationScope),形成链表栈;
  2. 触发 onScopeOpened,写入 ThreadLocal / MDC;
  3. openScope() 仅仅生成 Scope,不会自动 close
java 复制代码
@Override
public Scope openScope() {
    // 创建 SimpleScope
    Scope scope = new SimpleScope(this.registry, this);
    // 正序触发 onScopeOpened
    notifyOnScopeOpened();
    lastScope.put(Thread.currentThread(), scope);
    return scope;
}

void notifyOnScopeOpened() {
    for (ObservationHandler handler : this.handlers) {
        handler.onScopeOpened(this.context);
    }
}

4.3.3 event()

正序执行 onEvent,仅能在 start() 之后、stop() 之前有效。

java 复制代码
@Override
public Observation event(Event event) {
    notifyOnEvent(event);
    return this;
}

void notifyOnEvent(Event event) {
    for (ObservationHandler handler : this.handlers) {
        handler.onEvent(event, this.context);
    }
}

4.3.4 error()

  1. 将异常存入 Context
  2. 触发 onError
  3. ⚠️ 不会停止观测,不会调用 stop()
java 复制代码
@Override
public Observation error(Throwable error) {
    this.context.setError(error);
    notifyOnError();
    return this;
}

void notifyOnError() {
    for (ObservationHandler handler : this.handlers) {
        handler.onError(this.context);
    }
}

4.3.5 stop()

java 复制代码
@Override
public void stop() {
    if (this.convention != null) {
        // 1. stop 阶段再次执行 Convention(可以填充运行时数据)
        this.context.addLowCardinalityKeyValues(convention.getLowCardinalityKeyValues(context));
        this.context.addHighCardinalityKeyValues(convention.getHighCardinalityKeyValues(context));
        String newContextualName = convention.getContextualName(context);
        if (StringUtils.isNotBlank(newContextualName)) {
            this.context.setContextualName(newContextualName);
        }
    }
    // 2. 依次执行 ObservationFilter,可以修改上下文
    Context modifiedContext = this.context;
    for (ObservationFilter filter : this.filters) {
        modifiedContext = filter.map(modifiedContext);
    }
    // 3. 逆序执行 onStop
    notifyOnObservationStopped(modifiedContext);
}

void notifyOnObservationStopped(Context context) {
    this.handlers.descendingIterator().forEachRemaining(handler -> handler.onStop(context));
}
相关推荐
Henry-SAP3 小时前
SAP MRP类型如何影响计划订单生成
人工智能·云原生·sap·erp
wsad05326 小时前
CentOS Stream 10 Docker 容器无法访问外网:xt_addrtype 内核模块缺失解决方法
linux·网络·docker·云原生·eureka·centos
行业研究员6 小时前
TDSQL-C:云原生架构与AI能力解析
人工智能·云原生·架构·云原生数据库·ai能力解析
人间凡尔赛7 小时前
当 AI Agent 攻陷网关:Istio agentgateway 与 Gateway API Inference Extension 实战解析
后端·云原生·架构
运维老郭8 小时前
【K8s Pod生命周期】CrashLoopBackOff 避坑指南:7 条命令定位 Pod 反复重启根因
云原生
@insist12318 小时前
系统集成项目管理工程师-安全架构与云原生架构
云原生·架构·软考·安全架构·系统集成项目管理工程师·软考中项·软件水平考试
阿里云云原生21 小时前
一张告警卡片到一键 RCA:塔斯汀万店连锁的智能运维闭环实践
云原生
Kismet_nvi1 天前
《Kubernetes Service 进阶、kube-proxy 与 Ingress 实战精要》
云原生·容器·kubernetes
云烟成雨TD1 天前
Micrometer 系列【42】链路追踪:Span 体系 | 核心 API
java·链路追踪·micrometer