Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ dependencies {
implementation(mn.jackson.databind)
implementation(mn.jackson.datatype.jsr310)

// Distributed tracing (OpenTelemetry). Spans/export are only active when
// OTEL_TRACES_EXPORTER=otlp is set (prod/Docker) — see application.yml.
implementation(mn.micronaut.tracing.opentelemetry.http)
implementation(mn.micronaut.tracing.opentelemetry.jdbc)
implementation(libs.opentelemetry.exporter.otlp)

// Structured JSON logging for Grafana Loki + trace/log correlation.
// logstash encoder renders JSON; the OTel MDC appender injects trace_id/span_id.
implementation(libs.logstash.logback.encoder)
implementation(libs.opentelemetry.logback.mdc)
// Enables the <if>/<then>/<else> conditional in logback.xml.
runtimeOnly(libs.janino)

testImplementation(mn.micronaut.test.rest.assured)
testImplementation(mn.junit.jupiter.api)
testImplementation(mn.junit.jupiter.params)
Expand Down Expand Up @@ -76,7 +89,9 @@ micronaut {
optimizeClassLoading = true
deduceEnvironment = true
optimizeNetty = true
replaceLogbackXml = true
// Keep logback.xml parsed at runtime so the env-driven JSON/plain switch
// and ${...} substitutions work in the optimized (Docker/prod) jar.
replaceLogbackXml = false
}
}

Expand Down
23 changes: 23 additions & 0 deletions backend/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,29 @@ jpa:
auto: update
show_sql: false

# OpenTelemetry — tracing is disabled unless OTEL_TRACES_EXPORTER=otlp is set
# (do this in the Docker/prod container). Metrics stay on Prometheus/Micrometer.
otel:
service:
name: ${OTEL_SERVICE_NAME:otis}
traces:
exporter: ${OTEL_TRACES_EXPORTER:none}
metrics:
exporter: none
logs:
exporter: none
exporter:
otlp:
protocol: grpc
endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:`http://localhost:4317`}
exclusions:
- /health
- /health/.*
- /prometheus
- /swagger
- /swagger/.*
- /swagger-ui/.*

# OpenAPI
router:
static-resources:
Expand Down
33 changes: 28 additions & 5 deletions backend/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
<configuration>
<!--
Logging is human-readable by default (local dev) and switches to structured
JSON on stdout when LOG_JSON=true (set this in the Docker/prod container so
Grafana Loki can index the fields). The OpenTelemetry MDC appender injects
trace_id/span_id/trace_flags into the MDC for log <-> trace correlation.
-->
<property name="LOG_JSON" value="${LOG_JSON:-false}"/>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<if condition='"true".equalsIgnoreCase(property("LOG_JSON"))'>
<then>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<!-- service label so Loki/Tempo can group by app -->
<customFields>{"service":"${OTEL_SERVICE_NAME:-otis}"}</customFields>
</encoder>
</then>
<else>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</else>
</if>
</appender>

<appender name="OTEL" class="io.opentelemetry.instrumentation.logback.mdc.v1_0.OpenTelemetryAppender">
<appender-ref ref="STDOUT"/>
</appender>

<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="OTEL"/>
</root>
</configuration>
</configuration>
11 changes: 11 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ dependencyResolutionManagement {
version("jackson", "2.22.0")
version("jakarta-annotation", "3.0.0")

version("logstash-logback-encoder", "8.1")
version("opentelemetry-instrumentation-alpha", "2.20.1-alpha")
version("janino", "3.1.12")

library(
"jetbrains.annotations",
"org.jetbrains",
Expand All @@ -39,6 +43,13 @@ dependencyResolutionManagement {
library("jackson-databind-nullable", "org.openapitools", "jackson-databind-nullable").version("0.2.10")
library("jakarta-annotation-api", "jakarta.annotation", "jakarta.annotation-api").versionRef("jakarta-annotation")

// Observability — JSON logging + OpenTelemetry (see backend/build.gradle.kts).
// Version managed by the Micronaut platform BOM (opentelemetry-bom).
library("opentelemetry-exporter-otlp", "io.opentelemetry", "opentelemetry-exporter-otlp").withoutVersion()
library("logstash-logback-encoder", "net.logstash.logback", "logstash-logback-encoder").versionRef("logstash-logback-encoder")
library("opentelemetry-logback-mdc", "io.opentelemetry.instrumentation", "opentelemetry-logback-mdc-1.0").versionRef("opentelemetry-instrumentation-alpha")
library("janino", "org.codehaus.janino", "janino").versionRef("janino")


plugin("micronaut.application", "io.micronaut.application").versionRef("micronaut")
plugin("micronaut.aot", "io.micronaut.aot").versionRef("micronaut")
Expand Down
Loading