Skip to content

Spring Boot Observability:现代应用监控的三大支柱 🔍

什么是 Observability?为什么它如此重要?

想象一下,你正在运营一个在线购物网站。突然,用户开始抱怨页面加载缓慢,订单提交失败。作为开发者,你需要快速定位问题:

  • 是数据库查询太慢?
  • 是某个微服务出现了故障?
  • 还是网络连接有问题?

这就是 Observability(可观测性) 要解决的核心问题:让我们能够从外部观察运行中系统的内部状态

IMPORTANT

Observability 由三大支柱构成:

  • Logging(日志):记录系统发生了什么
  • Metrics(指标):量化系统的性能表现
  • Traces(链路追踪):追踪请求在系统中的完整流程

Spring Boot 中的 Observability 架构

Spring Boot 使用 Micrometer Observation 来统一处理指标和链路追踪,这是一个革命性的设计理念:

TIP

一次观测,多重收益!通过 Micrometer Observation,你只需要编写一次观测代码,就能同时获得指标和链路追踪数据。

创建自定义观测:从零到一的实践

让我们通过一个实际的电商订单处理场景来学习如何使用 Observability:

kotlin
@Service
class OrderService {
    
    fun processOrder(orderId: String): Order {
        // 记录日志
        logger.info("开始处理订单: $orderId")
        
        // 记录指标
        orderCounter.increment()
        val timer = Timer.start()
        
        try {
            // 业务逻辑
            val order = validateOrder(orderId)
            val payment = processPayment(order)
            val shipment = createShipment(order)
            
            logger.info("订单处理成功: $orderId")
            return order
        } catch (e: Exception) {
            logger.error("订单处理失败: $orderId", e)
            errorCounter.increment()
            throw e
        } finally {
            timer.stop(orderProcessingTimer)
        }
    }
}
kotlin
@Service
class OrderService(
    private val observationRegistry: ObservationRegistry
) {
    
    fun processOrder(orderId: String): Order {
        return Observation.createNotStarted("order.process", observationRegistry) 
            .lowCardinalityKeyValue("order.type", "standard")  // 用于指标和链路追踪
            .highCardinalityKeyValue("order.id", orderId)      // 仅用于链路追踪
            .observe { 
                // 业务逻辑 - 清晰分离
                val order = validateOrder(orderId)
                val payment = processPayment(order)
                val shipment = createShipment(order)
                order
            } 
    }
}

NOTE

Low Cardinality vs High Cardinality 标签的区别:

  • Low Cardinality:值的种类有限(如订单类型:standard、premium、vip),会同时添加到指标和链路追踪中
  • High Cardinality:值的种类很多(如用户ID、订单ID),仅添加到链路追踪中,避免指标维度爆炸

上下文传播:跨线程和响应式流的观测

在现代应用中,一个请求可能会跨越多个线程和响应式流。Spring Boot 提供了强大的上下文传播机制:

kotlin
@Configuration
class ObservabilityConfig {
    
    @Bean
    fun reactorContextPropagation(): ReactorContextPropagation {
        // 启用响应式流中的上下文传播
        return ReactorContextPropagation()
    }
}

@Service
class AsyncOrderService(
    private val observationRegistry: ObservationRegistry
) {
    
    @Async
    fun processOrderAsync(orderId: String): CompletableFuture<Order> {
        // 观测上下文会自动传播到异步线程中
        return Observation.createNotStarted("order.async.process", observationRegistry)
            .observe {
                // 异步业务逻辑
                processComplexOrder(orderId)
            }
    }
    
    fun processOrderReactive(orderId: String): Mono<Order> {
        return Observation.createNotStarted("order.reactive.process", observationRegistry)
            .observe {
                // 响应式业务逻辑
                Mono.fromCallable { processComplexOrder(orderId) }
                    .subscribeOn(Schedulers.boundedElastic())
            }
    }
}

启用自动上下文传播

application.yml 中设置:

yaml
spring:
  reactor:
    context-propagation: auto  # 启用响应式流中的自动上下文传播

