Observabilidade Java — Spring Boot
Os Três Pilares
| Pilar |
Ferramenta Spring |
Pergunta |
| Logs |
Logback + Logstash encoder |
O que aconteceu? |
| Métricas |
Micrometer + Actuator |
Quantas vezes? Quanto tempo? |
| Traces |
OpenTelemetry Java Agent |
Por onde passou? |
Spring Boot Actuator
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: when_authorized
probes:
enabled: true # /health/liveness e /health/readiness para K8s
metrics:
export:
prometheus:
enabled: true
// Health check customizado
@Component
public class PaymentProviderHealthIndicator implements HealthIndicator {
private final PaymentClient client;
@Override
public Health health() {
try {
client.ping();
return Health.up().withDetail("provider", "stripe").build();
} catch (Exception e) {
return Health.down(e).withDetail("provider", "stripe").build();
}
}
}
Micrometer — Métricas
@Service
@RequiredArgsConstructor
public class CreateOrderService {
private final MeterRegistry registry;
private final OrderRepository orderRepository;
@Transactional
public OrderResponse execute(CreateOrderCommand cmd) {
return Timer.builder("order.create.duration")
.description("Time to create an order")
.tag("status", "success")
.register(registry)
.record(() -> {
var order = Order.create(cmd.customerId(), cmd.items());
orderRepository.save(order);
registry.counter("order.created", "currency", order.currency().name()).increment();
return OrderResponse.from(order);
});
}
}
Métricas obrigatórias em produção:
http.server.requests (latência por endpoint)
- Métricas de negócio:
order.created, payment.failed
- JVM: heap, GC (via Actuator/Prometheus)
Logging Estruturado (Logback JSON)
<!-- logback-spring.xml -->
<configuration>
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>trace_id</includeMdcKeyName>
<includeMdcKeyName>request_id</includeMdcKeyName>
<includeMdcKeyName>user_id</includeMdcKeyName>
</encoder>
</appender>
<root level="INFO"><appender-ref ref="JSON"/></root>
</configuration>
// Filtro MDC para contexto de requisição
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestContextFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws ServletException, IOException {
var requestId = Optional.ofNullable(req.getHeader("X-Request-ID"))
.orElse(UUID.randomUUID().toString());
MDC.put("request_id", requestId);
MDC.put("trace_id", Optional.ofNullable(req.getHeader("traceparent"))
.orElse(requestId));
try {
chain.doFilter(req, res);
res.setHeader("X-Request-ID", requestId);
} finally {
MDC.clear();
}
}
}
// Uso
@Slf4j
@Service
public class CreateOrderService {
public OrderResponse execute(CreateOrderCommand cmd) {
log.info("order_created customer_id={} items_count={}",
cmd.customerId(), cmd.items().size());
// ...
}
}
OpenTelemetry
# application.yml com Java Agent
# java -javaagent:opentelemetry-javaagent.jar -jar app.jar
management:
otlp:
tracing:
endpoint: http://otel-collector:4318/v1/traces
tracing:
sampling:
probability: 1.0 # reduzir em prod (ex: 0.1)
// Span customizado
@Service
@RequiredArgsConstructor
public class CreateOrderService {
private final Tracer tracer;
public OrderResponse execute(CreateOrderCommand cmd) {
var span = tracer.spanBuilder("create_order")
.setAttribute("order.customer_id", cmd.customerId().toString())
.startSpan();
try (var scope = span.makeCurrent()) {
// lógica
span.setStatus(StatusCode.OK);
return result;
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR, e.getMessage());
throw e;
} finally {
span.end();
}
}
}
Convenções de Log
| Evento |
Nível |
Campos obrigatórios |
| Request recebido |
DEBUG |
request_id, method, path |
| Operação de negócio |
INFO |
event_name, entity_id |
| Erro de negócio |
WARN |
event_name, reason, entity_id |
| Erro inesperado |
ERROR |
event_name, stack trace |
| Sistema crítico |
ERROR/CRITICAL |
component |
O que NUNCA Logar
// ❌ PII
log.info("User login email={}", user.getEmail());
log.info("Payment card={}", cardNumber);
// ✅ IDs opacos
log.info("user_login user_id={}", user.getId());
log.info("payment_processed payment_id={}", payment.getId());
Stack Recomendada
Coleta → OpenTelemetry Collector
Logs → Loki / CloudWatch / Datadog
Métricas → Prometheus + Grafana (via Actuator /actuator/prometheus)
Traces → Jaeger / Tempo
Alertas → Alertmanager / PagerDuty
Equivalência Python (SKILL_5)
| Python |
Java |
| structlog |
Logback + LogstashEncoder |
| prometheus_client |
Micrometer |
| opentelemetry-sdk |
OTEL Java Agent |
| FastAPI middleware |
Servlet Filter + MDC |