|
对于最新稳定版本,请使用 Spring Framework 7.0.6! |
可观测性支持
Micrometer 定义了一种Observation(观测)概念,可在应用程序中同时支持指标(Metrics)和追踪(Traces)。 指标支持提供了一种创建计时器、仪表或计数器的方法,用于收集有关应用程序运行时行为的统计信息。 指标可以帮助你跟踪错误率、使用模式、性能等。 追踪则提供了跨越应用边界的整个系统的整体视图;你可以聚焦于特定的用户请求,并跟踪其在整个应用系统中的完整执行过程。
Spring Framework 对其自身代码库的多个部分进行了插桩,以便在配置了 ObservationRegistry 时发布观测数据。
您可以进一步了解在 Spring Boot 中配置可观测性基础设施。
已生成的观测列表
Spring Framework 提供了多种用于可观测性的功能。 如本节开头所述,根据配置的不同,观测(observations)可以生成计时器指标(Metrics)和/或追踪(Traces)。
| 观测名称 | <description> </description> |
|---|---|
HTTP 客户端交换所花费的时间 |
|
在框架级别处理 HTTP 服务器交换的时间 |
| 观测(Observations)使用的是 Micrometer 的官方命名规范,但指标(Metrics)名称将自动转换为 监控系统后端所偏好的格式 (如 Prometheus、Atlas、Graphite、InfluxDB 等)。 |
Micrometer 观测概念
如果你不熟悉 Micrometer Observation,以下是关于你需要了解的概念的简要概述。
-
Observation是对应用程序中发生的某件事情的实际记录。它由ObservationHandler的实现进行处理,以生成指标(metrics)或追踪(traces)。 -
每个观测(observation)都有一个对应的
ObservationContext实现;该类型保存了用于提取其元数据的所有相关信息。 对于 HTTP 服务器观测而言,其上下文实现可能包含 HTTP 请求、HTTP 响应、处理过程中抛出的任何异常等。 -
每个
Observation都包含KeyValues元数据。以 HTTP 服务器的 observation 为例,这些元数据可以是 HTTP 请求方法、HTTP 响应状态等。 这些元数据由ObservationConvention的实现类提供,这些实现类应声明它们所支持的ObservationContext类型。 -
如果
KeyValues元组的可能取值数量较少且有限(例如 HTTP 方法就是一个很好的例子),则称这些KeyValue为“低基数”(low cardinality)。 低基数的值仅用于指标(metrics)。 相反,“高基数”(high cardinality)的值是无界的(例如 HTTP 请求 URI),仅用于追踪(traces)。 -
ObservationDocumentation用于记录特定领域中的所有观测数据,列出预期的键名及其含义。
配置观测功能
全局配置选项可在 ObservationRegistry#observationConfig() 级别进行设置。
每个被监控的组件将提供两个扩展点:
-
设置
ObservationRegistry;如果未设置,观测数据将不会被记录,相关操作将变为无操作(no-op) -
提供一个自定义的
ObservationConvention来更改默认的观测名称和提取的KeyValues
使用自定义观测约定
以 Spring MVC 的 "http.server.requests" 指标监控为例,它使用了 ServerHttpObservationFilter。
该监控使用了一个带有 ServerRequestObservationConvention 的 ServerRequestObservationContext;自定义的约定可以在 Servlet 过滤器上进行配置。
如果您希望自定义监控所产生的元数据,可以根据自身需求继承 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 来实现类似的目标——为观测(observation)添加或移除键值。
过滤器不会替换默认的约定,而是作为后处理组件使用。
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 实例。
HTTP 服务器插桩
对于 Servlet 和响应式(Reactive)应用程序,HTTP 服务器交换观测(observations)会以名称 "http.server.requests" 创建。
Servlet 应用程序
应用程序需要在其应用中配置 org.springframework.web.filter.ServerHttpObservationFilter Servlet 过滤器。
它默认使用由 org.springframework.http.server.observation.DefaultServerRequestObservationConvention 支持的 ServerRequestObservationContext。
仅当 Exception 未被 Web 框架处理并冒泡至 Servlet 过滤器时,才会将此次观测记录为错误。
通常,由 Spring MVC 的 @ExceptionHandler 和 ProblemDetail 支持 处理的所有异常都不会被记录到该观测中。
您可以在请求处理的任何阶段,自行在 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 {
}
}
由于该检测(instrumentation)是在 Servlet 过滤器(Filter)级别完成的,因此观测(observation)范围仅涵盖排在此过滤器之后的其他过滤器以及请求的处理过程。
通常,Servlet 容器的错误处理是在更低的层级执行的,不会存在任何活跃的观测(observation)或 span。
针对这种使用场景,需要容器特定的实现方式,例如 Tomcat 中的 org.apache.catalina.Valve;这超出了本项目的范围。 |
默认情况下,会创建以下 KeyValues:
姓名 |
<description> </description> |
|
交换过程中抛出的异常名称,如果没有发生异常,则为 |
|
HTTP 请求方法的名称,如果非标准方法,则为 |
|
HTTP 服务器交换的结果。 |
|
HTTP 响应的原始状态码,如果未创建响应,则为 |
|
匹配处理器的 URI 模式(如果可用),否则对于 3xx 响应回退为 |
姓名 |
<description> </description> |
|
HTTP 请求 URI。 |
响应式应用程序
应用程序需要在其应用中配置 org.springframework.web.filter.reactive.ServerHttpObservationFilter 这个响应式的 WebFilter。
它默认使用 org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention,并由 ServerRequestObservationContext 提供支持。
仅当 Exception 未被 Web 框架处理并一直冒泡到 WebFilter 时,才会将观测记录为错误。
通常,由 Spring WebFlux 的 @ExceptionHandler 和 ProblemDetail 支持 处理的所有异常都不会被记录到观测中。
您可以在请求处理的任何阶段,自行在 ObservationContext 上设置错误字段:
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
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
ServerHttpObservationFilter.findObservationContext(exchange)
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}
static class MissingUserException extends RuntimeException {
}
}
默认情况下,会创建以下 KeyValues:
姓名 |
<description> </description> |
|
交换过程中抛出的异常名称,如果未发生异常,则为 |
|
HTTP 请求方法的名称,如果非标准方法,则为 |
|
HTTP 服务器交换的结果。 |
|
HTTP 响应的原始状态码,如果未创建响应,则为 |
|
匹配处理器的 URI 模式(如果可用),否则对于 3xx 响应回退为 |
姓名 |
<description> </description> |
|
HTTP 请求 URI。 |
HTTP 客户端插桩
HTTP 客户端交换观测(observations)会为阻塞式和响应式客户端创建,其名称为 "http.client.requests"。
与服务器端的对应项不同,该仪器化(instrumentation)直接在客户端中实现,因此唯一需要的步骤就是在客户端上配置一个 ObservationRegistry。
RestTemplate
应用程序必须在 ObservationRegistry 实例上配置一个 RestTemplate 才能启用仪器化;否则,观测操作将变为“空操作”(no-ops)。
Spring Boot 会自动配置已设置好观测注册表(observation registry)的 RestTemplateBuilder Bean。
Instrumentation 默认使用 org.springframework.http.client.observation.ClientRequestObservationConvention,其底层由 ClientRequestObservationContext 支持。
姓名 |
<description> </description> |
|
HTTP 请求方法的名称,如果非标准方法,则为 |
|
用于 HTTP 请求的 URI 模板,如果未提供则为 |
|
从请求 URI 的主机名派生的客户端名称。 |
|
HTTP 响应的原始状态码,如果发生 |
|
HTTP 客户端交换的结果。 |
|
交换过程中抛出的异常名称,如果未发生异常,则为 |
姓名 |
<description> </description> |
|
HTTP 请求 URI。 |
WebClient
应用程序必须在 ObservationRegistry 构建器上配置一个 WebClient 以启用监控;否则,监控操作将变为“空操作”(no-ops)。
Spring Boot 会自动配置已设置好监控注册表(observation registry)的 WebClient.Builder Bean。
Instrumentation 默认使用 org.springframework.web.reactive.function.client.ClientRequestObservationConvention,其底层由 ClientRequestObservationContext 支持。
姓名 |
<description> </description> |
|
HTTP 请求方法的名称,如果非标准方法,则为 |
|
用于 HTTP 请求的 URI 模板,如果未提供则为 |
|
从请求 URI 的主机名派生的客户端名称。 |
|
HTTP 响应的原始状态码,如果发生 |
|
HTTP 客户端交换的结果。 |
|
交换过程中抛出的异常名称,如果未发生异常,则为 |
姓名 |
<description> </description> |
|
HTTP 请求 URI。 |