1. 概述

本文将带你掌握 Spring Boot 3 的可观测性配置。可观测性是指仅通过系统的外部输出(日志、指标和链路追踪)来衡量其内部状态的能力。基础概念可参考《分布式系统可观测性》一文。

⚠️ 需特别注意:Spring Boot 2 (Spring 5) 和 Spring Boot 3 (Spring 6) 之间存在重大变化。Spring 6 推出了 Spring Observability 新方案,它基于 Micrometer 和 Micrometer Tracing(原 Spring Cloud Sleuth)构建。该方案能更高效地记录应用指标,并通过 OpenZipkin Brave 或 OpenTelemetry 等提供商实现链路追踪。Spring Observability 相比基于代理的可观测性方案优势明显:它能在原生编译的 Spring 应用中无缝运行,并提供更有效的信息。

本文仅聚焦 Spring Boot 3 的实现细节。如需从 Spring Boot 2 迁移,可参考官方迁移指南

2. Micrometer Observation API

Micrometer 是一个提供厂商中立的应用指标门面的项目。它定义了仪表、速率聚合、计数器、仪表盘和计时器等概念,各厂商可将其适配到自己的工具中。核心组件是Observation API,支持一次编写代码即可实现多种功能。

该 API 已集成到 Spring Framework 的多个组件中,因此理解它是掌握 Spring Boot 可观测性的关键。我们通过一个简单示例来学习。

2.1. Observation 和 ObservationRegistry

根据词典定义,观察是"为科学或其他特殊目的查看或记录事实或事件的行为"。在代码中,我们可以观察单个操作或完整的 HTTP 请求处理过程。在这些观察中,我们可以进行测量、创建分布式追踪的 span 或记录额外信息。

创建观察需要 ObservationRegistry

ObservationRegistry observationRegistry = ObservationRegistry.create();
Observation observation = Observation.createNotStarted("sample", observationRegistry);

观察的生命周期如下图所示:

Observation 状态图

使用 Observation 的方式如下:

observation.start();
try (Observation.Scope scope = observation.openScope()) {
    // ... 被观察的操作
} catch (Exception e) {
    observation.error(e);
    // 异常处理
} finally {
    observation.stop();
}

或更简洁的方式:

observation.observe(() -> {
    // ... 被观察的操作
});

2.2. ObservationHandler

数据收集代码通过 ObservationHandler 实现。该处理器监听 Observation 的生命周期事件,提供回调方法。一个简单的事件打印处理器实现如下:

public class SimpleLoggingHandler implements ObservationHandler<Observation.Context> {

    private static final Logger log = LoggerFactory.getLogger(SimpleLoggingHandler.class);

    @Override
    public boolean supportsContext(Observation.Context context) {
        return true;
    }

    @Override
    public void onStart(Observation.Context context) {
        log.info("Starting");
    }

    @Override
    public void onScopeOpened(Observation.Context context) {
        log.info("Scope opened");
    }

    @Override
    public void onScopeClosed(Observation.Context context) {
        log.info("Scope closed");
    }

    @Override
    public void onStop(Observation.Context context) {
        log.info("Stopping");
    }

    @Override
    public void onError(Observation.Context context) {
        log.info("Error");
    }
}

在创建 Observation 前,需将 ObservationHandler 注册到 ObservationRegistry

observationRegistry
  .observationConfig()
  .observationHandler(new SimpleLoggingHandler());

对于简单日志记录,已有现成实现。例如直接输出到控制台:

observationRegistry
  .observationConfig()
  .observationHandler(new ObservationTextPublisher(System.out::println));

要使用计时器样本和计数器,可这样配置:

MeterRegistry meterRegistry = new SimpleMeterRegistry();
observationRegistry
  .observationConfig()
  .observationHandler(new DefaultMeterObservationHandler(meterRegistry));

// ... 使用名为 "sample" 的 Observation

// 获取命名观察的最大持续时间
Optional<Double> maximumDuration = meterRegistry.getMeters().stream()
  .filter(m -> "sample".equals(m.getId().getName()))
  .flatMap(m -> StreamSupport.stream(m.measure().spliterator(), false))
  .filter(ms -> ms.getStatistic() == Statistic.MAX)
  .findFirst()
  .map(Measurement::getValue);

3. Spring 集成

3.1. Actuator

在 Spring Boot 应用中,通过 Actuator 依赖可获得最佳集成:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

它包含 ObservationAutoConfiguration,提供可注入的 ObservationRegistry 实例(如果不存在),并配置用于收集指标和链路的 ObservationHandler

