Skip to content

[REG-11] W0.6 - Outbound HTTP client instrumentation - #169

Merged
singret merged 1 commit into
mainfrom
reg-11-outbound-http-instrumentation
Sep 1, 2026
Merged

[REG-11] W0.6 - Outbound HTTP client instrumentation#169
singret merged 1 commit into
mainfrom
reg-11-outbound-http-instrumentation

Conversation

@singret

@singret singret commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Closes REG-11

Summary

Every outbound third-party HTTP call now produces a child span tagged with peer.service, so latency and error questions become measurements instead of guesses.

What changed

  • internal/observability/http_client.go (new): InstrumentedHTTPClient wraps an existing *http.Client's transport with otelhttp.NewTransport, preserving its existing transport (auth, retries), timeout, redirect policy, and cookie jar. Tags every span with peer.service.
  • internal/observability/google_api_client.go (new): InstrumentedGoogleAPIClient composes instrumentation with google.golang.org/api's own OAuth transport correctly. Found and fixed a real bug along the way: passing an instrumented client via option.WithHTTPClient alongside an auth option (e.g. option.WithAuthCredentialsJSON) silently bypasses the SDK's auth-transport construction entirely (confirmed against transport/http/dial.go source) - every FCM send would have failed with zero auth token and no error signal. Fixed by wrapping the instrumented RoundTripper as the base transport, which the library then layers real OAuth on top of.
  • Wired all 8 named integrations:
    • Slack - 5 client-construction sites (slack_service.go, slack_validator.go, slack_members.go, slack_config.go, slack_event_handler.go)
    • Teams - both graphClient and botfwClient, tagged msgraph/botframework respectively so the two OAuth-scoped clients are never conflated in a trace (plus the two standalone helper functions, TestTeamsCredentials and ListTeamMembers, which build their own Graph clients)
    • Telegram - all 3 client-construction sites; also threaded the ctx parameter these functions were already given but silently discarding into the actual HTTP calls
    • OpenAI / Anthropic / Ollama - newOpenAIClient now takes a peerService parameter since Ollama wraps the same underlying client type as OpenAI and would otherwise be mistagged
    • FCM - via InstrumentedGoogleAPIClient, fixing the auth-bypass bug above
    • Web Push

Acceptance criteria

  • otelhttp.NewTransport applied to all outbound clients: Slack, Teams (both clients), Telegram, OpenAI, Anthropic, Ollama, FCM, Web Push
  • Span attributes: method, status code, server address, peer.service (current otelhttp stable semconv names these http.request.method / http.response.status_code, superseding the ticket's http.method/http.status_code - verified live, semantically identical)
  • Retries appear as sibling spans, not one merged span (covered by test)
  • No auth headers, tokens, or request bodies on spans (covered by test + live verification)
  • Existing 4 MB io.LimitReader cap on Teams responses preserved (untouched)

Verification

  • go build ./..., go vet ./..., gofmt -l clean on all touched files
  • Full test suite green, plus -shuffle=on on all touched packages
  • golangci-lint run ./...: 0 issues
  • Live-verified against a real Jaeger collector: fired one request through each of the 9 peer.service values via a throwaway program, confirmed all 9 arrived as distinct, correctly tagged spans with zero PII (no auth/token/body content on any span or span event)

Wraps every third-party HTTP client with otelhttp.NewTransport so each
call becomes a child span tagged with peer.service, letting latency and
error-rate questions be answered from traces instead of guesswork.

- New internal/observability/http_client.go: InstrumentedHTTPClient
  wraps an existing *http.Client's transport (preserving auth,
  timeout, redirects, cookie jar) and tags spans with peer.service.
  Verified retries produce sibling spans (not one merged span) and
  that no auth headers, tokens, or bodies are ever recorded.
- New internal/observability/google_api_client.go:
  InstrumentedGoogleAPIClient composes instrumentation with
  google.golang.org/api's own OAuth transport correctly. Plain
  option.WithHTTPClient combined with an auth option silently drops
  the SDK's auth-transport construction entirely (confirmed against
  the library source) - this wraps the RoundTripper as the base
  transport instead, which the library layers real auth on top of.
- Wired: Slack (5 client-construction sites), Teams (graphClient and
  botfwClient, tagged msgraph/botframework so the two OAuth scopes are
  never conflated), Telegram (plus threading the ctx these calls were
  already given but silently discarding), OpenAI, Anthropic, Ollama
  (peer.service now parameterized on newOpenAIClient since Ollama
  wraps the same client type), FCM (via the new Google API helper,
  fixing the auth-bypass bug above), Web Push.
- Live-verified against a real Jaeger collector: all nine peer.service
  values arrive as distinct, correctly tagged spans with no PII on
  any span.
@mintlify

mintlify Bot commented Sep 1, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
fluidify 🟢 Ready View Preview Sep 1, 2026, 9:03 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@singret
singret merged commit 3857970 into main Sep 1, 2026
5 checks passed
@singret
singret deleted the reg-11-outbound-http-instrumentation branch September 1, 2026 09:07
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