领先一步
VMware 提供培训和认证,助您快速进步。
了解更多Spring 可观测性团队致力于为 Spring 应用添加可观测性支持已有一段时间,我们很高兴地通知您,此功能将在 Spring Framework 6 和 Spring Boot 3 中正式发布!
什么是可观测性?在我们看来,它是 “通过检查系统的输出来了解其内部运行状况的能力”。我们认为,将指标、日志和分布式追踪结合起来,您就能更好地理解系统状态,从而调试应用程序中的异常和延迟问题。您可以在 Jonatan Ivanov 的 Enlightning 节目中 了解更多关于我们对可观测性的看法。
即将发布的 Spring Boot 3.0.0-RC1
版本将包含许多自动配置,以改进与 Micrometer 的指标功能,并新增与 Micrometer Tracing(前身为 Spring Cloud Sleuth)的分布式追踪支持。最显著的变化包括内置的日志关联支持,W3C context propagation 将成为默认的传播类型,并且我们将支持元数据的自动传播,供追踪基础设施使用(称为“远程行李”),这有助于标记观测结果。
今年以来,我们对 Micrometer API 进行了大量更改。最重要的变化是引入了一个新的 API:Observation API。
其创立理念是希望用户只需使用一个 API 对代码进行一次插桩,即可从中获得多种好处(例如,指标、追踪、日志记录)。
这篇博文详细介绍了您需要了解的关于该 API 的知识,以及如何使用它来为您的应用程序提供更多洞察力。
要进行任何观测,您需要通过 ObservationRegistry
注册 ObservationHandler
对象。ObservationHandler
只对 Observation.Context
的受支持实现做出反应,并且可以通过对观测的生命周期事件(例如以下事件)做出反应来创建定时器、Span 和日志等:
start
- 观测已开始。在调用 Observation#start()
方法时发生。
stop
- 观测已停止。在调用 Observation#stop()
方法时发生。
error
- 观测期间发生错误。在调用 Observation#error(exception)
方法时发生。
event
- 观测期间发生事件。在调用 Observation#event(event)
方法时发生。
scope started
- 观测开启一个范围(scope)。该范围不再使用时必须关闭。处理程序可以在开始时创建线程本地变量,并在范围关闭时清除这些变量。在调用 Observation#openScope()
方法时发生。
scope stopped
- 观测停止一个范围(scope)。在调用 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
观测范围(Scope)的状态图如下所示
Observation
Context
Scope Started ----------> Scope Closed
为了能够调试生产问题,观测需要额外的元数据,例如键值对(也称为标签)。然后您可以使用这些标签查询您的指标或分布式追踪后端来查找所需的数据。标签可以是高基数或低基数。
这是 Micrometer Observation API 的一个示例。
// Create an ObservationRegistry
ObservationRegistry registry = ObservationRegistry.create();
// Register an ObservationHandler
registry.observationConfig().observationHandler(new MyHandler());
// Create an Observation and observe your code!
Observation.createNotStarted("user.name", registry)
.contextualName("getting-user-name")
.lowCardinalityKeyValue("userType", "userType1") // let's assume that you can have 3 user types
.highCardinalityKeyValue("userId", "1234") // let's assume that this is an arbitrary number
.observe(() -> log.info("Hello")); // this is a shortcut for starting an observation, opening a scope, running user's code, closing the scope and stopping the observation
重要提示
高基数意味着一对键值可能具有无限数量的值。HTTP URL 就是一个很好的示例(例如,/user/user1234
、/user/user2345
等等)。低基数意味着一对键值具有有限数量的值。模板化的 HTTP URL(例如 /user/{userId}
)就是这种键值的一个很好的示例。
为了将观测的生命周期操作与观测配置(如名称以及低基数和高基数标签)分开,您可以使用 ObservationConvention
,它提供了一种轻松覆盖默认命名约定的方法。
最简单的入门方法是从 https://start.spring.io 创建一个新项目。请确保选择 Spring Boot 3.0.0-SNAPSHOT(在 RC1 可用后 您可以切换到 RC1)以及您喜欢的构建工具。
我们将构建一个 Spring WebMvc 服务器应用程序和一个使用 RestTemplate 调用服务器的客户端。我们从服务器端开始。
由于我们要启动一个 HTTP 服务器,因此必须选择 org.springframework.boot:spring-boot-starter-web
依赖项。
要使用 @Observed
切面创建观测,我们需要添加 org.springframework.boot:spring-boot-starter-aop
依赖项。
要为您的应用程序添加观测功能,请选择 spring-boot-starter-actuator
(将 Micrometer 添加到类路径中)。
现在是时候添加与可观测性相关的功能了!
指标
io.micrometer:micrometer-registry-prometheus
依赖项。追踪
对于使用 Micrometer Tracing 的 追踪上下文传播,我们需要选择一个 tracer 桥接器(tracer 是一个用于处理 Span 生命周期)的库。我们通过添加 io.micrometer:micrometer-tracing-bridge-brave
来选择 Zipkin Brave。
对于 延迟可视化,我们需要以某种格式将完成的 Span 发送到服务器。在本例中,我们生成一个 Zipkin 兼容的 Span。为此,我们需要添加 io.zipkin.reporter2:zipkin-reporter-brave
依赖项。
日志
com.github.loki4j:loki-logback-appender
依赖项即可实现此目的(请参阅 此链接 获取最新发布版本)重要提示
如果您是追踪新手,我们需要快速定义几个基本术语。您可以将任何操作包装在 span
中。它有一个唯一的 span id
,并包含计时信息和一些额外的元数据(键值对)。由于您可以从 Span 生成子 Span,因此整个 Span 树构成一个 trace
,共享相同的 trace id
(即关联标识符)。
现在我们需要添加一些配置。我们将 actuator
和 metrics
设置为发布百分位数直方图,并重新定义日志模式以包含追踪和 Span 标识符。我们将采样概率设置为 1.0
,以将所有追踪发送到延迟分析工具。
/src/main/resources/application.properties
server.port=7654
spring.application.name=server
# All traces should be sent to latency analysis tool
management.tracing.sampling.probability=1.0
management.endpoints.web.exposure.include=prometheus
# For Exemplars to work we need histogram buckets
management.metrics.distribution.percentiles-histogram.http.server.requests=true
# traceID and spanId are predefined MDC keys - we want the logs to include them
logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]
由于我们在本地运行包含 Loki 和 Tempo 的 Grafana 技术栈,我们将 loki-logback-appender
配置为将日志发送到本地 Loki 实例。
/src/main/resources/logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<springProperty scope="context" name="appName" source="spring.application.name"/>
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
<http>
<url>http://localhost:3100/loki/api/v1/push</url>
</http>
<format>
<label>
<pattern>app=${appName},host=${HOSTNAME},traceID=%X{traceId:-NONE},level=%level</pattern>
</label>
<message>
<pattern>${FILE_LOG_PATTERN}</pattern>
</message>
<sortByTime>true</sortByTime>
</format>
</appender>
<root level="INFO">
<appender-ref ref="LOKI"/>
</root>
</configuration>
是时候编写一些服务器端代码了!我们希望实现应用程序的全面可观测性,包括指标、追踪和额外的日志记录。
首先,我们编写一个控制器,它将消息记录到控制台并将工作委托给一个服务。
MyController.java
@RestController
class MyController {
private static final Logger log = LoggerFactory.getLogger(MyController.class);
private final MyUserService myUserService;
MyController(MyUserService myUserService) {
this.myUserService = myUserService;
}
@GetMapping("/user/{userId}")
String userName(@PathVariable("userId") String userId) {
log.info("Got a request");
return myUserService.userName(userId);
}
}
我们希望对 MyUserService#userName
方法进行一些详细的观测。由于添加了 AOP 支持,我们可以使用 @Observed
注解。为此,我们可以注册一个 ObservedAspect
Bean。
MyConfiguration.java
@Configuration(proxyBeanMethods = false)
class MyConfiguration {
// To have the @Observed support we need to register this aspect
@Bean
ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}
}
MyUserService.java
@Service
class MyUserService {
private static final Logger log = LoggerFactory.getLogger(MyUserService.class);
private final Random random = new Random();
// Example of using an annotation to observe methods
// <user.name> will be used as a metric name
// <getting-user-name> will be used as a span name
// <userType=userType2> will be set as a tag for both metric & span
@Observed(name = "user.name",
contextualName = "getting-user-name",
lowCardinalityKeyValues = {"userType", "userType2"})
String userName(String userId) {
log.info("Getting user name for user with id <{}>", userId);
try {
Thread.sleep(random.nextLong(200L)); // simulates latency
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "foo";
}
}
在类路径中包含指标和追踪的情况下,使用此注解会导致创建一个 timer
、一个 long task timer
和一个 span
。该定时器将命名为 user.name
,长时间任务定时器将命名为 user.name.active
,Span 将命名为 getting-user-name
。
日志呢?我们不希望在每次观测发生时手动编写日志语句。我们可以创建一个专用的处理程序,为每个观测记录一些文本。
MyHandler.java
// Example of plugging in a custom handler that in this case will print a statement before and after all observations take place
@Component
class MyHandler implements ObservationHandler<Observation.Context> {
private static final Logger log = LoggerFactory.getLogger(MyHandler.class);
@Override
public void onStart(Observation.Context context) {
log.info("Before running the observation for context [{}], userType [{}]", context.getName(), getUserTypeFromContext(context));
}
@Override
public void onStop(Observation.Context context) {
log.info("After running the observation for context [{}], userType [{}]", context.getName(), getUserTypeFromContext(context));
}
@Override
public boolean supportsContext(Observation.Context context) {
return true;
}
private String getUserTypeFromContext(Observation.Context context) {
return StreamSupport.stream(context.getLowCardinalityKeyValues().spliterator(), false)
.filter(keyValue -> "userType".equals(keyValue.getKey()))
.map(KeyValue::getValue)
.findFirst()
.orElse("UNKNOWN");
}
}
就是这样!现在轮到客户端了。
和之前一样,我们添加 spring-boot-starter-web
和 spring-boot-starter-actuator
依赖项以运行 Web 服务器并添加 Micrometer 支持。
是时候添加与可观测性相关的功能了!
指标
io.micrometer:micrometer-registry-prometheus
依赖项。追踪
对于使用 Micrometer Tracing 的 追踪上下文传播,我们需要选择一个 tracer 桥接器。我们通过添加 io.micrometer:micrometer-tracing-bridge-otel
来选择 OpenTelemetry。
对于 延迟可视化,我们需要以某种格式将完成的 Span 发送到服务器。在本例中,我们生成一个 OpenZipkin 兼容的 Span。为此,我们需要添加 io.opentelemetry:opentelemetry-exporter-zipkin
依赖项。
日志
com.github.loki4j:loki-logback-appender
依赖项(请参阅 此链接 获取最新发布版本)将日志发送到 Loki。现在我们需要添加一些配置。我们添加了与服务器端几乎相同的配置。
/src/main/resources/application.properties
server.port=6543
spring.application.name=client
# All traces should be sent to latency analysis tool
management.tracing.sampling.probability=1.0
management.endpoints.web.exposure.include=prometheus
# traceID and spanId are predefined MDC keys - we want the logs to include them
logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]
Loki Appender 的配置完全相同。
/src/main/resources/logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<springProperty scope="context" name="appName" source="spring.application.name"/>
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
<http>
<url>http://localhost:3100/loki/api/v1/push</url>
</http>
<format>
<label>
<pattern>app=${appName},host=${HOSTNAME},traceID=%X{traceId:-NONE},level=%level</pattern>
</label>
<message>
<pattern>${FILE_LOG_PATTERN}</pattern>
</message>
<sortByTime>true</sortByTime>
</format>
</appender>
<root level="INFO">
<appender-ref ref="LOKI"/>
</root>
</configuration>
现在是时候编写一些客户端代码了!我们使用 RestTemplate
向服务器端发送请求,并且希望实现应用程序的全面可观测性,包括指标和追踪。
首先,我们需要一个由 Spring Boot 自动进行插桩的 RestTemplate
Bean。请记住注入 RestTemplateBuilder
并从该构建器构造一个 RestTemplate
实例。
MyConfiguration.java
@Configuration(proxyBeanMethods = false)
class MyConfiguration {
// IMPORTANT! To instrument RestTemplate you must inject the RestTemplateBuilder
@Bean
RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
现在我们可以编写一个使用 Observation API 进行包装的 CommandLineRunner
Bean,它向服务器端发送请求。该 API 的所有部分将在以下代码片段中详细描述。
MyConfiguration.java
@Configuration(proxyBeanMethods = false)
class MyConfiguration {
@Bean
CommandLineRunner myCommandLineRunner(ObservationRegistry registry, RestTemplate restTemplate) {
Random highCardinalityValues = new Random(); // Simulates potentially large number of values
List<String> lowCardinalityValues = Arrays.asList("userType1", "userType2", "userType3"); // Simulates low number of values
return args -> {
String highCardinalityUserId = String.valueOf(highCardinalityValues.nextLong(100_000));
// Example of using the Observability API manually
// <my.observation> is a "technical" name that does not depend on the context. It will be used to name e.g. Metrics
Observation.createNotStarted("my.observation", registry)
// Low cardinality means that the number of potential values won't be big. Low cardinality entries will end up in e.g. Metrics
.lowCardinalityKeyValue("userType", randomUserTypePicker(lowCardinalityValues))
// High cardinality means that the number of potential values can be large. High cardinality entries will end up in e.g. Spans
.highCardinalityKeyValue("userId", highCardinalityUserId)
// <command-line-runner> is a "contextual" name that gives more details within the provided context. It will be used to name e.g. Spans
.contextualName("command-line-runner")
// The following lambda will be executed with an observation scope (e.g. all the MDC entries will be populated with tracing information). Also the observation will be started, stopped and if an error occurred it will be recorded on the observation
.observe(() -> {
log.info("Will send a request to the server"); // Since we're in an observation scope - this log line will contain tracing MDC entries ...
String response = restTemplate.getForObject("http://localhost:7654/user/{userId}", String.class, highCardinalityUserId); // Boot's RestTemplate instrumentation creates a child span here
log.info("Got response [{}]", response); // ... so will this line
});
};
}
}
我们已在 此链接 下准备了整个可观测性基础设施的 Docker 设置。请按照以下步骤运行基础设施和两个应用程序。
运行示例的步骤:
启动可观测性技术栈(为演示目的,您可以使用提供的 Grafana、Tempo 和 Loki 技术栈),并等待其启动完成。
$ docker compose up
要访问 Prometheus,请前往 http://localhost:9090/
要访问 Grafana,请前往 http://localhost:3000/
运行服务器端应用程序(这将阻塞您当前的终端窗口)。
$ ./mvnw spring-boot:run -pl :server
运行客户端应用程序(这将阻塞您当前的终端窗口)
$ ./mvnw spring-boot:run -pl :client
您应该会看到类似于以下的日志语句
2022-10-04T15:04:55.345+02:00 INFO [client,bbe3aea006077640b66d40f3e62f04b9,93b7a150b7e293ef] 92556 --- [ main] com.example.client.ClientApplication : Will send a request to the server
2022-10-04T15:04:55.385+02:00 INFO [client,bbe3aea006077640b66d40f3e62f04b9,93b7a150b7e293ef] 92556 --- [ main] com.example.client.ClientApplication : Got response [foo]
前往 Grafana,进入仪表盘,然后点击 Logs, Traces, Metrics
仪表盘。在那里,您可以选择一个追踪 ID 值(例如,bbe3aea006077640b66d40f3e62f04b9
),以找到与该追踪 ID 对应的两个应用程序的所有日志和追踪。您应该会看到与同一追踪标识符相关的日志和追踪的关联视图,并且会看到在同一时间范围内的指标。这些指标与 HTTP 请求处理延迟相关。它们来自于使用 Micrometer API 的 Spring Boot WebMvc 自动插桩。
请注意指标中的菱形。这些是 Exemplars
。它们是“在给定时间间隔内测量的特定追踪代表”。如果您点击该形状,可以跳转到追踪 ID 视图以查看相应的追踪。
您可以点击追踪 ID 使用 Query it with Tempo
查询,或者前往 Tempo 自己选择追踪标识符。您将看到以下屏幕。
每个条形代表一个 span
。您可以看到每个操作完成花费的时间。如果您点击某个 Span,您可以看到与该特定操作相关的标签(键值元数据)和计时信息。
这是日志在 Loki 中的关联视图的样子。
如果您想查看 @Observed
注解方法的指标,您可以前往 Prometheus
视图并找到 user_name
定时器。
如果您想查看您手动创建的 Observation 的指标,请前往 Prometheus
视图并找到 my_observation
定时器。
为了更好地了解 Spring Boot 如何支持 Native,请阅读 这篇精彩的博文。我们重用这些知识来使用 Spring Native 运行之前创建的应用程序。
要构建应用程序,您的路径中需要有 GraalVM。如果您使用 SDKMan
,请执行以下命令
sdk install java 22.3.r17.ea-nik
另请参阅 GraalVM 快速入门。
要使用 Maven 构建应用程序,您需要启用 native
profile
$ ./mvnw native:compile -Pnative
首先运行服务器端应用程序
$ ./server/target/server
接下来,运行客户端应用程序。
$ ./client/target/client
您应该会得到类似于以下的输出
客户端日志
2022-10-10T12:57:17.712+02:00 INFO \[client,,\] 82009 --- \[ main\] com.example.client.ClientApplication : Starting ClientApplication using Java 17.0.4 on marcin-precision5560 with PID 82009 (/home/marcin/repo/observability/blogs/bootRc1/client/target/client started by marcin in /home/marcin/repo/observability/blogs/bootRc1)
2022-10-10T12:57:17.712+02:00 INFO \[client,,\] 82009 --- \[ main\] com.example.client.ClientApplication : No active profile set, falling back to 1 default profile: "default"
2022-10-10T12:57:17.723+02:00 INFO \[client,,\] 82009 --- \[ main\] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 6543 (http)
2022-10-10T12:57:17.723+02:00 INFO \[client,,\] 82009 --- \[ main\] o.apache.catalina.core.StandardService : Starting service \[Tomcat\]
2022-10-10T12:57:17.723+02:00 INFO \[client,,\] 82009 --- \[ main\] o.apache.catalina.core.StandardEngine : Starting Servlet engine: \[Apache Tomcat/10.0.23\]
2022-10-10T12:57:17.727+02:00 INFO \[client,,\] 82009 --- \[ main\] o.a.c.c.C.\[Tomcat\].\[localhost\].\[/\] : Initializing Spring embedded WebApplicationContext
2022-10-10T12:57:17.727+02:00 INFO \[client,,\] 82009 --- \[ main\] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 15 ms
2022-10-10T12:57:17.731+02:00 WARN \[client,,\] 82009 --- \[ main\] i.m.c.i.binder.jvm.JvmGcMetrics : GC notifications will not be available because MemoryPoolMXBeans are not provided by the JVM
2022-10-10T12:57:17.781+02:00 INFO \[client,,\] 82009 --- \[ main\] o.s.b.a.e.web.EndpointLinksResolver : Exposing 15 endpoint(s) beneath base path '/actuator'
2022-10-10T12:57:17.783+02:00 INFO \[client,,\] 82009 --- \[ main\] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 6543 (http) with context path ''
2022-10-10T12:57:17.783+02:00 INFO \[client,,\] 82009 --- \[ main\] com.example.client.ClientApplication : Started ClientApplication in 0.077 seconds (process running for 0.079)
2022-10-10T12:57:17.784+02:00 INFO \[client,27c1113e4276c4173daec3675f536bf4,e0f2db8b983607d8\] 82009 --- \[ main\] com.example.client.ClientApplication : Will send a request to the server
2022-10-10T12:57:17.820+02:00 INFO \[client,27c1113e4276c4173daec3675f536bf4,e0f2db8b983607d8\] 82009 --- \[ main\] com.example.client.ClientApplication : Got response \[foo\]
2022-10-10T12:57:18.966+02:00 INFO \[client,,\] 82009 --- \[nio-6543-exec-1\] o.a.c.c.C.\[Tomcat\].\[localhost\].\[/\] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-10-10T12:57:18.966+02:00 INFO \[client,,\] 82009 --- \[nio-6543-exec-1\] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-10-10T12:57:18.966+02:00 INFO \[client,,\] 82009 --- \[nio-6543-exec-1\] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
服务器端日志
2022-10-10T12:57:07.200+02:00 INFO \[server,,\] 81760 --- \[ main\] com.example.server.ServerApplication : Starting ServerApplication using Java 17.0.4 on marcin-precision5560 with PID 81760 (/home/marcin/repo/observability/blogs/bootRc1/server/target/server started by marcin in /home/marcin/repo/observability/blogs/bootRc1)
2022-10-10T12:57:07.201+02:00 INFO \[server,,\] 81760 --- \[ main\] com.example.server.ServerApplication : No active profile set, falling back to 1 default profile: "default"
2022-10-10T12:57:07.213+02:00 INFO \[server,,\] 81760 --- \[ main\] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 7654 (http)
2022-10-10T12:57:07.213+02:00 INFO \[server,,\] 81760 --- \[ main\] o.apache.catalina.core.StandardService : Starting service \[Tomcat\]
2022-10-10T12:57:07.213+02:00 INFO \[server,,\] 81760 --- \[ main\] o.apache.catalina.core.StandardEngine : Starting Servlet engine: \[Apache Tomcat/10.0.23\]
2022-10-10T12:57:07.217+02:00 INFO \[server,,\] 81760 --- \[ main\] o.a.c.c.C.\[Tomcat\].\[localhost\].\[/\] : Initializing Spring embedded WebApplicationContext
2022-10-10T12:57:07.217+02:00 INFO \[server,,\] 81760 --- \[ main\] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 16 ms
2022-10-10T12:57:07.222+02:00 WARN \[server,,\] 81760 --- \[ main\] i.m.c.i.binder.jvm.JvmGcMetrics : GC notifications will not be available because MemoryPoolMXBeans are not provided by the JVM
2022-10-10T12:57:07.278+02:00 INFO \[server,,\] 81760 --- \[ main\] o.s.b.a.e.web.EndpointLinksResolver : Exposing 15 endpoint(s) beneath base path '/actuator'
2022-10-10T12:57:07.280+02:00 INFO \[server,,\] 81760 --- \[ main\] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 7654 (http) with context path ''
2022-10-10T12:57:07.281+02:00 INFO \[server,,\] 81760 --- \[ main\] com.example.server.ServerApplication : Started ServerApplication in 0.086 seconds (process running for 0.088)
2022-10-10T12:57:07.639+02:00 INFO \[server,,\] 81760 --- \[nio-7654-exec-1\] o.a.c.c.C.\[Tomcat\].\[localhost\].\[/\] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-10-10T12:57:07.639+02:00 INFO \[server,,\] 81760 --- \[nio-7654-exec-1\] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-10-10T12:57:07.640+02:00 INFO \[server,,\] 81760 --- \[nio-7654-exec-1\] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2022-10-10T12:57:17.785+02:00 INFO \[server,,\] 81760 --- \[nio-7654-exec-8\] com.example.server.MyHandler : Before running the observation for context \[http.server.requests\]
2022-10-10T12:57:17.785+02:00 INFO \[server,27c1113e4276c4173daec3675f536bf4,9affba5698490e2d\] 81760 --- \[nio-7654-exec-8\] com.example.server.MyController : Got a request
2022-10-10T12:57:17.820+02:00 INFO \[server,,\] 81760 --- \[nio-7654-exec-8\] com.example.server.MyHandler : After running the observation for context \[http.server.requests\]
您可以在 Grafana 中查看指标、追踪和日志!
在客户端,我们需要手动提供 reflect-config.js
配置。更多信息请参阅 此 PR。
在这篇博文中,我们成功地向您介绍了 Micrometer Observability API 背后的主要概念。我们还展示了如何使用 Observation API 和注解创建观测。您还可以可视化延迟,查看关联日志,并检查来自您的 Spring Boot 应用程序的指标。
您还可以使用 Spring Native 通过 Native 镜像来观测您的应用程序。
如果没有整个 Spring 团队、Tadaya Tsuyukubo、Johnny Lim 以及所有其他贡献者和评审员的大力支持,Micrometer Observability 的工作是不可能完成的。
根据社区反馈,我们将继续改进我们的可观测性方案。我们计划 今年 11 月正式发布 (GA)。
这对我们来说是一个令人兴奋的时刻。我们再次感谢所有已经做出贡献并报告反馈的人,并期待收到更多反馈!请查看 Spring Boot 的最新快照版本!请查看我们项目的文档:Micrometer Context Propagation、Micrometer、Micrometer Observation、Micrometer Tracing 和 Micrometer Docs Generator!点击 此处 查看本文使用的代码。