通用标签:为所有观测添加环境信息

通用标签让你能够按照环境维度对所有观测数据进行分析:

properties
# 为所有观测添加通用标签
management.observations.key-values.region=us-east-1
management.observations.key-values.stack=prod
management.observations.key-values.service=order-service
management.observations.key-values.version=1.2.3
yaml
management:
  observations:
    key-values:
      region: "us-east-1"      # 部署区域
      stack: "prod"            # 环境标识
      service: "order-service" # 服务名称
      version: "1.2.3"         # 服务版本

这样配置后,所有的观测数据都会自动包含这些标签,便于在监控系统中进行多维度分析。

观测控制:精确管理你想要的数据

1. 通过配置禁用特定观测

有时候某些观测可能产生过多噪音,你可以选择性地禁用它们:

properties
# 禁用特定前缀的观测
management.observations.enable.http.client=false
management.observations.enable.spring.security=false
management.observations.enable.jdbc=false
yaml
management:
  observations:
    enable:
      http:
        client: false      # 禁用 HTTP 客户端观测
      spring:
        security: false    # 禁用 Spring Security 观测
      jdbc: false          # 禁用 JDBC 观测

2. 通过代码实现精细控制

对于更复杂的控制逻辑,你可以实现 ObservationPredicate

kotlin
@Component
class CustomObservationPredicate : ObservationPredicate {
    
    override fun test(name: String, context: Observation.Context): Boolean {
        // 禁用健康检查相关的观测
        if (name.contains("health")) {
            return false
        }
        
        // 只在生产环境启用详细的数据库观测
        if (name.startsWith("jdbc") && !isProductionEnvironment()) {
            return false
        }
        
        // 根据请求路径过滤
        if (context is ServerRequestObservationContext) {
            val uri = context.carrier?.requestURI
            return !uri?.startsWith("/internal/")
        }
        
        return true
    }
    
    private fun isProductionEnvironment(): Boolean {
        // 检查环境逻辑
        return System.getProperty("spring.profiles.active")?.contains("prod") == true
    }
}

WARNING

观测控制要谨慎使用!过度禁用观测可能会让你在关键时刻失去重要的监控数据。

注解驱动的观测:让监控更简单

Spring Boot 支持通过注解来启用观测,让代码更加简洁:

kotlin
# 启用注解支持
management:
  observations:
    annotations:
      enabled: true
kotlin
@Service
class ProductService {
    
    @Observed(
        name = "product.search",
        contextualName = "product-search-by-category",
        lowCardinalityKeyValues = ["operation", "search"]
    )
    fun searchProducts(
        @MeterTag("category") category: String,  // 自动添加为标签
        @MeterTag("limit") limit: Int
    ): List<Product> {
        // 业务逻辑 - 自动被观测
        return productRepository.findByCategory(category, limit)
    }
    
    @Timed(name = "product.creation.time", description = "产品创建耗时")
    @Counted(name = "product.creation.count", description = "产品创建次数")
    fun createProduct(product: Product): Product {
        // 同时记录耗时和计数
        return productRepository.save(product)
    }
}

CAUTION

避免重复观测!如果你的方法已经被 Spring 自动观测(如 Spring MVC 控制器、Spring Data 仓库),再添加注解会产生重复的观测数据。

OpenTelemetry 集成:标准化的可观测性

Spring Boot 提供了对 OpenTelemetry 的原生支持:

kotlin
@Configuration
class OpenTelemetryConfig {
    
    @Bean
    fun openTelemetryResource(): Resource {
        return Resource.getDefault()
            .merge(Resource.builder()
                .put(ResourceAttributes.SERVICE_NAME, "order-service")
                .put(ResourceAttributes.SERVICE_VERSION, "1.0.0")
                .put(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "production")
                .build())
    }
    
