Observabilidade

Três pilares implementados — métricas, logs estruturados e tracing distribuído.

Fluxo de observabilidade completo

  Request
    │
    ▼
  API (Spring Boot :8080)
    │
    ├── Métricas  →  /actuator/prometheus  →  Prometheus (:9090)  ✓ v2.1.0
    │                                              ↓
    │                                          Grafana (:3000)     ✓ v2.6.0
    │                 9 painéis: req/s · erros · cache ratio · Whisper p99
    │                            CB state · retries · latência · throughput
    │
    ├── Logs JSON  →  stdout  →  ingestão (Loki/ELK/qualquer)     ✓ v2.2.0
    │               Campos: requestId · fileName · fileSizeBytes
    │                        whisperModel · traceId · spanId
    │
    └── Traces  →  Zipkin (:9411)                                  ✓ v2.7.0
                   Spans automáticos: HTTP recebido + WebClient→Speaches

Métricas v2.1.0 + v2.5.0 + v3.3.0

Métricas de negócio (TranscriptionMetrics)

MétricaTipoTagsDescrição
transcription.requests.totalCounterstatus=success|errorTotal de transcrições
transcription.whisper.durationTimerLatência Whisper (p50/p95/p99 + histograma)
transcription.file.size.bytesDistributionDistribuição dos tamanhos de arquivo
transcription.cache.totalCounterresult=hit|missCache hits e misses

Métricas de resiliência (Resilience4j → Micrometer automático)

MétricaDescrição
resilience4j_circuitbreaker_stateEstado: 0=CLOSED · 1=OPEN · 2=HALF_OPEN
resilience4j_circuitbreaker_calls_seconds_countChamadas por tipo: successful/failed/not_permitted
resilience4j_retry_calls_totalRetries: successful_with_retry / failed_with_retry

Queries Prometheus úteis

# Taxa de sucesso por minuto
rate(transcription_requests_total{status="success"}[1m]) * 60

# p99 de latência do Whisper
histogram_quantile(0.99, rate(transcription_whisper_duration_seconds_bucket[5m]))

# Cache hit ratio
rate(transcription_cache_total{result="hit"}[5m])
  / (rate(transcription_cache_total{result="hit"}[5m])
     + rate(transcription_cache_total{result="miss"}[5m]) + 0.0001)

# Estado do circuit breaker (0=fechado, 1=aberto)
resilience4j_circuitbreaker_state{name="whisper"}

# Chamadas não-permitidas pelo CB (circuito aberto)
rate(resilience4j_circuitbreaker_calls_seconds_count{name="whisper",kind="not_permitted"}[1m])

Logs estruturados v2.2.0

Dois perfis — logback-spring.xml

PerfilFormatoAtivação
prod (padrão)JSON via logstash-logback-encodersem argumento
devTexto colorido com requestId e fileName--spring.profiles.active=dev

Campos MDC por requisição

CampoQuem populaValor
requestIdMdcLoggingFilterUUID único por request
httpMethodMdcLoggingFilterPOST, GET...
requestUriMdcLoggingFilter/api/transcriptions
fileNameTranscriptionServiceNome original do arquivo
fileSizeBytesTranscriptionServiceTamanho em bytes
whisperModelSpeechToTextClientSystran/faster-whisper-small
traceId / spanIdMicrometer Tracing (automático)Hex OpenTelemetry

Exemplo de log JSON (prod)

{
  "@timestamp": "2026-04-11T12:00:00.123Z",
  "level": "INFO",
  "logger": "TranscriptionService",
  "message": "Transcrição concluída | chars=142 | size=461842bytes",
  "requestId": "a3f2c1d0-7f3e-4b2a-9c1d-e8f5a6b7c8d9",
  "fileName": "audio.wav",
  "fileSizeBytes": "461842",
  "whisperModel": "Systran/faster-whisper-small",
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "spanId": "00f067aa0ba902b7",
  "app": "dio-speech-ai",
  "version": "3.5.0"
}

O requestId também é incluído no ProblemDetail de respostas de erro, permitindo que o cliente correlacione um erro 4xx/5xx com os logs do servidor. O header X-Request-Id é propagado em todas as respostas HTTP.

Tracing distribuído v2.7.0

OpenTelemetry SDK via micrometer-tracing-bridge-otel. Exportação para Zipkin com opentelemetry-exporter-zipkin. Sampling 100% (ajustável via TRACING_SAMPLING env).

Spans automáticos por requisição

SpanO que cobre
HTTP POST /api/transcriptionsSpan raiz — toda a requisição de entrada
WebClient POST /v1/audio/transcriptionsChamada ao Speaches (Whisper)
Redis GET/SETLeitura e escrita no cache (automático via Spring Data)

Acessar Zipkin

# Subir ambiente
docker compose up -d   # ou docker compose -f docker-compose.dev.yml up -d

# Fazer uma transcrição
curl -s -X POST http://localhost:8080/api/transcriptions \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@audio.wav;type=audio/wav"

# Abrir UI
open http://localhost:9411
# Pesquisar por: Service Name = "dio-speech-ai"

Grafana — Dashboard v2.6.0 + v3.3.0

Acesse http://localhost:3000 (admin/admin) — dashboard abre automaticamente. Provisioning via monitoring/grafana/provisioning/ — nenhuma configuração manual necessária.

SeçãoPainéis
Requisições Taxa req/s · Taxa de erros · Cache hit ratio · Whisper p99 · Tamanho médio arquivo
Latência Whisper Timeseries p50/p95/p99 · Throughput success/error por minuto
Cache Redis Hit vs miss por minuto · Hit ratio gauge
Resiliência Estado do Circuit Breaker · Chamadas por tipo · Retries por resultado