例如,在服务中创建自定义观察:

@Service
public class GreetingService {

    private ObservationRegistry observationRegistry;

    // 构造函数

    public String sayHello() {
        return Observation
          .createNotStarted("greetingService", observationRegistry)
          .observe(this::sayHelloNoObserver);
    }

    private String sayHelloNoObserver() {
        return "Hello World!";
    }
}

此外,它会将 ObservationHandler Bean 注册到 ObservationRegistry。我们只需提供 Bean:

@Configuration
public class ObservationTextPublisherConfiguration {

    private static final Logger log = LoggerFactory.getLogger(ObservationTextPublisherConfiguration.class);

    @Bean
    public ObservationHandler<Observation.Context> observationTextPublisher() {
        return new ObservationTextPublisher(log::info);
    }
}

3.2. Web

MVC 提供了过滤器 org.springframework.web.filter.ServerHttpObservationFilter 用于 HTTP 服务端观察。当应用包含 Actuator 时,该过滤器已自动注册。否则需手动配置:

@Configuration
public class ObservationFilterConfiguration {

    // 如果已配置 ObservationRegistry
    @ConditionalOnBean(ObservationRegistry.class)
    // 如果未使用 Actuator
    @ConditionalOnMissingBean(ServerHttpObservationFilter.class)
    @Bean
    public ServerHttpObservationFilter observationFilter(ObservationRegistry registry) {
        return new ServerHttpObservationFilter(registry);
    }
}

WebFlux 也有类似过滤器 org.springframework.web.filter.reactive.ServerHttpObservationFilter,但自 Spring Framework 6.1 (Spring Boot 3.2) 起已弃用。取而代之的是创建 WebHttpHandlerBuilder。使用 Actuator 时,这也会自动配置

3.3. AOP

Micrometer Observation API 还提供了基于 AspectJ 的 @Observed 注解和切面实现。要启用此功能,需添加 AOP 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

然后将切面实现注册为 Spring 管理的 Bean:

@Configuration
public class ObservedAspectConfiguration {

    @Bean
    public ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
        return new ObservedAspect(observationRegistry);
    }
}

现在,GreetingService 可简化为:

@Observed(name = "greetingService")
@Service
public class GreetingService {

    public String sayHello() {
        return "Hello World!";
    }
}

结合 Actuator,调用服务后可通过 http://localhost:8080/actuator/metrics/greetingService 获取预配置指标:

{
    "name": "greetingService",
    "baseUnit": "seconds",
    "measurements": [
        {
            "statistic": "COUNT",
            "value": 15
        },
        {
            "statistic": "TOTAL_TIME",
            "value": 0.0237577
        },
        {
            "statistic": "MAX",
            "value": 0.0035475
        }
    ],
    ...
}

4. 测试观察

Micrometer Observability API 提供了测试模块。添加以下依赖:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-observation-test</artifactId>
    <scope>test</scope>
</dependency>

micrometer-bom 已包含在 Spring Boot 管理的依赖中,无需指定版本。

默认情况下测试会禁用整个可观测性自动配置,需使用 @AutoConfigureObservability 重新启用。

4.1. TestObservationRegistry

使用 TestObservationRegistry 进行基于 AssertJ 的断言。需将上下文中的 ObservationRegistry 替换为 TestObservationRegistry 实例。

测试 GreetingService 观察的设置示例:

@ExtendWith(SpringExtension.class)
@ComponentScan(basePackageClasses = GreetingService.class)
@EnableAutoConfiguration
@Import(ObservedAspectConfiguration.class)
@AutoConfigureObservability
class GreetingServiceObservationIntegrationTest {

    @Autowired
    GreetingService service;
    @Autowired
    TestObservationRegistry registry;

    @TestConfiguration
    static class ObservationTestConfiguration {

        @Bean
        TestObservationRegistry observationRegistry() {
            return TestObservationRegistry.create();
        }
    }

    // ...
}

也可使用 JUnit 元注解配置:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({
  ObservedAspectConfiguration.class,
  EnableTestObservation.ObservationTestConfiguration.class
})
@AutoConfigureObservability
public @interface EnableTestObservation {

    @TestConfiguration
    class ObservationTestConfiguration {

        @Bean
        TestObservationRegistry observationRegistry() {
            return TestObservationRegistry.create();
        }
    }
}

在测试类上添加注解:

@ExtendWith(SpringExtension.class)
@ComponentScan(basePackageClasses = GreetingService.class)
@EnableAutoConfiguration
@EnableTestObservation
class GreetingServiceObservationIntegrationTest {

