From 5514160067ecc5d3d2fefec1e3f703b6e022447a Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Tue, 14 Jul 2026 01:12:47 +0530 Subject: [PATCH] feat(api/observability): trace-correlate JSON logs via LOGGING dict when OTEL_ENABLED --- apps/api/plane/settings/local.py | 16 ++++++++++++++++ apps/api/plane/settings/production.py | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/apps/api/plane/settings/local.py b/apps/api/plane/settings/local.py index a346eca141..cd726d8e33 100644 --- a/apps/api/plane/settings/local.py +++ b/apps/api/plane/settings/local.py @@ -87,3 +87,19 @@ LOGGING = { }, }, } + +# OpenTelemetry APM: only when OTEL_ENABLED=1 do we extend the JSON +# formatter and attach TraceContextFilter to every handler. Attaching at +# the handler level (rather than the root logger) is required because +# most plane.* loggers have propagate=False; runtime mutation also wouldn't +# survive Django's dictConfig. Off path leaves the log schema unchanged. +if os.environ.get("OTEL_ENABLED", "0").lower() in ("1", "true", "yes"): + LOGGING["formatters"]["json"]["fmt"] = ( + "%(levelname)s %(asctime)s %(module)s %(name)s %(message)s " + "%(service_name)s %(trace_id)s %(span_id)s %(trace_flags)s" + ) + LOGGING["filters"] = { + "trace_context": {"()": "plane.observability.logging.TraceContextFilter"}, + } + for _handler in LOGGING["handlers"].values(): + _handler["filters"] = ["trace_context"] diff --git a/apps/api/plane/settings/production.py b/apps/api/plane/settings/production.py index 1dbf43196a..d9c5ed781b 100644 --- a/apps/api/plane/settings/production.py +++ b/apps/api/plane/settings/production.py @@ -97,3 +97,19 @@ LOGGING = { }, }, } + +# OpenTelemetry APM: only when OTEL_ENABLED=1 do we extend the JSON +# formatter and attach TraceContextFilter to every handler. Attaching at +# the handler level (rather than the root logger) is required because +# most plane.* loggers have propagate=False; runtime mutation also wouldn't +# survive Django's dictConfig. Off path leaves the log schema unchanged. +if os.environ.get("OTEL_ENABLED", "0").lower() in ("1", "true", "yes"): + LOGGING["formatters"]["json"]["fmt"] = ( + "%(levelname)s %(asctime)s %(module)s %(name)s %(message)s " + "%(service_name)s %(trace_id)s %(span_id)s %(trace_flags)s" + ) + LOGGING["filters"] = { + "trace_context": {"()": "plane.observability.logging.TraceContextFilter"}, + } + for _handler in LOGGING["handlers"].values(): + _handler["filters"] = ["trace_context"]