feat(observability): make the OTel tracing that is already wired up actually emit - #1577
Open
blarghmatey wants to merge 1 commit into
Open
feat(observability): make the OTel tracing that is already wired up actually emit#1577blarghmatey wants to merge 1 commit into
blarghmatey wants to merge 1 commit into
Conversation
…ctually emit OVS has had `mitol.observability.apps.ObservabilityConfig` in INSTALLED_APPS since the observability library was adopted, and it has been producing exactly zero spans the whole time. Two halves were missing and each one alone is silent: 1. NO INSTRUMENTORS WERE INSTALLED. `configure_opentelemetry()` discovers instrumentation through the `opentelemetry_instrumentor` entry point group. The venv had opentelemetry-api/sdk/exporter and nothing else, so the discovery loop found no entry points, instrumented nothing, and logged "OpenTelemetry initialized successfully" anyway. 2. NO ENDPOINT WAS READABLE. The library takes an endpoint from `OTEL_EXPORTER_OTLP_ENDPOINT` in the environment or from the `OPENTELEMETRY_ENDPOINT` Django setting. OVS declared neither, so `configure_opentelemetry()` returned at its early exit. ★ THE FAILURE MODE IS THE POINT. Neither half raises, warns, or shows up in a health check. A service with tracing "enabled" and no spans looks identical to a healthy one until somebody goes looking in Tempo and finds nothing. ── psycopg2, NOT psycopg ── `opentelemetry-instrumentation-psycopg` targets psycopg 3. This app is on psycopg2 (`psycopg2>=2.9.10,<3`), so that package would install cleanly, expose an entry point, instrument nothing, and produce no database spans — the same silent-success shape as above. mit-learn and mitxonline use the psycopg variant because they are on psycopg 3; that difference is deliberate, not drift. Deliberately NOT adding the library's `postgres` extra, which is the other way to get database instrumentation: it pins `psycopg[c]` and Django's postgresql backend prefers psycopg 3 over psycopg2 whenever it is importable, so the extra silently changes this application's database driver. That is a migration, not a tracing change. ── WHY A DJANGO SETTING RATHER THAN THE STANDARD ENV VAR ── `OPENTELEMETRY_ENDPOINT` carries the full `/v1/traces` path because the released library (2026.3.11) hands whatever it reads straight to `OTLPSpanExporter(endpoint=...)`, which uses it verbatim. The spec-standard `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` is not consulted by that version at all, and pointing `OTEL_EXPORTER_OTLP_ENDPOINT` at a base URL only starts working once mitodl/ol-django#551 ships. The Django setting is the one spelling that behaves identically before and after that change. `service.name` needs no setting: the library reads `OTEL_SERVICE_NAME` from the environment directly. ── VERIFIED ── `manage.py check` with the endpoint set logs "OTLP exporter configured to http://localhost:4318/v1/traces", and the entry point group now resolves to ['celery', 'django', 'psycopg2', 'redis', 'requests']. Traces stay off in every environment that does not set the endpoint, including local development. The ol-infrastructure side that sets it in QA and Production is a separate change and must land after this image ships. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5y3R8zEkr2A26X3Y43aBi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What are the relevant tickets?
Part of the OpenTelemetry APM coverage epic (
tk-extend-otel-tracing-to-the-uninstrumented-apps-m-41729c). OVS is one of four in-house Django apps running inapplications-productionwith zero tracing.Description (What does it do?)
OVS has had
mitol.observability.apps.ObservabilityConfiginINSTALLED_APPSsince the observability library was adopted, and has produced exactly zero spans the whole time. Two halves were missing, and each one alone is silent:configure_opentelemetry()discovers instrumentation through theopentelemetry_instrumentorentry point group. The venv hadopentelemetry-api/sdk/exporterand nothing else, so the discovery loop found no entry points, instrumented nothing, and loggedOpenTelemetry initialized successfullyanyway.OTEL_EXPORTER_OTLP_ENDPOINTin the environment or from theOPENTELEMETRY_ENDPOINTDjango setting. OVS declared neither, soconfigure_opentelemetry()returned at its early exit.Neither half raises, warns, or shows up in a health check. A service with tracing "enabled" and no spans looks identical to a healthy one until somebody goes looking in Tempo and finds nothing.
psycopg2, notpsycopg.opentelemetry-instrumentation-psycopgtargets psycopg 3; this app is onpsycopg2>=2.9.10,<3, so that package would install cleanly, expose an entry point and produce no database spans — the same silent-success shape as above. mit-learn and mitxonline use thepsycopgvariant because they are on psycopg 3; the difference is deliberate.The library's
postgresextra is also deliberately unused — it pinspsycopg[c], and Django's postgresql backend prefers psycopg 3 whenever it is importable, so the extra would silently change this application's database driver. That is a migration, not a tracing change.Why a Django setting rather than the standard env var?
OPENTELEMETRY_ENDPOINTcarries the full/v1/tracespath because the released library (2026.3.11) hands whatever it reads straight toOTLPSpanExporter(endpoint=...), which uses it verbatim. The spec-standardOTEL_EXPORTER_OTLP_TRACES_ENDPOINTis not consulted by that version at all, and pointingOTEL_EXPORTER_OTLP_ENDPOINTat a base URL only starts working once mitodl/ol-django#551 ships. The Django setting is the one spelling that behaves identically before and after that change.service.nameneeds no setting — the library readsOTEL_SERVICE_NAMEfrom the environment directly.Logging is unaffected:
ObservabilityConfigwas already installed, so structlog has been configuring this app's logging all along.How can this be tested?
Tracing stays off everywhere the endpoint is unset, including local development, so the default path is a no-op.
manage.py checkwith the endpoint set logsOpenTelemetry: OTLP exporter configured to http://localhost:4318/v1/traces.['celery', 'django', 'psycopg2', 'redis', 'requests'](previously empty).To see spans end to end you need the ol-infrastructure change that sets the endpoint in QA — that is a separate PR and must land after this image deploys, since the env vars are inert without this.
Additional Context
The only new runtime cost is the instrumentation overhead per request plus a background
BatchSpanProcessorexport. Grafana Alloy tail-samples what actually reaches Tempo (all errors, everything over 5000ms, 15% of the rest).