    @Autowired
    GreetingService service;
    @Autowired
    TestObservationRegistry registry;

    // ...
}

然后调用服务并验证观察:

import static io.micrometer.observation.tck.TestObservationRegistryAssert.assertThat;

// ...

@Test
void testObservation() {
    // 调用服务
    service.sayHello();
    assertThat(registry)
      .hasObservationWithNameEqualTo("greetingService")
      .that()
      .hasBeenStarted()
      .hasBeenStopped();
}

4.2. 观察处理器兼容性套件

测试 ObservationHandler 实现时,可继承以下基类(兼容性套件):

  • NullContextObservationHandlerCompatibilityKit:测试处理器在参数为 null 时的正确性
  • AnyContextObservationHandlerCompatibilityKit:测试处理器在未指定测试上下文参数时的正确性(包含 NullContextObservationHandlerCompatibilityKit
  • ConcreteContextObservationHandlerCompatibilityKit:测试处理器在特定上下文类型下的正确性

实现示例:

public class SimpleLoggingHandlerUnitTest
  extends AnyContextObservationHandlerCompatibilityKit {

    SimpleLoggingHandler handler = new SimpleLoggingHandler();

    @Override
    public ObservationHandler<Observation.Context> handler() {
        return handler;
    }
}

测试结果输出:

兼容性套件测试结果

5. Micrometer Tracing

原 Spring Cloud Sleuth 项目已迁移到 Micrometer,核心变为 Micrometer Tracing(Spring Boot 3 起)。其文档定义如下:

Micrometer Tracing 为流行的追踪库提供简单门面,让你无需绑定厂商即可为 JVM 应用代码添加检测。它旨在为追踪收集活动增加极低开销,同时最大化追踪工作的可移植性。

可独立使用,也通过 ObservationHandler 扩展与 Observation API 集成。

5.1. 集成到 Observation API

添加以下依赖(版本由 Spring Boot 管理):

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing</artifactId>
</dependency>

然后选择支持的追踪器(当前支持 OpenZipkin BraveOpenTelemetry),添加厂商特定集成依赖:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>

Spring Actuator 对两种追踪器都提供自动配置,即注册厂商特定对象和 Micrometer Tracing 的 ObservationHandler 实现,无需额外配置。

5.2. 测试支持

添加测试依赖(版本由 Spring Boot 管理):

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-test</artifactId>
    <scope>test</scope>
</dependency>

使用 SimpleTracer 类在测试中收集和验证追踪数据。需将应用上下文中的厂商特定 Tracer 替换为 SimpleTracer,并使用 @AutoConfigureObservability 启用追踪自动配置。

最小追踪测试配置:

@ExtendWith(SpringExtension.class)
@EnableAutoConfiguration
@AutoConfigureObservability
public class GreetingServiceTracingIntegrationTest {

    @TestConfiguration
    static class ObservationTestConfiguration {
        @Bean
        TestObservationRegistry observationRegistry() {
            return TestObservationRegistry.create();
        }
        @Bean
        SimpleTracer simpleTracer() {
            return new SimpleTracer();
        }
    }

    @Test
    void shouldTrace() {
        // 测试代码
    }
}

或使用 JUnit 元注解:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@AutoConfigureObservability
@Import({
  ObservedAspectConfiguration.class,
  EnableTestObservation.ObservationTestConfiguration.class
})
public @interface EnableTestObservation {

    @TestConfiguration
    class ObservationTestConfiguration {

        @Bean
        TestObservationRegistry observationRegistry() {
            return TestObservationRegistry.create();
        }

        @Bean
        SimpleTracer simpleTracer() {
            return new SimpleTracer();
        }
    }
}

测试 GreetingService 的示例:

import static io.micrometer.tracing.test.simple.TracerAssert.assertThat;

// ...

@Autowired
GreetingService service;
@Autowired
SimpleTracer tracer;

// ...

@Test
void testTracingForGreeting() {
    service.sayHello();
    assertThat(tracer)
      .onlySpan()
      .hasNameEqualTo("greeting-service#say-hello")
      .isEnded();
}

6. 总结

本文探索了 Micrometer Observation API 及其在 Spring Boot 3 中的集成。我们了解到:

  • ✅ Micrometer 是厂商无关的检测 API
  • ✅ Micrometer Tracing 是其扩展
  • ✅ Actuator 提供预配置的观察和追踪
  • ⚠️ 测试默认禁用可观测性自动配置

所有代码实现可在 GitHub 获取。


原始标题:Observability With Spring Boot 3