Why Logs Alone Fail in Spring Boot: Achieving True Observability

The article explains that relying solely on log statements in Spring Boot applications cannot reveal request identities, latency, async task health, failure details, or cross‑service flows, and demonstrates how to augment logs with MDC correlation IDs, Micrometer metrics, and Zipkin tracing for comprehensive observability.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Why Logs Alone Fail in Spring Boot: Achieving True Observability

In everyday Spring Boot development and operations, developers often over‑rely on simple log statements such as log.info("处理完成") to assume normal behavior, but logs cannot answer critical questions about which user made the request, how long it took, or whether it was stuck in a queue.

1. Introduction

True observability requires combining unique trace identifiers (MDC), metric monitoring, and distributed tracing; logs are only one piece of the puzzle.

2. Practical Cases

2.1 Logs Miss Request Flow

Typical log output: log.info("create order..."); This provides no insight into which request, user, service, or retry generated the log. To solve this, a correlation ID is added to each request:

@Component
public class CorrelationIdFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        String correlationId = UUID.randomUUID().toString();
        MDC.put("CID", correlationId);
        try {
            filterChain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}

The logging pattern is then configured to include the CID:

<property name="TRACEX_PATTERN" value="%green(%d{HH:mm:ss}) traceId:[%red(%X{CID})] %X{req.remoteHost} %highlight(%-5level) [%yellow(%thread)] %logger Line:%-3L - %msg%n" />
<appender name="TRACEX" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
    <pattern>${TRACEX_PATTERN}</pattern>
  </encoder>
</appender>

Resulting log entries now contain the unique CID, making request tracing possible.

2.2 Logs Miss Latency Information

Typical code logs start and end of a payment operation but does not show duration:

log.info("开始支付...");
paymentService.pay();
log.info("支付完成...");

To capture latency, Micrometer metrics are used:

private final MeterRegistry meterRegistry;
public PaymentService(MeterRegistry meterRegistry) {
    this.meterRegistry = meterRegistry;
}
public void pay() throws Exception {
    Timer.Sample sample = Timer.start(meterRegistry);
    // simulate payment delay
    TimeUnit.MILLISECONDS.sleep(new Random().nextInt(2000));
    sample.stop(Timer.builder("payment.pay.time")
        .tag("order", "product")
        .register(meterRegistry));
}

The metrics can be inspected via Spring Boot Actuator endpoints, revealing actual execution times.

2.3 Async Tasks Lack Visibility

Using @Async without metrics hides queue length, thread blockage, and stagnation:

@Async
public void task() {
    // ...
}

Expose thread‑pool metrics with Micrometer:

@Bean
ThreadPoolTaskExecutor taskExecutor(MeterRegistry registry) {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(10);
    executor.setQueueCapacity(100);
    executor.setThreadNamePrefix("pack-asyn-");
    executor.initialize();
    ExecutorServiceMetrics.monitor(registry, executor.getThreadPoolExecutor(), "pack.async.executor");
    return executor;
}

Actuator now shows thread‑pool statistics, helping detect saturation.

2.4 Logs Miss Failure Details

When an exception occurs during order creation, a simple error log does not reveal whether the database commit succeeded or if a retry happened:

try {
    repository.save(order);
    externalService.notify(order);
} catch (Exception e) {
    log.error("Failed", e);
}

Using transactional event hooks makes the process observable and predictable:

private final OrderRepository orderRepository;
@Transactional
public void createOrder(Order order) {
    orderRepository.save(order);
    eventPublisher.publishEvent(new OrderCreatedEvent(order.getId()));
}

@TransactionalEventListener(phase = AFTER_COMMIT)
public void handle(OrderCreatedEvent event) {
    notifyExternal(event.getOrderId());
}

2.5 Logs Break Across Services

In a microservice setup, logs at service boundaries cannot confirm whether the same request traversed both services or how long each segment took.

Distributed tracing with Zipkin solves this:

@GetMapping("/orders")
public ResponseEntity<Object> create() {
    this.orderService.process();
    return ResponseEntity.ok("success");
}

@Service
public class OrderService {
    private final Tracer tracer;
    private final RestTemplate restTemplate;
    public OrderService(Tracer tracer, RestTemplate restTemplate) {
        this.tracer = tracer;
        this.restTemplate = restTemplate;
    }
    public void process() {
        Span span = tracer.nextSpan().name("create-order").start();
        try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
            this.callService();
        } finally {
            span.end();
        }
    }
    private void callService() {
        try {
            String ret = this.restTemplate.getForObject("http://localhost:8080/payment", String.class);
            System.err.println("调动结果: %s".formatted(ret));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Zipkin configuration:

management:
  zipkin:
    tracing:
      endpoint: http://localhost:9411/api/v2/spans

Required Maven dependencies:

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

By integrating MDC, Micrometer metrics, and Zipkin tracing, the application gains full observability beyond what plain logs can provide.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ObservabilityMetricsloggingtracingzipkinMicrometerspring-boot
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.