介绍
要使任何观测(Observation)发生,你需要通过 ObservationRegistry
注册 ObservationHandler
对象。一个 ObservationHandler
只会对支持的 Observation.Context
实现做出反应,并且可以通过对观测的生命周期事件做出反应来创建计时器(timers)、跨度(spans)和日志(logs),这些事件包括:
-
start
- 观察已启动。当调用Observation#start()
方法时发生。 -
stop
- 观察已停止。当调用Observation#stop()
方法时发生。 -
error
- 观察过程中发生错误。当调用Observation#error(exception)
方法时发生。 -
event
- 观察过程中发生事件。当调用Observation#event(event)
方法时发生。 -
scope started
- 观察开启了一个作用域(Scope)。当不再使用时,必须关闭该作用域。处理器可以在启动时创建线程局部变量,这些变量在作用域关闭时被清除。当调用Observation#openScope()
方法时发生。 -
scope stopped
- 观察停止了一个作用域。当调用Observation.Scope#close()
方法时发生。
每当调用这些方法中的任何一个时,都会调用一个 ObservationHandler
方法(例如 onStart(T extends Observation.Context ctx)
、onStop(T extends Observation.Context ctx)
等)。为了在处理器方法之间传递状态,你可以使用 Observation.Context
。
观察状态图如下所示:
Observation Observation
Context Context
Created ----------> Started ----------> Stopped
这是观测范围(Observation Scope)状态图的样子:
Observation
Context
Scope Started ----------> Scope Finished
为了使生产环境中的问题调试成为可能,一个 Observation 需要额外的元数据,例如键值对(也称为标签)。然后,您可以通过这些标签查询您的指标或分布式追踪后端,以找到所需的数据。标签可以是高基数或低基数的。
高基数 意味着一个键值对可能有无限多个可能的值。HTTP URL 就是一个很好的例子(例如 /example/user1234
、/example/user2345
等)。低基数 意味着一个键值对可能有有限数量的可能值。模板化 的 HTTP URL 就是一个很好的例子(例如 /example/{userId}
)。
为了将观测生命周期操作与观测配置(如名称和高低基数标签)分离,您可以使用 ObservationConvention
,它提供了一种简单的方式来覆盖默认的命名约定。
以下示例使用了 Observation API:
static class Example {
private final ObservationRegistry registry;
Example(ObservationRegistry registry) {
this.registry = registry;
}
void run() {
Observation.createNotStarted("foo", registry)
.lowCardinalityKeyValue("lowTag", "lowTagValue")
.highCardinalityKeyValue("highTag", "highTagValue")
.observe(() -> System.out.println("Hello"));
}
}
调用 observe(() → …)
会启动观察(Observation),将其置于作用域中,执行 lambda 表达式,如果发生错误,则将错误记录到观察中,关闭作用域并停止观察。
构建模块
在本节中,我们将对 Micrometer 观测组件进行简要概述。你可以在文档的这一部分中阅读更多相关内容。
术语表
-
ObservationRegistry
- 包含Observation
相关配置的注册表(例如处理程序、谓词、过滤器) -
ObservationHandler
- 处理Observation
的生命周期(例如在观察开始时创建计时器,在观察结束时停止它) -
ObservationFilter
- 在Observation
停止之前修改Context
(例如添加一个高基数的键值对,表示云服务器区域) -
ObservationPredicate
- 禁用创建Observation
的条件(例如不创建具有给定键值对的观察) -
Context
(实际上是Observation.Context
)- 附加到Observation
的可变映射,在处理程序之间传递(这样你可以在不使用任何线程本地变量的情况下传递状态) -
ObservationConvention
- 将Observation
生命周期(开始、停止、打开作用域)与添加元数据(例如观察名称、键值对)分离的方式。这样,观察的命名和元数据处理就变成了一个配置问题(例如,添加键值对不需要更改检测代码,而是更改约定)
使用流程
┌──────────────────┐┌─────────────────┐┌────────────────────┐┌───────┐┌─────────────────────┐
│ObservationHandler││ObservationFilter││ObservationPredicate││Context││ObservationConvention│
└┬─────────────────┘└┬────────────────┘└┬───────────────────┘└┬──────┘└┬────────────────────┘
┌▽───────────────────▽──────────────────▽┐ │ │
│ObservationRegistry │ │ │
└┬───────────────────────────────────────┘ │ │
┌▽────────────────────────────────────────────────────────────▽────────▽┐
│Observation │
└───────────────────────────────────────────────────────────────────────┘
-
ObservationHandler
、ObservationFilter
和ObservationPredicate
会被注册到ObservationRegistry
上ObservationHandler
可以组合在一起(例如通过FirstMatchingCompositeObservationHandler
- 当你将多个相同类型的处理器分组时,这很有用,例如TracingHandler
或MeterHandler
)
-
创建
Observation
时,ObservationRegistry
和Context
是必需的- 你可以通过名称、
ObservationRegistry
和Context
创建Observation
,或者通过ObservationRegistry
和ObservationConvention
创建
- 你可以通过名称、
-
当
Observation
调用其生命周期方法时,所有通过supportsContext
检查的ObservationHandlers
将执行其生命周期方法(例如,如果一个处理器在supportsContext
中有instanceof SenderContext
检查,那么它只会针对这种类型的上下文被调用)