Spring Boot 3 的可观测性

工程 | Marcin Grzejszczak | 2022 年 10 月 12 日 | ...

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 的知识,以及如何使用它来为您的应用程序提供更多洞察力。

Micrometer Observation 如何工作?

要进行任何观测,您需要通过 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 调用服务器的客户端。我们从服务器端开始。

WebMvc 服务器设置

由于我们要启动一个 HTTP 服务器,因此必须选择 org.springframework.boot:spring-boot-starter-web 依赖项。

要使用 @Observed 切面创建观测,我们需要添加 org.springframework.boot:spring-boot-starter-aop 依赖项。

要为您的应用程序添加观测功能,请选择 spring-boot-starter-actuator(将 Micrometer 添加到类路径中)。

现在是时候添加与可观测性相关的功能了!

  • 指标

    • 对于使用 Prometheus 的 Micrometer 指标,我们需要添加 io.micrometer:micrometer-registry-prometheus 依赖项。
  • 追踪

    • 对于使用 Micrometer Tracing 的 追踪上下文传播,我们需要选择一个 tracer 桥接器(tracer 是一个用于处理 Span 生命周期)的库。我们通过添加 io.micrometer:micrometer-tracing-bridge-brave 来选择 Zipkin Brave

      • 我们将为此演示创建的客户端应用程序将使用另一个 Tracer 库,以展示 Tracer 之间的互操作性。
    • 对于 延迟可视化,我们需要以某种格式将完成的 Span 发送到服务器。在本例中,我们生成一个 Zipkin 兼容的 Span。为此,我们需要添加 io.zipkin.reporter2:zipkin-reporter-brave 依赖项。

  • 日志

    • 由于类路径中包含了 Micrometer Tracing,日志会自动关联(即它们包含唯一的追踪标识符)。现在我们需要发送日志。对于此演示,我们将它们发送到 Grafana Loki。通过添加 com.github.loki4j:loki-logback-appender 依赖项即可实现此目的(请参阅 此链接 获取最新发布版本)

重要提示

如果您是追踪新手,我们需要快速定义几个基本术语。您可以将任何操作包装在 span 中。它有一个唯一的 span id,并包含计时信息和一些额外的元数据(键值对)。由于您可以从 Span 生成子 Span,因此整个 Span 树构成一个 trace,共享相同的 trace id(即关联标识符)。

现在我们需要添加一些配置。我们将 actuatormetrics 设置为发布百分位数直方图,并重新定义日志模式以包含追踪和 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:-}]

由于我们在本地运行包含 LokiTempoGrafana 技术栈,我们将 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>

WebMvc 服务器代码

是时候编写一些服务器端代码了!我们希望实现应用程序的全面可观测性,包括指标、追踪和额外的日志记录。

首先,我们编写一个控制器,它将消息记录到控制台并将工作委托给一个服务。

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");
    }
}

就是这样!现在轮到客户端了。

RestTemplate 客户端应用程序设置

和之前一样,我们添加 spring-boot-starter-webspring-boot-starter-actuator 依赖项以运行 Web 服务器并添加 Micrometer 支持。

是时候添加与可观测性相关的功能了!

  • 指标

    • 对于使用 Prometheus 的 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 应用程序客户端代码

现在是时候编写一些客户端代码了!我们使用 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 设置。请按照以下步骤运行基础设施和两个应用程序。

运行示例

运行示例的步骤:

  1. 启动可观测性技术栈(为演示目的,您可以使用提供的 Grafana、Tempo 和 Loki 技术栈),并等待其启动完成。

    $ docker compose up
    
  2. 运行服务器端应用程序(这将阻塞您当前的终端窗口)。

    $ ./mvnw spring-boot:run -pl :server
    
  3. 运行客户端应用程序(这将阻塞您当前的终端窗口)

    $ ./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]
    
  4. 前往 Grafana,进入仪表盘,然后点击 Logs, Traces, Metrics 仪表盘。在那里,您可以选择一个追踪 ID 值(例如,bbe3aea006077640b66d40f3e62f04b9),以找到与该追踪 ID 对应的两个应用程序的所有日志和追踪。您应该会看到与同一追踪标识符相关的日志和追踪的关联视图,并且会看到在同一时间范围内的指标。这些指标与 HTTP 请求处理延迟相关。它们来自于使用 Micrometer API 的 Spring Boot WebMvc 自动插桩。

    logs metrics traces

    请注意指标中的菱形。这些是 Exemplars。它们是“在给定时间间隔内测量的特定追踪代表”。如果您点击该形状,可以跳转到追踪 ID 视图以查看相应的追踪。

    exemplar

  5. 您可以点击追踪 ID 使用 Query it with Tempo 查询,或者前往 Tempo 自己选择追踪标识符。您将看到以下屏幕。

trace view

每个条形代表一个 span。您可以看到每个操作完成花费的时间。如果您点击某个 Span,您可以看到与该特定操作相关的标签(键值元数据)和计时信息。

span tags

这是日志在 Loki 中的关联视图的样子。

correlated logs

如果您想查看 @Observed 注解方法的指标,您可以前往 Prometheus 视图并找到 user_name 定时器。

annotation metric

如果您想查看您手动创建的 Observation 的指标,请前往 Prometheus 视图并找到 my_observation 定时器。

my observation

结合 AOT 支持一起运行

为了更好地了解 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 中查看指标、追踪和日志!

Native 支持的局限性

在客户端,我们需要手动提供 reflect-config.js 配置。更多信息请参阅 此 PR

总结

在这篇博文中,我们成功地向您介绍了 Micrometer Observability API 背后的主要概念。我们还展示了如何使用 Observation API 和注解创建观测。您还可以可视化延迟,查看关联日志,并检查来自您的 Spring Boot 应用程序的指标。

您还可以使用 Spring Native 通过 Native 镜像来观测您的应用程序。

致谢

如果没有整个 Spring 团队、Tadaya TsuyukuboJohnny Lim 以及所有其他贡献者和评审员的大力支持,Micrometer Observability 的工作是不可能完成的。

下一步

根据社区反馈,我们将继续改进我们的可观测性方案。我们计划 今年 11 月正式发布 (GA)

这对我们来说是一个令人兴奋的时刻。我们再次感谢所有已经做出贡献并报告反馈的人,并期待收到更多反馈!请查看 Spring Boot 的最新快照版本!请查看我们项目的文档:Micrometer Context PropagationMicrometerMicrometer ObservationMicrometer TracingMicrometer Docs Generator!点击 此处 查看本文使用的代码。

订阅 Spring 邮件列表

订阅 Spring 邮件列表,保持联系

订阅

领先一步

VMware 提供培训和认证,助您快速进步。

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅为您提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

近期活动

查看 Spring 社区的所有近期活动。

查看全部