feat: handle session_id in agent middleware - #28039
Draft
aqandrew wants to merge 1 commit into
Draft
Conversation
Add tracing.SessionIDMiddleware, a log-only middleware that reads the session_id W3C baggage member and attaches it to the request log context, and wire it into the agent HTTP stack before loggermw so agent request logs can be correlated by session. Unlike Middleware, it does not create spans, emit telemetry, or gate on route patterns, per RFC requirement 6.2. Verified with go test ./coderd/tracing/..., go vet, and golangci-lint on the changed packages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements DEVEX-660: handle
session_idin the agent middleware, per connection-log RFC requirement 6.2.What
tracing.SessionIDMiddleware, a log-only middleware that reads thesession_idW3C baggage member and attaches it to the request log context. Unliketracing.Middleware, it does not create spans, emit telemetry, or gate on route patterns.agent/api.go) beforeloggermw.Logger, so agent request logs (including the access-log line) can be correlated by session ID.Why not spans on the agent
RFC 6.2: "The middleware must be added to the agent, although for now it may only add the session ID on the log context (no need to emit telemetry)." Spans/telemetry on the agent are out of scope here.
Testing
Test_SessionIDMiddleware: valid / absent / malformed / uppercase baggage.Test_SessionIDMiddleware_AccessLog: confirms the field reachesloggermw's completion log line when wired in the agent order.go vetandgolangci-lintpass oncoderd/tracingandagent.Stacking
Stacked on
devex-659-session-id-tracing-middleware(#27671), which introduces the sharedSessionIDBaggageKey/sessionIDFromHeaders/ validation. Review/merge #27671 first.Implementation plan
DEVEX-660: Handle
session_idin agent middlewareImplementation status
Done locally on branch
devex-660-session-id-agent-middleware(stacked onDEVEX-659), commit
13ed4696c8:tracing.SessionIDMiddleware(log-only) incoderd/tracing/httpmw.go.agent/api.gobeforeloggermw.Logger.Test_SessionIDMiddleware(valid/none/malformed/uppercase) andTest_SessionIDMiddleware_AccessLog(verifies the field reaches loggermw'saccess-log line). Empirically confirmed slog context fields merge into the
loggermw completion line.
go test ./coderd/tracing/...,go vet ./coderd/tracing/... ./agent/,golangci-lint runon both packages,gofmtclean.--no-verifydue to the known environmental actionlintpre-commit deadlock in this workspace; ran the equivalent Go checks manually.
Not yet done: push branch, open PR.
Summary
Add the connection-log RFC's
session_idcorrelation to the agent's HTTPmiddleware stack. When an incoming agent API request carries a
session_idW3C baggage member, the agent must attach it to the request log context so
agent-side request logs can be correlated with coderd logs and client logs by a
single session ID.
Per RFC requirement 6.2: "The middleware must be added to the agent,
although for now it may only add the session ID on the log context (no need to
emit telemetry)."
Scope
In scope:
agent/api.go) that reads thesession_idbaggage member and adds it to the request log context.Explicitly out of scope (separate RFC items / tickets):
agentsshcommand logging with session ID(RFC feat(cdr): Approach 3 - Initial UI (port over cdr/m components) #8, chore: Add documentation of our phased approach to the UX #15).
connection_logssession_id column / user ID (RFC feat(cdr): Approach 3 - Initial UI (port over cdr/m components) #8).How this differs from DEVEX-659
tracing.Middleware(tracerProvider)incoderd/coderd.goagent/api.gotracing.Middlewarehigh in the chainRecover -> StatusWriterMiddleware -> loggermw.Logger -> agentchat.Middleware(no span middleware)/api/v0/...routessession_idspan attribute when a tracer is presentslog.With(ctx, slog.F("session_id", id))surfaced by downstream logging with the request contextloggermw's completion log because it logs vialogger.Debug(ctx, ...)Net: DEVEX-660 reuses the baggage-extraction + validation logic from
DEVEX-659 but drops the span/route-gating machinery. It is a strictly smaller,
log-only middleware.
Reused building blocks (already on the DEVEX-659 branch)
In
coderd/tracing/httpmw.go:const SessionIDBaggageKey = "session_id"(wire contract).func sessionIDFromHeaders(h http.Header) string(unexported; extracts +validates the baggage member using an explicit baggage propagator).
func ValidSessionID(s string) bool(exported; lowercase 32-char hex).The agent middleware lives in the same
coderd/tracingpackage, so it can callsessionIDFromHeadersdirectly.Design
Add a standalone, log-only middleware to
coderd/tracing/httpmw.go:Wire it into the agent stack in
agent/api.go, beforeloggermw.Loggerso the field is present in the request context when the completion log is
emitted:
Why placement before
loggermwworksloggermw.Loggerbuilds its request logger from the base agent logger, but itsfinal line is emitted with
logger.Debug(ctx, c.message)using the requestcontext. slog merges fields stored on the context via
slog.With, so asession_idadded bySessionIDMiddlewareappears both on the completion logline and on any downstream handler log that uses the request context. This is
the same behavior DEVEX-659 verifies on the coderd side.
slog.Fliteral constraintAs on the coderd side, the first argument to
slog.Fmust be a snake_casestring literal (repo ruleguard). Keep
slog.F("session_id", ...)literal;do not pass
SessionIDBaggageKey. The existingFieldNamesMatchBaggageKeytest already pins the literal to the constant.
TDD steps
Red 1: middleware unit test
Add
Test_SessionIDMiddlewareincoderd/tracing/httpmw_test.go(reuse thetestutil.NewFakeSinkpattern already inTest_Middleware_SessionID):a
session_idfield equal to the sent value;session_idfield;session_id=not-valid) -> nosession_idfield;session_idfield (guards lowercase-only).Runs red because
SessionIDMiddlewaredoes not exist yet.Green 1
Implement
SessionIDMiddlewareas above. Run:go test ./coderd/tracing/... -run 'Test_SessionIDMiddleware' -count=1.Red 2: agent wiring test
Add a test that exercises the agent middleware chain end to end and asserts the
request completion log carries
session_id. Mirror the existing pattern inagent/agentchat/log_test.go, which composestracing.StatusWriterMiddleware(loggermw.Logger(sink.Logger(), nil)(handler))with a fake sink. Build the same chain including
tracing.SessionIDMiddleware,send a request with a
baggage: session_id=<hex>header, and assert thecaptured log entry contains the
session_idfield. Add a negative case with nobaggage.
Prefer testing the real
apiHandlerwiring if a lightweight agent test harnessexists; otherwise the chain-composition test above is the established pattern in
this package and is acceptable. Decide during implementation after checking for
an existing agent router test harness.
Green 2
Add
tracing.SessionIDMiddlewareto ther.Use(...)list inagent/api.go. Run the new agent test.Refactor
sessionIDFromHeaders/ValidSessionIDare reused, not reimplemented.
Middlewareshould also delegate itslog-context step to
SessionIDMiddlewareto remove the small duplication.Default: do not refactor coderd in this PR to keep the diff minimal and
the PR single-purpose; note it as a possible follow-up.
Validation
go test ./coderd/tracing/... -count=1go test ./agent/... -run '<new test name>' -count=1go vet ./coderd/tracing/... ./agent/...make lint(verify ruleguard passes on the literalslog.Ffield).make genis not required (no DB/proto changes).Branch / PR strategy
devex-660-session-id-agent-middleware, its own PR per theRFC phasing and the established one-ticket-per-PR pattern.
coderd/tracingsymbols (SessionIDBaggageKey,sessionIDFromHeaders,ValidSessionID) introduced by DEVEX-659(PR feat(coderd/tracing): correlate request logs and spans by session_id #27671).
devex-660-...ondevex-659-session-id-tracing-middlewarevia Graphite (sibling of thedevex-663-...frontend branch).feat(agent): add session_id to agent request log context(scope path must contain all changed files; if the change spans
coderd/tracingandagent, use a broader scope or omit it).Agents disclosure.
Open questions / risks
Which base?Resolved: stack ondevex-659-session-id-tracing-middlewarevia Graphite (feat(coderd/tracing): correlate request logs and spans by session_id #27671 not merged yet).
way to drive the real
apiHandlerwith a sink logger, or whether to use thechain-composition pattern from
agentchat/log_test.go.terminal uses the reconnecting-PTY path, which does not traverse this HTTP
middleware. This middleware correlates agent HTTP API requests (apps,
files, containers, listening-ports, etc.) whose clients send
session_idbaggage per RFC chore: Add golangci-lint and codecov #3. Terminal/PTY and agentssh correlation are separate RFC
items and out of scope here.
Opened by Coder Agents on behalf of @aqandrew.