Micrometer 定义了一个 Observation 概念,该概念在应用程序中同时启用 Metrics 和 Traces。 Metrics Support 提供了一种创建计时器、仪表或计数器的方法,用于收集有关应用程序运行时行为的统计信息。 指标可以帮助您跟踪错误率、使用模式、性能等。 跟踪提供了整个系统的整体视图,跨越了应用程序边界;您可以放大特定用户请求,并跟踪它们在应用程序中的整个完成情况。
Spring 框架检测其自己的代码库的各个部分以发布观察结果(如果配置了)。
您可以了解有关在 Spring Boot 中配置可观测性基础设施的更多信息。ObservationRegistry
生成的 Observation 列表
Spring Framework 检测各种功能以实现可观察性。 如本节开头所述,观察可以生成计时器指标和/或跟踪,具体取决于配置。
观察项名称 | 描述 |
---|---|
HTTP 客户端交换所花费的时间 |
|
框架级别的 HTTP 服务器交换的处理时间 |
|
消息生成者将 JMS 消息发送到目标所花费的时间。 |
|
消息使用者以前收到的 JMS 消息的处理时间。 |
|
任务执行的处理时间 |
观测使用 Micrometer 的官方命名约定,但 Metrics 名称将自动转换为监控系统后端首选的格式(Prometheus、Atlas、Graphite、InfluxDB 等)。 |
观察项名称 | 描述 |
---|---|
HTTP 客户端交换所花费的时间 |
|
框架级别的 HTTP 服务器交换的处理时间 |
|
消息生成者将 JMS 消息发送到目标所花费的时间。 |
|
消息使用者以前收到的 JMS 消息的处理时间。 |
|
任务执行的处理时间 |
观测使用 Micrometer 的官方命名约定,但 Metrics 名称将自动转换为监控系统后端首选的格式(Prometheus、Atlas、Graphite、InfluxDB 等)。 |
千分尺观察概念
如果您不熟悉千分尺观察,以下是您应该了解的概念的快速总结。
-
Observation
是应用程序中发生的事情的实际记录。这由实施处理以生成指标或跟踪。ObservationHandler
-
每个观察都有相应的实现;此类型包含用于提取其元数据的所有相关信息。 对于 HTTP 服务器观察,上下文实现可以保存 HTTP 请求、HTTP 响应、处理过程中引发的任何异常等。
ObservationContext
-
每个都包含元数据。对于 HTTP 服务器观察,这可能是 HTTP 请求方法、HTTP 响应状态等。 此元数据由应声明其支持的类型的实现提供。
Observation
KeyValues
ObservationConvention
ObservationContext
-
KeyValues
如果 Tuples 的可能值数量较少,则称为“低基数”(HTTP 方法就是一个很好的例子)。 低基数值仅贡献给量度。 相反,“高基数”值是无限的(例如,HTTP 请求 URI),并且仅参与跟踪。KeyValue
-
A 记录了特定域中的所有观察结果,列出了预期的键名称及其含义。
ObservationDocumentation
配置观测
全局配置选项在 级别 中可用。
每个 instrumented 组件将提供两个扩展点:ObservationRegistry#observationConfig()
-
设置 ;如果未设置,则不会记录观测值,并且将为 NO-OPS
ObservationRegistry
-
提供自定义以更改默认观察项名称和提取
ObservationConvention
KeyValues
使用自定义 Observation 约定
让我们以 Spring MVC“http.server.requests”指标插桩为例。
此观察使用 a 和 ;可以在 Servlet 过滤器上配置自定义约定。
如果要自定义使用观察生成的元数据,可以根据您的要求扩展 :ServerHttpObservationFilter
ServerRequestObservationConvention
ServerRequestObservationContext
DefaultServerRequestObservationConvention
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext;
public class ExtendedServerRequestObservationConvention extends DefaultServerRequestObservationConvention {
@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
// here, we just want to have an additional KeyValue to the observation, keeping the default values
return super.getLowCardinalityKeyValues(context).and(custom(context));
}
private KeyValue custom(ServerRequestObservationContext context) {
return KeyValue.of("custom.method", context.getCarrier().getMethod());
}
}
如果您想要完全控制,您可以为您感兴趣的观察实现整个约定协定:
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import org.springframework.http.server.observation.ServerHttpObservationDocumentation;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.http.server.observation.ServerRequestObservationConvention;
public class CustomServerRequestObservationConvention implements ServerRequestObservationConvention {
@Override
public String getName() {
// will be used as the metric name
return "http.server.requests";
}
@Override
public String getContextualName(ServerRequestObservationContext context) {
// will be used for the trace name
return "http " + context.getCarrier().getMethod().toLowerCase();
}
@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
return KeyValues.of(method(context), status(context), exception(context));
}
@Override
public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) {
return KeyValues.of(httpUrl(context));
}
private KeyValue method(ServerRequestObservationContext context) {
// You should reuse as much as possible the corresponding ObservationDocumentation for key names
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.METHOD, context.getCarrier().getMethod());
}
// status(), exception(), httpUrl()...
private KeyValue status(ServerRequestObservationContext context) {
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.STATUS, String.valueOf(context.getResponse().getStatus()));
}
private KeyValue exception(ServerRequestObservationContext context) {
String exception = (context.getError() != null ? context.getError().getClass().getSimpleName() : KeyValue.NONE_VALUE);
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.EXCEPTION, exception);
}
private KeyValue httpUrl(ServerRequestObservationContext context) {
return KeyValue.of(ServerHttpObservationDocumentation.HighCardinalityKeyNames.HTTP_URL, context.getCarrier().getRequestURI());
}
}
您还可以使用自定义 – 添加或删除观察的关键值来实现类似的目标。
过滤器不会替换默认约定,而是用作后处理组件。ObservationFilter
import io.micrometer.common.KeyValue;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationFilter;
import org.springframework.http.server.observation.ServerRequestObservationContext;
public class ServerRequestObservationFilter implements ObservationFilter {
@Override
public Observation.Context map(Observation.Context context) {
if (context instanceof ServerRequestObservationContext serverContext) {
context.setName("custom.observation.name");
context.addLowCardinalityKeyValue(KeyValue.of("project", "spring"));
String customAttribute = (String) serverContext.getCarrier().getAttribute("customAttribute");
context.addLowCardinalityKeyValue(KeyValue.of("custom.attribute", customAttribute));
}
return context;
}
}
您可以在 .ObservationFilter
ObservationRegistry
@Scheduled 任务检测
每次执行 @Scheduled
任务时,都会创建一个 Observation。
应用程序需要配置 才能启用观测记录。
这可以通过声明一个设置 observation 注册表的 bean 来完成:ObservationRegistry
ScheduledTaskRegistrar
SchedulingConfigurer
import io.micrometer.observation.ObservationRegistry;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
public class ObservationSchedulingConfigurer implements SchedulingConfigurer {
private final ObservationRegistry observationRegistry;
public ObservationSchedulingConfigurer(ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setObservationRegistry(this.observationRegistry);
}
}
默认情况下,它使用 ,由 .
您可以直接在 上配置自定义实现。
在执行 scheduled 方法期间,当前观察将在 context 或 Reactor 上下文中恢复(如果 scheduled 方法返回 or 类型)。org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention
ScheduledTaskObservationContext
ObservationRegistry
ThreadLocal
Mono
Flux
默认情况下,将创建以下内容:KeyValues
名字 |
描述 |
|
计划执行的 Java 的名称。 |
|
保存 scheduled 方法的 bean 实例的类的规范名称,或用于匿名类的规范名称。 |
|
执行期间引发的异常的类名,或者如果未发生异常。 |
|
复制密钥,将来可能会将其删除。 |
|
方法执行的结果。可以是 、 或 (例如,如果在执行期间取消了操作)。 |
名字 |
描述 |
|
计划执行的 Java 的名称。 |
|
保存 scheduled 方法的 bean 实例的类的规范名称,或用于匿名类的规范名称。 |
|
执行期间引发的异常的类名,或者如果未发生异常。 |
|
复制密钥,将来可能会将其删除。 |
|
方法执行的结果。可以是 、 或 (例如,如果在执行期间取消了操作)。 |
JMS 消息传递检测
如果依赖项位于 Classpath 上,则 Spring Framework 使用 Micrometer 提供的 Jakarta JMS 插桩。
仪器并记录相关观察结果。io.micrometer:micrometer-jakarta9
io.micrometer.jakarta9.instrument.jms.JmsInstrumentation
jakarta.jms.Session
此检测将创建 2 种类型的观察值:
-
"jms.message.publish"
将 JMS 消息发送到代理时,通常带有 .JmsTemplate
-
"jms.message.process"
当应用程序处理 JMS 消息时,通常使用 A 或 A 带注释的方法。MessageListener
@JmsListener
目前没有用于观察的仪器,因为测量等待接收消息所花费的时间几乎没有价值。
此类集成通常会检测方法调用。但是,一旦这些返回,则不会测量处理时间,并且跟踪范围无法传播到应用程序。"jms.message.receive" MessageConsumer#receive |
默认情况下,两个观测值共享同一组 possible :KeyValues
名字 |
描述 |
|
消息传送操作期间引发的异常的类名(或“none”)。 |
|
复制密钥,将来可能会将其删除。 |
|
目标是 a 还是 (values: or )。 |
|
正在执行的 JMS 操作的名称(值:或 )。 |
名字 |
描述 |
|
JMS 消息的相关 ID。 |
|
当前邮件发送到的目标的名称。 |
|
消息系统用作消息标识符的值。 |
JMS 消息发布检测
"jms.message.publish"
将 JMS 消息发送到 Broker 时,将记录观察结果。
它们测量发送消息所花费的时间,并使用传出的 JMS 消息标头传播跟踪信息。
您需要在 上配置 以启用观察:ObservationRegistry
JmsTemplate
import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.jms.core.JmsTemplate;
public class JmsTemplatePublish {
private final JmsTemplate jmsTemplate;
private final JmsMessagingTemplate jmsMessagingTemplate;
public JmsTemplatePublish(ObservationRegistry observationRegistry, ConnectionFactory connectionFactory) {
this.jmsTemplate = new JmsTemplate(connectionFactory);
// configure the observation registry
this.jmsTemplate.setObservationRegistry(observationRegistry);
// For JmsMessagingTemplate, instantiate it with a JMS template that has a configured registry
this.jmsMessagingTemplate = new JmsMessagingTemplate(this.jmsTemplate);
}
public void sendMessages() {
this.jmsTemplate.convertAndSend("spring.observation.test", "test message");
}
}
默认情况下,它使用 ,由 .io.micrometer.jakarta9.instrument.jms.DefaultJmsPublishObservationConvention
io.micrometer.jakarta9.instrument.jms.JmsPublishObservationContext
当从 listener 方法返回响应消息时,使用带注释的方法记录类似的观察结果。@JmsListener
JMS 消息处理检测
"jms.message.process"
当应用程序处理 JMS 消息时,将记录观察结果。
它们测量处理消息所花费的时间,并使用传入的 JMS 消息标头传播跟踪上下文。
大多数应用程序将使用 @JmsListener
Annotated methods 机制来处理传入的消息。
您需要确保在专用 上配置 :ObservationRegistry
JmsListenerContainerFactory
import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
@Configuration
@EnableJms
public class JmsConfiguration {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory, ObservationRegistry observationRegistry) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setObservationRegistry(observationRegistry);
return factory;
}
}
需要默认的容器工厂才能启用注解支持,
但请注意,注解可以出于特定目的引用特定的容器工厂 bean。
在所有情况下,只有在容器工厂上配置了 observation 注册表时,才会记录 Observations。@JmsListener
当消息由 处理时,也会记录类似的观察结果。
此类侦听器是在会话回调中设置的(请参阅 )。JmsTemplate
MessageListener
MessageConsumer
JmsTemplate.execute(SessionCallback<T>)
此观察默认使用 ,由 .io.micrometer.jakarta9.instrument.jms.DefaultJmsProcessObservationConvention
io.micrometer.jakarta9.instrument.jms.JmsProcessObservationContext
目前没有用于观察的仪器,因为测量等待接收消息所花费的时间几乎没有价值。
此类集成通常会检测方法调用。但是,一旦这些返回,则不会测量处理时间,并且跟踪范围无法传播到应用程序。"jms.message.receive" MessageConsumer#receive |
名字 |
描述 |
|
消息传送操作期间引发的异常的类名(或“none”)。 |
|
复制密钥,将来可能会将其删除。 |
|
目标是 a 还是 (values: or )。 |
|
正在执行的 JMS 操作的名称(值:或 )。 |
名字 |
描述 |
|
JMS 消息的相关 ID。 |
|
当前邮件发送到的目标的名称。 |
|
消息系统用作消息标识符的值。 |
HTTP 服务器检测
HTTP 服务器交换观察是使用 Servlet 和 Reactive 应用程序的名称创建的。"http.server.requests"
Servlet 应用程序
应用程序需要在其应用程序中配置 Servlet 过滤器。
默认情况下,它使用 ,由 .org.springframework.web.filter.ServerHttpObservationFilter
org.springframework.http.server.observation.DefaultServerRequestObservationConvention
ServerRequestObservationContext
如果 Web 框架尚未处理观察结果,并且已冒泡到 Servlet 过滤器,则只会将观察结果记录为错误。
通常,由 Spring MVC 和 ProblemDetail
支持处理的所有异常都不会与观察一起记录。
在请求处理过程中,您可以随时在 self 上设置 error 字段:Exception
@ExceptionHandler
ObservationContext
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.ServerHttpObservationFilter;
@Controller
public class UserController {
@ExceptionHandler(MissingUserException.class)
ResponseEntity<Void> handleMissingUser(HttpServletRequest request, MissingUserException exception) {
// We want to record this exception with the observation
ServerHttpObservationFilter.findObservationContext(request)
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}
static class MissingUserException extends RuntimeException {
}
}
由于插桩是在 Servlet Filter 级别完成的,因此观察范围仅涵盖在此过滤器之后排序的过滤器以及请求的处理。
通常,Servlet 容器错误处理在较低级别执行,并且不会有任何活动的观察或 span。
对于此用例,需要特定于容器的实现,例如 Tomcat;这超出了本项目的范围。org.apache.catalina.Valve |
默认情况下,将创建以下内容:KeyValues
名字 |
描述 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
|
HTTP 请求方法的名称,或者如果不是众所周知的方法。 |
|
HTTP 服务器交换的结果。 |
|
HTTP 响应原始状态代码,或者如果未创建响应。 |
|
匹配处理程序的 URI 模式(如果可用),回退到 3xx 响应、404 响应、没有路径信息的请求以及所有其他请求。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
响应式应用程序
应用程序需要使用 进行配置 才能启用服务器插桩。
这可以在 上完成,如下所示:WebHttpHandlerBuilder
MeterRegistry
WebHttpHandlerBuilder
import io.micrometer.observation.ObservationRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
@Configuration(proxyBeanMethods = false)
public class HttpHandlerConfiguration {
private final ApplicationContext applicationContext;
public HttpHandlerConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
public HttpHandler httpHandler(ObservationRegistry registry) {
return WebHttpHandlerBuilder.applicationContext(this.applicationContext)
.observationRegistry(registry)
.build();
}
}
默认情况下,它使用 ,由 .org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention
ServerRequestObservationContext
如果应用程序 Controller 尚未处理,则只会将观察记录为错误。
通常,Spring WebFlux 和 ProblemDetail
支持处理的所有异常都不会与观察一起记录。
在请求处理过程中,您可以随时在 self 上设置 error 字段:Exception
@ExceptionHandler
ObservationContext
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.server.ServerWebExchange;
@Controller
public class UserController {
@ExceptionHandler(MissingUserException.class)
ResponseEntity<Void> handleMissingUser(ServerWebExchange exchange, MissingUserException exception) {
// We want to record this exception with the observation
ServerRequestObservationContext.findCurrent(exchange.getAttributes())
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}
static class MissingUserException extends RuntimeException {
}
}
默认情况下,将创建以下内容:KeyValues
名字 |
描述 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
|
HTTP 请求方法的名称,或者如果不是众所周知的方法。 |
|
HTTP 服务器交换的结果。 |
|
HTTP 响应原始状态代码,或者如果未创建响应。 |
|
匹配处理程序的 URI 模式(如果可用),回退到 3xx 响应、404 响应、没有路径信息的请求以及所有其他请求。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
由于插桩是在 Servlet Filter 级别完成的,因此观察范围仅涵盖在此过滤器之后排序的过滤器以及请求的处理。
通常,Servlet 容器错误处理在较低级别执行,并且不会有任何活动的观察或 span。
对于此用例,需要特定于容器的实现,例如 Tomcat;这超出了本项目的范围。org.apache.catalina.Valve |
名字 |
描述 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
|
HTTP 请求方法的名称,或者如果不是众所周知的方法。 |
|
HTTP 服务器交换的结果。 |
|
HTTP 响应原始状态代码,或者如果未创建响应。 |
|
匹配处理程序的 URI 模式(如果可用),回退到 3xx 响应、404 响应、没有路径信息的请求以及所有其他请求。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
名字 |
描述 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
|
HTTP 请求方法的名称,或者如果不是众所周知的方法。 |
|
HTTP 服务器交换的结果。 |
|
HTTP 响应原始状态代码,或者如果未创建响应。 |
|
匹配处理程序的 URI 模式(如果可用),回退到 3xx 响应、404 响应、没有路径信息的请求以及所有其他请求。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
HTTP 客户端检测
HTTP 客户端交换观察是使用阻塞客户端和反应客户端的名称创建的。
与它们的服务器对应物不同,检测直接在 Client 端实现,因此唯一需要的步骤是在 Client 端配置 an 。"http.client.requests"
ObservationRegistry
RestTemplate (休息模板)
应用程序必须配置 on 实例才能启用检测;没有它,观察就是 “无操作”。
Spring Boot 将在已设置观察注册表的情况下自动配置 bean。ObservationRegistry
RestTemplate
RestTemplateBuilder
默认情况下,插桩使用 ,由 .org.springframework.http.client.observation.ClientRequestObservationConvention
ClientRequestObservationContext
名字 |
描述 |
|
HTTP 请求方法的名称,或者如果不是众所周知的方法。 |
|
用于 HTTP 请求的 URI 模板,或者如果未提供任何模板。仅考虑 URI 的 path 部分。 |
|
从请求 URI 主机派生的客户端名称。 |
|
HTTP 响应原始状态代码,或者 if if , 或 if 未收到响应。 |
|
HTTP 客户端交换的结果。 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
Rest客户端
应用程序必须在 上配置 才能启用检测;没有它,观察就是 “无操作”。ObservationRegistry
RestClient.Builder
默认情况下,插桩使用 ,由 .org.springframework.http.client.observation.ClientRequestObservationConvention
ClientRequestObservationContext
名字 |
描述 |
|
HTTP 请求方法的名称,或者无法创建请求。 |
|
用于 HTTP 请求的 URI 模板,或者如果未提供任何模板。仅考虑 URI 的 path 部分。 |
|
从请求 URI 主机派生的客户端名称。 |
|
HTTP 响应原始状态代码,或者 if if , 或 if 未收到响应。 |
|
HTTP 客户端交换的结果。 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
Web客户端
应用程序必须在构建器上配置 才能启用检测;没有它,观察就是 “无操作”。
Spring Boot 将在已设置观察注册表的情况下自动配置 bean。ObservationRegistry
WebClient
WebClient.Builder
默认情况下,插桩使用 ,由 .org.springframework.web.reactive.function.client.ClientRequestObservationConvention
ClientRequestObservationContext
名字 |
描述 |
|
HTTP 请求方法的名称,或者如果不是众所周知的方法。 |
|
用于 HTTP 请求的 URI 模板,或者如果未提供任何模板。仅考虑 URI 的 path 部分。 |
|
从请求 URI 主机派生的客户端名称。 |
|
HTTP 响应原始状态代码,或者 if if , 或 if 未收到响应。 |
|
HTTP 客户端交换的结果。 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
名字 |
描述 |
|
HTTP 请求方法的名称,或者如果不是众所周知的方法。 |
|
用于 HTTP 请求的 URI 模板,或者如果未提供任何模板。仅考虑 URI 的 path 部分。 |
|
从请求 URI 主机派生的客户端名称。 |
|
HTTP 响应原始状态代码,或者 if if , 或 if 未收到响应。 |
|
HTTP 客户端交换的结果。 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
名字 |
描述 |
|
HTTP 请求方法的名称,或者无法创建请求。 |
|
用于 HTTP 请求的 URI 模板,或者如果未提供任何模板。仅考虑 URI 的 path 部分。 |
|
从请求 URI 主机派生的客户端名称。 |
|
HTTP 响应原始状态代码,或者 if if , 或 if 未收到响应。 |
|
HTTP 客户端交换的结果。 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
名字 |
描述 |
|
HTTP 请求方法的名称,或者如果不是众所周知的方法。 |
|
用于 HTTP 请求的 URI 模板,或者如果未提供任何模板。仅考虑 URI 的 path 部分。 |
|
从请求 URI 主机派生的客户端名称。 |
|
HTTP 响应原始状态代码,或者 if if , 或 if 未收到响应。 |
|
HTTP 客户端交换的结果。 |
|
在交换期间或未发生异常时引发的异常的类名。 |
|
复制密钥,将来可能会将其删除。 |
名字 |
描述 |
|
HTTP 请求 URI。 |
Application Events 和@EventListener
Spring Framework 不为 @EventListener
调用提供 Observations,
因为他们没有用于此类检测的正确语义。
默认情况下,事件发布和处理是在同一线程上同步完成的。
这意味着在执行该任务期间,ThreadLocals 和日志记录上下文将与事件发布者相同。
如果应用程序使用在不同线程上调度事件处理的策略全局配置自定义,则不再如此。
所有方法都将在主事件发布线程之外的不同线程上进行处理。
在这些情况下,Micrometer Context Propagation 库可以帮助传播这些值并更好地关联事件的处理。
应用程序可以将 chosen 配置为使用 a 来装饰任务和传播上下文。
为此,该库必须存在于 Classpath 中:ApplicationEventMulticaster
@EventListener
TaskExecutor
ContextPropagatingTaskDecorator
io.micrometer:context-propagation
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;
@Configuration
public class ApplicationEventsConfiguration {
@Bean(name = "applicationEventMulticaster")
public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
// decorate task execution with a decorator that supports context propagation
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
eventMulticaster.setTaskExecutor(taskExecutor);
return eventMulticaster;
}
}
同样,如果该异步选择是在本地为每个带注解的方法进行的,则通过向该方法添加
您可以通过限定符引用 a 来传播上下文。
给定以下 bean 定义,配置了专用任务装饰器:@EventListener
@Async
TaskExecutor
TaskExecutor
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;
@Configuration
public class EventAsyncExecutionConfiguration {
@Bean(name = "propagatingContextExecutor")
public TaskExecutor propagatingContextExecutor() {
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
// decorate task execution with a decorator that supports context propagation
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
return taskExecutor;
}
}
使用 和 相关限定符对事件侦听器进行注释将获得类似的上下文传播结果:@Async
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class EmailNotificationListener {
private final Log logger = LogFactory.getLog(EmailNotificationListener.class);
@EventListener(EmailReceivedEvent.class)
@Async("propagatingContextExecutor")
public void emailReceived(EmailReceivedEvent event) {
// asynchronously process the received event
// this logging statement will contain the expected MDC entries from the propagated context
logger.info("email has been received");
}
}