mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -18,7 +18,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"github.com/coder/coder/v2/agent/agentgit"
|
||||
"github.com/coder/coder/v2/agent/agentchat"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/workspacesdk"
|
||||
@@ -86,6 +86,8 @@ func (api *API) HandleReadFile(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (api *API) streamFile(ctx context.Context, rw http.ResponseWriter, path string, offset, limit int64) (HTTPResponseCode, error) {
|
||||
logger := api.logger.With(agentchat.Fields(ctx)...)
|
||||
|
||||
if !filepath.IsAbs(path) {
|
||||
return http.StatusBadRequest, xerrors.Errorf("file path must be absolute: %q", path)
|
||||
}
|
||||
@@ -131,7 +133,7 @@ func (api *API) streamFile(ctx context.Context, rw http.ResponseWriter, path str
|
||||
reader := io.NewSectionReader(f, offset, bytesToRead)
|
||||
_, err = io.Copy(rw, reader)
|
||||
if err != nil && !errors.Is(err, io.EOF) && ctx.Err() == nil {
|
||||
api.logger.Error(ctx, "workspace agent read file", slog.Error(err))
|
||||
logger.Error(ctx, "workspace agent read file", slog.Error(err))
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
@@ -322,8 +324,8 @@ func (api *API) HandleWriteFile(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Track edited path for git watch.
|
||||
if api.pathStore != nil {
|
||||
if chatID, ancestorIDs, ok := agentgit.ExtractChatContext(r); ok {
|
||||
api.pathStore.AddPaths(append([]uuid.UUID{chatID}, ancestorIDs...), []string{path})
|
||||
if chatContext, ok := agentchat.FromContext(ctx); ok {
|
||||
api.pathStore.AddPaths(append([]uuid.UUID{chatContext.ID}, chatContext.AncestorIDs...), []string{path})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,12 +460,12 @@ func (api *API) HandleEditFiles(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Track edited paths for git watch.
|
||||
if api.pathStore != nil {
|
||||
if chatID, ancestorIDs, ok := agentgit.ExtractChatContext(r); ok {
|
||||
if chatContext, ok := agentchat.FromContext(ctx); ok {
|
||||
filePaths := make([]string, 0, len(req.Files))
|
||||
for _, f := range req.Files {
|
||||
filePaths = append(filePaths, f.Path)
|
||||
}
|
||||
api.pathStore.AddPaths(append([]uuid.UUID{chatID}, ancestorIDs...), filePaths)
|
||||
api.pathStore.AddPaths(append([]uuid.UUID{chatContext.ID}, chatContext.AncestorIDs...), filePaths)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,6 +567,8 @@ func (api *API) prepareFileEdit(path string, edits []workspacesdk.FileEdit) (int
|
||||
// On failure the temp file is cleaned up and the original is
|
||||
// untouched.
|
||||
func (api *API) atomicWrite(ctx context.Context, path string, mode *os.FileMode, r io.Reader) (int, error) {
|
||||
logger := api.logger.With(agentchat.Fields(ctx)...)
|
||||
|
||||
dir := filepath.Dir(path)
|
||||
tmpName := filepath.Join(dir, fmt.Sprintf(".%s.tmp.%s", filepath.Base(path), uuid.New().String()[:8]))
|
||||
|
||||
@@ -579,7 +583,7 @@ func (api *API) atomicWrite(ctx context.Context, path string, mode *os.FileMode,
|
||||
|
||||
cleanup := func() {
|
||||
if err := api.filesystem.Remove(tmpName); err != nil {
|
||||
api.logger.Warn(ctx, "unable to clean up temp file", slog.Error(err))
|
||||
logger.Warn(ctx, "unable to clean up temp file", slog.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,7 +605,7 @@ func (api *API) atomicWrite(ctx context.Context, path string, mode *os.FileMode,
|
||||
// no window where the target has wrong permissions.
|
||||
if mode != nil {
|
||||
if err := api.filesystem.Chmod(tmpName, *mode); err != nil {
|
||||
api.logger.Warn(ctx, "unable to set file permissions",
|
||||
logger.Warn(ctx, "unable to set file permissions",
|
||||
slog.F("path", path),
|
||||
slog.Error(err),
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"cdr.dev/slog/v3/sloggers/slogtest"
|
||||
"github.com/coder/coder/v2/agent/agentchat"
|
||||
"github.com/coder/coder/v2/agent/agentfiles"
|
||||
"github.com/coder/coder/v2/agent/agentgit"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
@@ -1157,7 +1158,7 @@ func TestHandleWriteFile_ChatHeaders_UpdatesPathStore(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
r := chi.NewRouter()
|
||||
r.Post("/write-file", api.HandleWriteFile)
|
||||
r.ServeHTTP(rr, req)
|
||||
agentchat.Middleware(r).ServeHTTP(rr, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, rr.Code)
|
||||
|
||||
@@ -1185,7 +1186,7 @@ func TestHandleWriteFile_NoChatHeaders_NoPathStoreUpdate(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
r := chi.NewRouter()
|
||||
r.Post("/write-file", api.HandleWriteFile)
|
||||
r.ServeHTTP(rr, req)
|
||||
agentchat.Middleware(r).ServeHTTP(rr, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, rr.Code)
|
||||
|
||||
@@ -1211,7 +1212,7 @@ func TestHandleWriteFile_Failure_NoPathStoreUpdate(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
r := chi.NewRouter()
|
||||
r.Post("/write-file", api.HandleWriteFile)
|
||||
r.ServeHTTP(rr, req)
|
||||
agentchat.Middleware(r).ServeHTTP(rr, req)
|
||||
|
||||
require.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
|
||||
@@ -1252,7 +1253,7 @@ func TestHandleEditFiles_ChatHeaders_UpdatesPathStore(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
r := chi.NewRouter()
|
||||
r.Post("/edit-files", api.HandleEditFiles)
|
||||
r.ServeHTTP(rr, req)
|
||||
agentchat.Middleware(r).ServeHTTP(rr, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, rr.Code)
|
||||
|
||||
@@ -1289,7 +1290,7 @@ func TestHandleEditFiles_Failure_NoPathStoreUpdate(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
r := chi.NewRouter()
|
||||
r.Post("/edit-files", api.HandleEditFiles)
|
||||
r.ServeHTTP(rr, req)
|
||||
agentchat.Middleware(r).ServeHTTP(rr, req)
|
||||
|
||||
require.NotEqual(t, http.StatusOK, rr.Code)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user