天天看點

Spring Boot 3.0 要來了,這個特性真心強

作者:二哥學Java

前言

Spring 可觀察性團隊一直緻力于為 Spring 應用程式添加可觀察性支援,該特性将在 Spring Framework 6 和 Spring Boot 3 中更加簡單、易用! 通過可觀測性,能更好的了解系統内部運作狀态。metrics, logging 和分布式 tracing 之間的互相連通能更好的推斷系統的運作狀态,以便調試應用程式中的異常、延遲和性能。

即将釋出的 Spring Boot3.0.0-RC1 将包含大量的自動配置,用于使用 Micrometer 改進 metrics,并通過 Micrometer tracing (以前稱為 Spring Cloud Sleuth)提供新的分布式 tracing 支援。最值得注意的變化是,它将包含對 log 關聯的内置支援,W3C上下文傳遞将是預設傳播類型,我們将支援自動傳播中繼資料,以供 tracing 基礎設施(稱為“遠端包裹”)使用,幫助标記觀察結果。

Micrometer API 得到了大量的改進,最重要的變化是我們引入了一個新的API:Observation API。

它建立理念是,希望使用者使用單一 API 就能檢測代碼,并從中獲得多種資訊(例如 metrics, tracing, logging)。

這篇博文詳細介紹了你需要了解的API,以及如何使用它來為你的應用程式提供更多可觀測資訊。

一、Micrometer Observation 怎麼運作?

