Skip to content

feat(observability): built-in OpenTelemetry auto-instrumentation (apso-packages#39) - #4

Open
cultron wants to merge 1 commit into
mainfrom
feat/opentelemetry
Open

feat(observability): built-in OpenTelemetry auto-instrumentation (apso-packages#39)#4
cultron wants to merge 1 commit into
mainfrom
feat/opentelemetry

Conversation

@cultron

@cultron cultron commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

What this does

Adds built-in OpenTelemetry auto-instrumentation to the Python service template, matching what shipped for the TS template (apsoai/service-template-ts #24).

What got wired

  • app/observability/tracing.pysetup_tracing(app, engine) builds a TracerProvider with an OTLP HTTP span exporter and resource attributes (service.name, service.version), then auto-instruments:
    • the FastAPI app via FastAPIInstrumentor.instrument_app
    • the SQLAlchemy engine via SQLAlchemyInstrumentor (uses engine.sync_engine for the async engine)
  • app/main.pysetup_tracing(app, engine) is called from the FastAPI lifespan after the app + engine exist; shutdown_tracing() flushes/shuts down the provider on shutdown so in-flight spans are exported.
  • pyproject.toml — pinned, mutually-compatible deps: opentelemetry-sdk, opentelemetry-exporter-otlp-proto-http, opentelemetry-instrumentation-fastapi, opentelemetry-instrumentation-sqlalchemy.
  • .env.example — commented/disabled OTel vars.
  • README — "Observability (OpenTelemetry)" section (enable steps + env table + how it is wired).

Environment variables

Variable Description Default
OTEL_EXPORTER_OTLP_ENDPOINT OTLP HTTP endpoint. Setting this enables tracing. unset (tracing off)
OTEL_SERVICE_NAME service.name on every trace. falls back to APP_NAME, then .apsorc serviceName, then apso-service
OTEL_ENABLED Force-enable (true/1/yes/on) or force-disable (false/0/no/off), overriding endpoint detection. unset

Standard OTEL_* SDK variables (headers, resource attrs, sampling) are honored too.

Safe by default

Tracing is a no-op unless OTEL_ENABLED is truthy, or (unset) an OTEL_EXPORTER_OTLP_ENDPOINT is present. Setup is wrapped in try/except so telemetry can never take the app down, and BatchSpanProcessor exports on a background thread so an unreachable collector never blocks startup or requests.

Verification

  • pip install -e ".[dev]" resolves cleanly (Python 3.12); OTel imports OK.
  • pytest — 2 passed.
  • Critical regression: with OTel disabled (no endpoint, no flag), the app imports and runs through the full lifespan (startup + shutdown) with no error and no unreachable-collector hang.
  • Also verified the enabled path (unreachable collector at http://127.0.0.1:4318) starts and shuts down without hanging.
  • ruff / black clean on the changed files.

This matches the TS template's pattern: a tracing bootstrap started with the app, auto-instrumentation, OTLP exporter, and a safe no-op default.

🤖 Generated with Claude Code

…o-packages#39)

Wire OpenTelemetry into the Python service template, mirroring the TS
template. A setup_tracing(app, engine) bootstrap builds a TracerProvider
with an OTLP HTTP span exporter and service.name/service.version resource
attributes, then auto-instruments the FastAPI app (FastAPIInstrumentor) and
the SQLAlchemy engine (SQLAlchemyInstrumentor). It is called from the FastAPI
lifespan in app/main.py after the app and engine exist, and the provider is
flushed/shut down on app shutdown.

Safe by default: tracing is a no-op unless OTEL_ENABLED is truthy or (unset)
an OTEL_EXPORTER_OTLP_ENDPOINT is present. Setup is wrapped in try/except so
telemetry can never take the app down, and BatchSpanProcessor exports on a
background thread so an unreachable collector never blocks startup.

Env vars: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME (falls back to
APP_NAME, then .apsorc serviceName, then apso-service), OTEL_ENABLED. Added
commented/disabled to .env.example and documented in a README "Observability
(OpenTelemetry)" section.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
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