feat: tag chat-originating agent logs with chat_id (#25019)

Workspace-agent logs emitted while serving chatd-driven requests were
not correlated with the originating chat, making agent logs hard to
attribute to the corresponding/originating chat.

This adds agent-side chat context middleware that parses `Coder-Chat-Id`
once, enriches agent access logs and structured handler/background logs,
and adds a chatd bridge log when chat headers are attached to an agent
connection.

Closes CODAGT-324
This commit is contained in:
Ethan
2026-05-08 13:25:30 +10:00
committed by GitHub
parent e9f0385198
commit 3a9080fff6
15 changed files with 380 additions and 95 deletions
+33 -2
View File
@@ -20,9 +20,12 @@ import (
"cdr.dev/slog/v3"
"cdr.dev/slog/v3/sloggers/slogtest"
"github.com/coder/coder/v2/agent/agentchat"
"github.com/coder/coder/v2/agent/agentexec"
"github.com/coder/coder/v2/agent/agentgit"
"github.com/coder/coder/v2/agent/agentproc"
"github.com/coder/coder/v2/coderd/httpmw/loggermw"
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/coder/v2/testutil"
@@ -137,7 +140,35 @@ func newTestAPIWithOptions(t *testing.T, updateEnv func([]string) ([]string, err
t.Cleanup(func() {
_ = api.Close()
})
return api.Routes()
return agentchat.Middleware(api.Routes())
}
func TestAccessLogIncludesChatID(t *testing.T) {
t.Parallel()
sink := testutil.NewFakeSink(t)
logger := sink.Logger()
api := agentproc.NewAPI(logger, agentexec.DefaultExecer, nil, nil, nil)
t.Cleanup(func() {
_ = api.Close()
})
handler := tracing.StatusWriterMiddleware(loggermw.Logger(logger)(
agentchat.Middleware(api.Routes()),
))
chatID := uuid.New().String()
w := getListWithChatHeader(t, handler, chatID)
require.Equal(t, http.StatusOK, w.Code)
entries := sink.Entries(func(entry slog.SinkEntry) bool {
return entry.Message == http.MethodGet
})
require.Len(t, entries, 1)
fields := make(map[string]any, len(entries[0].Fields))
for _, field := range entries[0].Fields {
fields[field.Name] = field.Value
}
require.Equal(t, chatID, fields["chat_id"])
}
// waitForExit polls the output endpoint until the process is
@@ -1058,7 +1089,7 @@ func TestHandleStartProcess_ChatHeaders_EmptyWorkDir_StillNotifies(t *testing.T)
}, pathStore, nil)
defer api.Close()
routes := api.Routes()
routes := agentchat.Middleware(api.Routes())
body, err := json.Marshal(workspacesdk.StartProcessRequest{
Command: "echo hello",