我們需要通過 ObservationRegistry 注冊 ObservationHandler 對象。ObservationHandler 僅對支援的 Observation.Context 實作起作用。Context`and可以通過對觀察的生命周期事件作出反應來建立計時器、跨度和日志,例如: 觀測處理程式僅對受支援的觀測實作作出反應。上下文,可以通過對觀察的生命周期事件作出反應來建立計時器、跨度和日志,例如:

  • start - 調用 Observation#start() 方法啟動 Observation。
  • stop - 調用 Observation#stop() 方法停止 Observation。
  • error - 調用 Observation#error(exception) 方法抛出觀測。
  • event - 調用 Observation#event(event) 觀察時發生的事。
  • scope started - 調用 Observation#openScope() 方法時,打開了一個 scope。不使用時必須關閉。可以在此建立線程局部變量,這些變量在 scope 關閉時被清除。
  • scope stopped - 調用 Observation.Scope#close() 方法時停止 Observation scope。 每當調用這些方法中的任何一個時,都會調用 ObservationHandler 方法(例如 onStart(T extends Observation.Context ctx) 、onStop(T extends Observation.Context ctx) 等)。要在處理方法之間傳遞狀态,可以使用Observation.Context。

Observation 狀态圖如下:

Observation           Observation
        Context               Context
Created ----------> Started ----------> Stopped
           

Observation Scope 狀态圖如下:

Observation
              Context
Scope Started ----------> Scope Closed
           

為了調試生産問題,observation 需要額外的中繼資料,例如鍵值對(也稱為 tags)。然後,可以使用這些 tag 來查找所需的資料,進而查詢 metrics 或分布式 tracing 後端。tag 可以是高基數或低基數。

這是 Micrometer Observation API 的一個示例。

// 建立 ObservationRegistry
ObservationRegistry registry = ObservationRegistry.create();
// 注冊 ObservationHandler
registry.observationConfig().observationHandler(new MyHandler());

// 建立 Observation!
Observation.createNotStarted("user.name", registry)
        .contextualName("getting-user-name")
        .lowCardinalityKeyValue("userType", "userType1") // 假設你有3種使用者類型
        .highCardinalityKeyValue("userId", "1234") // 假設這是一個任意數
        .observe(() -> log.info("Hello")); // 這是啟動觀察、打開範圍、運作使用者代碼、關閉範圍和停止觀察的快捷方式
           

重要說明:高基數意味着一對可能值的數量是無限多的。HTTP URL 就是這樣一個鍵值的好例子(例如 /user/user1234,/user/user2345 等)。低基數意味着鍵值将具有有限數量的可能值。模闆化的 HTTP URL(例如 /user/{userId})就是這樣一個鍵值的好例子。

要将 observation 生命周期操作與 observation 配置(例如名稱以及低基數和高基數标記)分離,可以使用 ObservationConvention ,它提供了一種覆寫預設命名約定的簡單方法。

二、建構可觀測應用

開始的最簡單方法是從 https://start.spring.io。確定選擇的時 Spring Boot 3.0.0-SNAPSHOT (你可以切換到 RC1 如果他已經釋出) 和你喜歡的建構工具。 我們将建構一個 Spring WebMvc 服務端應用程式和一個 RestTemplate 調用伺服器的用戶端。下面我們從服務端開始。

2.1 WebMvc 服務啟動

因為我們想啟動 HTTP 服務, 需要選擇 org.springframework.boot:spring-boot-starter-web 依賴。 要使用 @Observed 切面建立 observations, 我們需要添加org.springframework.boot:spring-boot-starter-aop 依賴。 要在應用中添加可觀測功能,請選擇 spring-boot-starter-actuator (并将 Micrometer 添加到 classpath).

  • Metrics
    • 對于 Micrometer metrics 可以使用 Prometheus, 隻需要添加io.micrometer:micrometer-registry-prometheus 依賴.
  • Tracing
    • 對于使用 Micrometer Tracing 跟蹤 Tracing 上下文傳播 , 我們需要選擇一個 tracer橋接 (tracer 用于處理跨度生命周期的庫). 我們選擇 Zipkin Brave 然後添加 io.micrometer:micrometer-tracing-bridge-brave依賴。
    • 對于延遲可視化, 我們需要以某種格式将完成的 spans 發送到伺服器。在我們的案例中我們生成了一個符合 Zipkin-compliant 标準的 span。為此,我們需要添加io.zipkin.reporter2:zipkin-reporter-brave 依賴。
  • Logs
    • 因為我們在 classpath 添加了 Micrometer Tracing, 是以 logs 也是自動關聯的 (也就是說,它們包含一個惟一的 trace 辨別符). 為了這個示範,我們将日志推送到 Grafana Loki。我們可以通過添加 com.github.loki4j:loki-logback-appender:latest.release 依賴來實作。 | 重要說明 | 如果你不熟悉 tracing,我們需要快速定義兩個基本術語。你可以在 span 中包裝任何操作。它具有唯一的 span id,并包含計時資訊和一些附加的中繼資料(鍵-值對)。因為可以從 span 生成子 span,是以整個 span 樹形成一個共享相同 trace id(即關聯辨別符)的 trace。

現在我們需要添加一些配置。我們設定actuator 和metrics用來生成百分位直方圖,并重新定義日志模式以包括跟蹤和範圍辨別符。我們将采樣機率設定為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>
           

2.2 WebMvc 服務端代碼

是時候編寫一些伺服器端代碼了!我們希望實作應用程式的完全可觀察性,包括metrics, tracing 和附加 logging 記錄。 首先,我們編寫一個控制器,列印 log 并調用 service。

// 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。 logs 我們則可以建立一個專門的處理程式,為每個觀察記錄一些日志資訊。

// MyHandler.java
@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");
    }
}
           

注冊一個 bean 打開控制器的可觀察性。

// MyConfiguration.java
@Configuration(proxyBeanMethods = false)
class MyConfiguration {
    // You must set this manually until this is registered in Boot
    @Bean
    FilterRegistrationBean observationWebFilter(ObservationRegistry observationRegistry) {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new HttpRequestsObservationFilter(observationRegistry));
        filterRegistrationBean.setDispatcherTypes(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.FORWARD,
                DispatcherType.INCLUDE, DispatcherType.REQUEST);
        filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        // We provide a list of URLs that we want to create observations for
        filterRegistrationBean.setUrlPatterns(Collections.singletonList("/user/*"));
        return filterRegistrationBean;
    }
}
           

2.3 RestTemplate 用戶端啟動

首先我們添加 spring-boot-starter-web 和 spring-boot-starter-actuator 依賴 運作web伺服器并添加 Micrometer 支援。 是時候添加可觀察性相關功能了!

  • Metrics
    • 對于 Micrometer metrics 可以使用 Prometheus, 隻需要添加 io.micrometer:micrometer-registry-prometheus 依賴。
  • Tracing
    • 對于使用 Micrometer Tracing 跟蹤 Tracing 上下文傳播,我們需要選擇一個 tracer橋. 我選擇 OpenTelemetry 并添加 io.micrometer:micrometer-tracing-bridge-otel依賴。
    • 對于**延遲可視化,**我們需要以某種格式将完成的 spans 發送到伺服器。在我們的案例中我們生成了一個符合 OpenZipkin compliant 标準 span, 我們需要添加 io.opentelemetry:opentelemetry-exporter-zipkin 依賴。
  • Logs
    • 添加 com.github.loki4j:loki-logback-appender:latest.release 依賴并将日志發送到 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>
           

2.4 RestTemplate 應用代碼

我們利用 RestTemplate 發送一個請求,我們希望實作應用程式的全部可觀測性,包括 metrics 和 tracing。 首先,我們使用 RestTemplateBuilder 建立一個 RestTemplatebean。

// MyConfiguration.java
@Configuration(proxyBeanMethods = false)
class MyConfiguration {
    // IMPORTANT! To instrument RestTemplate you must inject the RestTemplateBuilder
    @Bean
    RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }
}
           

現在,我們可以編寫一個 CommandLineRunner bean,它使用 Observation API包裝,并向伺服器端發送請求。下面的代碼片段更詳細地描述了 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
                    });

        };
    }
}
           

限制

WebMvc Observability 的 Spring Boot AutoConfiguration 尚未完成。是以,我們需要手動設定。有關詳細資訊,請參閱此 issue。 為了讓 Spring Boot Exemplars AutoConfiguration 正常工作,我們需要等待此 PR 和此 PR 合并。在此之前,我們需要手動建立配置。

2.5 運作

我們已經準備好了這個連結下整個可觀測性基礎設施的Docker設定。按照以下步驟運作基礎架構和兩個應用程式。

運作示例

使用 docker-compose 啟動他們

$ docker-compose up
1. Prometheus 預設位址:[http://localhost:9090/](http://localhost:9090/)
2. Grafana預設位址:[http://localhost:3000/](http://localhost:3000/)
           

運作伺服器端應用程式。

$ ./mvnw spring-boot:run -pl :server
           

運作用戶端應用程式。

$ ./mvnw spring-boot:run -pl :client

You should see log statements similar to these: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 儀表盤。在這裡,您可以選擇一個 trace ID(例如 bbe3aea006077640b66d40f3e62f04b9),以從兩個應用程式中查找與該 trace ID 對應的所有 logs 和 traces 資訊。應該會看到以下與同一 trace ID 相關的日志和traces 相關視圖,你将看到在同一時間範圍内發生的 metrics。這些名額與 HTTP 請求處理延遲有關。這些來自使用 Micrometer API 的自動收集的 Spring Boot WebMvc 資訊。

Spring Boot 3.0 要來了,這個特性真心強
Spring Boot 3.0 要來了,這個特性真心強
  1. 注意 metrics 中的圖性,這些是Exemplars 。這些是“特定軌迹,代表在給定時間間隔内進行的測量”。如果單擊形狀,可以跳到 trace ID 視圖檢視對應該的 trace.
  2. 單擊 trace ID 用 Tempo 查詢 要麼跳轉到自己選擇的 trace ID。您将看到以下螢幕。 每個條形代表一個span 。我們可以看到完成每個操作所需的時間。如果單擊給定的範圍,我們可以看到與該特定操作相關的 tags(鍵值中繼資料)和計時資訊。
Spring Boot 3.0 要來了,這個特性真心強

!

這就是相關日志視圖在 Loki 中的效果圖:

Spring Boot 3.0 要來了,這個特性真心強

如果想檢視@Observed 注解的方法 metrics,可以轉到 Prometheus 視圖并找到user_name Timer。 如果想檢視手動建立的 Observation metrics,請轉到 Prometheus 視圖并找到my_observation Timer。

2.6 與 AOT 支援一起運作

為了更好地了解 Spring Boot 如何支援 Native,請閱讀這篇優秀的部落格文章。

建構

要建構應用程式,需要配置 GraalVM 到 path。如果使用 SDKMan 調用如下:

sdk install java 22.2.r17-nik
           

檢視 GraalVM 快速使用。 使用 Maven 建構,你需要啟用 native 環境:

$ ./mvnw -Pnative clean package
           

運作

先運作服務端。

$ ./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 的 metrics 和 traces。閱讀 Native 支援限制 章節檢視為什麼不能将日志推送到 Loki。

Native 支援限制

日志還不會被推給 Loki。更多資訊請檢視:issue 25847。 用戶端還需要手動配置 reflect-config.js 。更多資訊,請參閱此 PR。

三、總結

在這篇博文中,我們成功地向您介紹了微米可觀察性 API 背後的主要概念。我們還向您展示了如何使用 observe API 和 annotations 自定義可觀察。您還可以可視化延遲,檢視相關日志,并檢查來自 Spring Boot 應用程式的名額。 當然還可以通過使用 Spring native 中的 native images 來觀察應用程式。

繼續閱讀