Skip to content

tracing: propagate campus trace context (X-Request-ID / X-Parent-Span-ID) on SDK calls #92

Closed

Description

@nycomp

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

What

Instrument the campus_python SDK's outbound HTTP so that calls made while the host app is handling a traced request carry Campus trace context headers:

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

Receiving campus services (campus.auth, campus.api) already turn these headers into a child span of the caller's span (#794) — today only campus.api's embedded client does this, via campus.audit.middleware.tracing.instrument_requests_session in the campus repo. The SDK itself does not (code search confirms no trace headers anywhere in this repo).

Why

Every server-to-server call a client app's backend makes to campus.auth currently starts a brand-new 1-span trace with no parentage — the audit UI can show who (client_id, campus#802) and when, but not which route in the caller fired it or in what sequence. With SDK propagation (plus host-app tracing, see campus#816 item 2 / the classroom sub-issue), an SDK call lands as a child span and the waterfall answers those questions.

Implementation notes / constraints

  • Reference implementation: instrument_requests_session() wraps requests.Session.request, computes headers at call time from the active request context (safe for shared sessions; calls outside a request context — startup, background threads — stay unparented), and is idempotent.
  • The SDK must stay host-agnostic: the flask dependency should be optional/guarded — read the host's request context if one exists (classroom is Flask), emit no headers otherwise. Decide whether to depend on campus for the helper (couples the client package to the server monorepo — probably not) or replicate the ~30-line wrapper.
  • The campus repo's Flask test harness patches the transport (tests.flask_test), so cross-repo testing can lean on campus#816's acceptance flow rather than a live deployment.

Acceptance

  • An SDK call made inside a traced host request carries both headers; the same call outside any request context carries none.
  • End-to-end (with the host-app lane from campus#816): the auth service records the SDK call as a child span of the host's span, visible in the audit waterfall.

Activity

  1. nycomp commented on Oct 5, 2026

    @nycomp
    ContributorAuthor

    Delivered by #93 (merged, dd84fc7) and verified end-to-end on the dev deployment.

    Acceptance:

    • SDK call inside a traced host request carries both headers — live-observed: a classroom request produced trace 5600b568ec804da2b2a3e3c524cb326d where the SDK call GET /auth/v1/sessions/campus/<id>/ ingested as a child span (parent_span_id = the classroom root span's id, waterfall depth 2 across services).
    • Same call outside any request context carries none — control trace 2181e77b5e534051a2fb7fd0312028c5 (no session → no SDK call) plus unit coverage; background/startup calls stay unparented by construction (headers computed at call time).

    Shape: new campus_python.tracing mirrors the propagation half of campus.audit.middleware.tracing (same headers, same flask.g contract, same idempotency marker). Deliberately self-contained — the SDK doesn't import the server-side audit client stack to emit two headers; the two implementations are no-ops for each other. 243 unit tests pass (5 new). Wire-up is automatic at CampusRequest construction; campus.api's existing explicit instrumentation is now redundant but compatible.

    Parent: nyjc-computing/campus#816.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions