Skip to content

feat: OpenTelemetry tracing + JSON logging for Grafana Loki#127

Merged
TheMeinerLP merged 3 commits into
mainfrom
feat/observability-loki
Jun 20, 2026
Merged

feat: OpenTelemetry tracing + JSON logging for Grafana Loki#127
TheMeinerLP merged 3 commits into
mainfrom
feat/observability-loki

Conversation

@TheMeinerLP

Copy link
Copy Markdown
Contributor

Why

For prod/Docker we want structured JSON logs that Grafana Loki can index cleanly, plus OpenTelemetry tracing with log↔trace correlation. Metrics stay on the existing Prometheus/Micrometer path.

What

  • JSON logging (logback.xml): emits structured JSON on stdout when LOG_JSON=true; local dev keeps the human-readable pattern. Every line carries trace_id/span_id via the OpenTelemetry MDC appender.
  • OpenTelemetry tracing (application.yml): disabled by default (otel.traces.exporter: none), activates when OTEL_TRACES_EXPORTER=otlp is set, exporting spans over OTLP gRPC. /health, /prometheus, /swagger* are excluded.
  • Dependencies moved into the libs version catalog in settings.gradle.kts (OTLP exporter version managed by the Micronaut platform BOM).
  • AOT: replaceLogbackXml disabled so the env-driven log switch works at runtime in the optimized jar.

Container env vars (prod/Docker)

LOG_JSON=true
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_ENDPOINT=http://alloy:4317   # your Alloy/Collector (gRPC)
OTEL_SERVICE_NAME=otis                           # optional, default "otis"

Without these (local dev) behavior is unchanged: readable logs, no export.

Grafana

  • Loki's JSON parser now picks up level/logger_name/service etc.
  • Set a Loki derived field on trace_id → Tempo for log-to-trace jumps.

Verification

  • ./gradlew :backend:test
  • Prod AOT path :backend:jar :backend:optimizedBuildLayers :backend:optimizedDockerfile ✅ (logback parses both branches; janino conditional resolves)
  • Catalog aliases resolve: exporter-otlp → 1.54.1, logstash-encoder 8.1, logback-mdc 2.20.1-alpha, janino 3.1.12

Not done: runtime smoke test of an actual JSON line / live span (needs a running DB + collector).

Add the version-catalog entries (settings.gradle.kts) and wire them into
the backend: micronaut-tracing-opentelemetry http/jdbc, the OTLP exporter,
logstash-logback-encoder, the OpenTelemetry logback MDC appender and janino.

Also disable the AOT replaceLogbackXml optimization so logback.xml is parsed
at runtime, which the env-driven JSON/plain switch and ${...} substitution
in the optimized (Docker/prod) jar depend on.
Switch logback to emit structured JSON on stdout when LOG_JSON=true (set in
the Docker/prod container) so Loki can index the fields; local dev keeps the
human-readable pattern. Wrap the appender with the OpenTelemetry MDC appender
so each line carries trace_id/span_id for log <-> trace correlation.
Add the otel config block: tracing is disabled by default (exporter "none")
and activates when OTEL_TRACES_EXPORTER=otlp is set, exporting spans over OTLP
gRPC to the configured endpoint. Metrics stay on Prometheus/Micrometer, and
health/prometheus/swagger paths are excluded from tracing.
@github-actions

Copy link
Copy Markdown
Contributor

Test results

 6 files   6 suites   0s ⏱️
 5 tests  4 ✅ 1 💤 0 ❌
15 runs  12 ✅ 3 💤 0 ❌

Results for commit e35b7d8.

@TheMeinerLP TheMeinerLP merged commit 78b6b35 into main Jun 20, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant