Skip to content

tracing: propagate campus trace context on SDK calls - #93

Merged
nycomp merged 1 commit into
mainfrom
feat/92-trace-propagation
Oct 5, 2026
Merged

nycomp merged 1 commit into
mainfrom
feat/92-trace-propagation

Conversation

@nycomp

@nycomp nycomp commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Parent: nyjc-computing/campus#816 (item 1 — SDK trace propagation). Refs #92.

What

Instrument CampusRequest's requests.Session at construction so SDK calls made while the host app is handling a traced request carry campus trace context headers:

  • X-Request-ID — the host request's trace id (32-hex)
  • X-Parent-Span-ID — the host request's span id (16-hex)

Receiving campus services already turn these into a child span of the caller's span (nyjc-computing/campus#794). Until now only campus.api's embedded client did this, via server-side instrumentation of the SDK session; with this change every host gets it for free, and campus.api's instrumentation becomes redundant-but-compatible (idempotent across both implementations via the shared _campus_trace_instrumented marker).

Design

New module campus_python/tracing.py mirrors the propagation half of campus.audit.middleware.tracing:

  • Same header names and the same flask.g contract (trace_id/span_id, stashed by the middleware's before_request hook).
  • Host-agnostic per tracing: propagate campus trace context (X-Request-ID / X-Parent-Span-ID) on SDK calls #92's constraint: reads the host's request context if one exists (guarded flask import), emits no headers otherwise — startup/background-thread calls stay unparented. Headers are computed at call time, so shared sessions stay correct under concurrency.
  • Deliberately self-contained: though the SDK already depends on campus-suite (for campus.common/campus.model), importing campus.audit.middleware would pull the server-side audit client stack into every client construction just to emit two headers. The two modules cross-reference each other to control drift; if the contract grows (W3C traceparent), extraction into a shared home remains the longer-term option.

Tests

tests/unit/test_trace_propagation.py (5 cases): headers present inside a traced request context; absent outside one; absent when the context has no span state (tracing off / middleware absent); caller-supplied headers take precedence (setdefault semantics); instrumentation is idempotent (construction-time, no double wrap). Full suite: 243 passed.

E2E

Acceptance runs through classroom-as-producer (nyjc-computing/campus#816 item 2, nyjc-computing/campus-classroom#37): a classroom login should show auth calls as child spans of classroom requests in the audit waterfall.

Instrument CampusRequest's requests.Session so calls made while the
host app handles a traced request carry X-Request-ID /
X-Parent-Span-ID and land as child spans in the campus audit pipeline
(campus#816 item 1, #92).

- New campus_python.tracing mirrors the propagation half of
  campus.audit.middleware.tracing: same headers, same flask.g contract
  (trace_id/span_id stashed by the middleware's before_request hook),
  same _campus_trace_instrumented idempotency marker so the two
  implementations never double-wrap.
- Deliberately self-contained: the SDK must not import the server-side
  audit client stack to emit two headers. Calls outside a traced
  request context (startup, background threads) stay unparented.
- Makes campus.api's existing session instrumentation redundant but
  compatible (idempotent across both implementations).
@nycomp
nycomp merged commit dd84fc7 into main Oct 5, 2026
2 checks passed
@nycomp
nycomp deleted the feat/92-trace-propagation branch October 5, 2026 04:06
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.

2 participants