Skip to content

feat(observability): auto-instrument httpx for outbound trace propagation - #4148

Closed
ecthelion77 wants to merge 1 commit into
IBM:mainfrom
forterro:feat/otel-httpx-instrumentation-upstream
Closed

feat(observability): auto-instrument httpx for outbound trace propagation#4148
ecthelion77 wants to merge 1 commit into
IBM:mainfrom
forterro:feat/otel-httpx-instrumentation-upstream

Conversation

@ecthelion77

@ecthelion77 ecthelion77 commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

✨ Feature / Enhancement PR

🔗 Epic / Issue

Closes #4155

Improves observability for outbound HTTP calls made by the gateway to upstream MCP servers and OAuth providers.


🚀 Summary

This PR adds automatic OpenTelemetry instrumentation for httpx, the HTTP client used by the gateway for all outbound requests (MCP server communication, OAuth token exchanges, health checks). Without this, outbound HTTP calls are invisible in distributed traces — the trace stops at the gateway and does not propagate traceparent headers to upstream services.

Changes:

  1. mcpgateway/observability.py: Adds opentelemetry-instrumentation-httpx initialization in setup_observability(), calling HTTPXClientInstrumentor().instrument() to auto-instrument all httpx.AsyncClient and httpx.Client instances.

  2. gunicorn.config.py: Re-applies httpx instrumentation in post_fork(). Gunicorn's pre-fork model requires re-initialization of OTel instrumentors in each worker process. The implementation calls uninstrument() first to avoid double-instrumentation warnings, then instrument().

  3. pyproject.toml: Adds opentelemetry-instrumentation-httpx as a dependency.


🧪 Checks

  • make lint passes
  • make test passes
  • CHANGELOG updated (if user-facing)

📓 Notes

Why uninstrument() before instrument() in post_fork?

Gunicorn forks workers from the master process. If the master already instrumented httpx, the forked worker inherits the instrumented state. Calling instrument() again would raise warnings. The safe pattern is uninstrument() + instrument() to ensure clean re-initialization in each worker.

@ecthelion77

Copy link
Copy Markdown
Contributor Author

Suggested labels: enhancement, COULD, python, observability

@ecthelion77
ecthelion77 force-pushed the feat/otel-httpx-instrumentation-upstream branch from e15f90a to d2ff2f1 Compare April 14, 2026 12:46
@ecthelion77
ecthelion77 force-pushed the feat/otel-httpx-instrumentation-upstream branch 3 times, most recently from 2c1a113 to 4322a24 Compare April 14, 2026 15:45
@ecthelion77
ecthelion77 force-pushed the feat/otel-httpx-instrumentation-upstream branch 2 times, most recently from 5920933 to c6b4d08 Compare April 22, 2026 12:15
@ecthelion77
ecthelion77 force-pushed the feat/otel-httpx-instrumentation-upstream branch from c6b4d08 to a6ab07d Compare April 27, 2026 09:37
@ecthelion77
ecthelion77 force-pushed the feat/otel-httpx-instrumentation-upstream branch from a6ab07d to 4eae6e1 Compare May 27, 2026 15:03
@ecthelion77
ecthelion77 force-pushed the feat/otel-httpx-instrumentation-upstream branch from 4eae6e1 to 5df0449 Compare June 3, 2026 09:54
@ecthelion77
ecthelion77 force-pushed the feat/otel-httpx-instrumentation-upstream branch from 5df0449 to 0207eea Compare June 11, 2026 14:45
…tion

Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
@ecthelion77
ecthelion77 force-pushed the feat/otel-httpx-instrumentation-upstream branch from 0207eea to 5a32736 Compare June 29, 2026 10:32

@jonpspri jonpspri left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this, @ecthelion77 — auto-instrumenting httpx for outbound trace propagation is genuinely the right thing to do, and the approach here (calling HTTPXClientInstrumentor().instrument() in setup_observability() and re-applying in gunicorn.config.py's post_fork()) is sound.

However, this work has been superseded by PR #6164 (affinity-path OTEL spans and concurrent forward dispatch), which covers the same capability in a more complete and opt-in form.

What PR #6164 does instead

mcpgateway/config.py on the jps-affinity-concurrency branch (and in the PR #6164 diff) adds three explicit, operator-controllable settings:

otel_httpx_instrumentation_enabled: bool = Field(
    default=False,
    description="Auto-instrument outbound httpx clients (HTTP client spans + W3C trace header injection)"
)
otel_sqlalchemy_instrumentation_enabled: bool = Field(
    default=False,
    description="Emit OTel spans for SQLAlchemy queries (high span volume; enable for debugging)"
)
otel_redis_instrumentation_enabled: bool = Field(
    default=False,
    description="Auto-instrument redis/redis.asyncio clients (per-command CLIENT spans)"
)

And mcpgateway/observability.py wires them up in setup_observability() with graceful fallback when the optional packages are not installed. It also supports both HTTPXClientInstrumentor and HTTPX2ClientInstrumentor, and handles the Gunicorn pre-fork re-init concern you identified.

Key design differences

This PR (#4148) PR #6164
Always-on (no config toggle) Opt-in via OTEL_HTTPX_INSTRUMENTATION_ENABLED
Hard dependency on opentelemetry-instrumentation-httpx Optional: logs a warning if package absent
httpx only httpx, httpx2, redis, SQLAlchemy
default=True behaviour default=False — avoids surprises in existing deployments

The opt-in default is important: httpx instrumentation adds a span per outbound call and injects traceparent headers — both are desirable, but they represent a behaviour change for existing deployments that previously had no outbound spans. Making it opt-in (with clear documentation) is the safer production approach.

Closing this PR

Since PR #6164 subsumes this work in a more complete, opt-in, multi-client form, I'm closing this PR. Issue #4155 will be closed with a pointer to PR #6164.

Again — the problem statement and approach here were correct. The subsequent affinity work just arrived at a more complete solution.

@jonpspri

Copy link
Copy Markdown
Collaborator

Closing — superseded by PR #6164. See review comment above for the full rationale.

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.

[FEATURE][OBSERVABILITY]: Auto-instrument httpx for outbound trace propagation to upstream servers

2 participants