    @Bean
    fun sdkTracerProvider(): SdkTracerProvider {
        return SdkTracerProvider.builder()
            .addSpanProcessor(BatchSpanProcessor.builder(
                OtlpGrpcSpanExporter.builder()
                    .setEndpoint("http://jaeger:14250")
                    .build()
            ).build())
            .setResource(openTelemetryResource())
            .build()
    }
}
OpenTelemetry 配置示例
yaml
management:
  opentelemetry:
    resource-attributes:
      service.name: "order-service"
      service.version: "1.0.0"
      deployment.environment: "production"
      team: "backend"

实战场景:电商系统的完整观测方案

让我们通过一个完整的电商订单处理流程来展示 Observability 的威力:

kotlin
@RestController
@RequestMapping("/api/orders")
class OrderController(
    private val orderService: OrderService,
    private val observationRegistry: ObservationRegistry
) {
    
    @PostMapping
    fun createOrder(@RequestBody orderRequest: OrderRequest): ResponseEntity<Order> {
        return Observation.createNotStarted("order.api.create", observationRegistry)
            .lowCardinalityKeyValue("order.type", orderRequest.type)
            .lowCardinalityKeyValue("payment.method", orderRequest.paymentMethod)
            .highCardinalityKeyValue("user.id", orderRequest.userId.toString())
            .observe {
                val order = orderService.processOrder(orderRequest)
                ResponseEntity.ok(order)
            }
    }
}

@Service
class OrderService(
    private val paymentService: PaymentService,
    private val inventoryService: InventoryService,
    private val observationRegistry: ObservationRegistry
) {
    
    fun processOrder(orderRequest: OrderRequest): Order {
        return Observation.createNotStarted("order.service.process", observationRegistry)
            .observe {
                // 1. 验证库存
                val inventoryCheck = Observation.createNotStarted("inventory.check", observationRegistry)
                    .observe { inventoryService.checkAvailability(orderRequest.items) }
                
                if (!inventoryCheck.available) {
                    throw InsufficientInventoryException("库存不足")
                }
                
                // 2. 处理支付
                val payment = Observation.createNotStarted("payment.process", observationRegistry)
                    .lowCardinalityKeyValue("payment.method", orderRequest.paymentMethod)
                    .observe { paymentService.processPayment(orderRequest.payment) }
                
                // 3. 创建订单
                val order = Order(
                    id = generateOrderId(),
                    items = orderRequest.items,
                    payment = payment,
                    status = OrderStatus.CONFIRMED
                )
                
                // 4. 扣减库存
                Observation.createNotStarted("inventory.reserve", observationRegistry)
                    .observe { inventoryService.reserveItems(orderRequest.items) }
                
                order
            }
    }
}

通过这种方式,你可以在监控系统中看到:

  • 📊 指标数据:订单创建成功率、平均处理时间、不同支付方式的分布
  • 🔍 链路追踪:完整的请求流程,包括每个步骤的耗时和依赖关系
  • 📝 结构化日志:与观测上下文关联的日志信息

最佳实践总结

观测命名规范

  • 使用点号分隔的层次结构:service.operation.action
  • 保持名称简洁且有意义:order.payment.process 而不是 processOrderPayment
  • 使用一致的命名约定

标签使用策略

  • Low Cardinality:环境、服务类型、操作类型等固定值
  • High Cardinality:用户ID、订单ID、会话ID等唯一值
  • 避免在标签中使用时间戳或随机值

性能考虑

  • 观测本身也有性能开销,在高并发场景下要合理控制观测的粒度
  • 使用采样策略减少链路追踪的数据量
  • 定期清理不再需要的观测点

总结

Spring Boot 的 Observability 为我们提供了一套完整、现代化的应用监控解决方案。通过统一的 API,我们可以:

简化监控代码:一次编写,多重收益
提升可维护性:清晰分离业务逻辑和监控逻辑
增强问题定位能力:完整的链路追踪和丰富的指标数据
支持现代架构:异步、响应式、微服务友好

在微服务和云原生时代,Observability 不再是可选项,而是必需品。掌握 Spring Boot 的 Observability 特性,让你的应用在复杂的生产环境中始终保持透明和可控! 🚀