Skip to content

Commit 2212f08

Browse files
committed
added logging for workspace related parameters and generic chi context logger + tests
1 parent 83f4f22 commit 2212f08

File tree

4 files changed

+92
-0
lines changed

4 files changed

+92
-0
lines changed

coderd/httpmw/loggermw/logger.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"sync"
88
"time"
99

10+
"github.com/go-chi/chi/v5"
11+
1012
"cdr.dev/slog"
1113
"github.com/coder/coder/v2/coderd/httpapi"
1214
"github.com/coder/coder/v2/coderd/rbac"
@@ -136,6 +138,21 @@ func (c *SlogRequestLogger) WriteLog(ctx context.Context, status int) {
136138
slog.F("latency_ms", float64(end.Sub(c.start)/time.Millisecond)),
137139
)
138140

141+
if chiCtx := chi.RouteContext(ctx); chiCtx != nil {
142+
urlParams := chiCtx.URLParams
143+
routeParamsFields := make([]slog.Field, 0, len(urlParams.Keys))
144+
145+
for k, v := range urlParams.Keys {
146+
if urlParams.Values[k] != "" {
147+
routeParamsFields = append(routeParamsFields, slog.F(v, urlParams.Values[k]))
148+
}
149+
}
150+
151+
if len(routeParamsFields) > 0 {
152+
logger = logger.With(routeParamsFields...)
153+
}
154+
}
155+
139156
// We already capture most of this information in the span (minus
140157
// the response body which we don't want to capture anyways).
141158
tracing.RunWithoutSpan(ctx, func(ctx context.Context) {

coderd/httpmw/loggermw/logger_internal_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99
"time"
1010

11+
"github.com/go-chi/chi/v5"
1112
"github.com/stretchr/testify/assert"
1213
"github.com/stretchr/testify/require"
1314

@@ -156,6 +157,54 @@ func TestLoggerMiddleware_WebSocket(t *testing.T) {
156157
require.Len(t, sink.entries, 1, "log was written twice")
157158
}
158159

160+
func TestRequestLogger_HTTPRouteParams(t *testing.T) {
161+
t.Parallel()
162+
163+
sink := &fakeSink{}
164+
logger := slog.Make(sink)
165+
logger = logger.Leveled(slog.LevelDebug)
166+
167+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
168+
defer cancel()
169+
170+
chiCtx := chi.NewRouteContext()
171+
chiCtx.URLParams.Add("workspace", "test-workspace")
172+
chiCtx.URLParams.Add("agent", "test-agent")
173+
174+
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
175+
176+
// Create a test handler to simulate an HTTP request
177+
testHandler := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
178+
rw.WriteHeader(http.StatusOK)
179+
_, _ = rw.Write([]byte("OK"))
180+
})
181+
182+
// Wrap the test handler with the Logger middleware
183+
loggerMiddleware := Logger(logger)
184+
wrappedHandler := loggerMiddleware(testHandler)
185+
186+
// Create a test HTTP request
187+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "/test-path/}", nil)
188+
require.NoError(t, err, "failed to create request")
189+
190+
sw := &tracing.StatusWriter{ResponseWriter: httptest.NewRecorder()}
191+
192+
// Serve the request
193+
wrappedHandler.ServeHTTP(sw, req)
194+
195+
fieldsMap := make(map[string]any)
196+
for _, field := range sink.entries[0].Fields {
197+
fieldsMap[field.Name] = field.Value
198+
}
199+
200+
// Check that the log contains the expected fields
201+
requiredFields := []string{"workspace", "agent"}
202+
for _, field := range requiredFields {
203+
_, exists := fieldsMap[field]
204+
require.True(t, exists, "field %q is missing in log fields", field)
205+
}
206+
}
207+
159208
type fakeSink struct {
160209
entries []slog.SinkEntry
161210
newEntries chan slog.SinkEntry

coderd/httpmw/workspaceagentparam.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import (
66

77
"github.com/go-chi/chi/v5"
88

9+
"cdr.dev/slog"
10+
911
"github.com/coder/coder/v2/coderd/database"
1012
"github.com/coder/coder/v2/coderd/httpapi"
13+
"github.com/coder/coder/v2/coderd/httpmw/loggermw"
1114
"github.com/coder/coder/v2/codersdk"
1215
)
1316

@@ -81,6 +84,14 @@ func ExtractWorkspaceAgentParam(db database.Store) func(http.Handler) http.Handl
8184

8285
ctx = context.WithValue(ctx, workspaceAgentParamContextKey{}, agent)
8386
chi.RouteContext(ctx).URLParams.Add("workspace", build.WorkspaceID.String())
87+
88+
if rlogger := loggermw.RequestLoggerFromContext(ctx); rlogger != nil {
89+
rlogger.WithFields(
90+
slog.F("workspace_name", resource.Name),
91+
slog.F("agent_name", agent.Name),
92+
)
93+
}
94+
8495
next.ServeHTTP(rw, r.WithContext(ctx))
8596
})
8697
}

coderd/httpmw/workspaceparam.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ import (
99
"github.com/go-chi/chi/v5"
1010
"github.com/google/uuid"
1111

12+
"cdr.dev/slog"
13+
1214
"github.com/coder/coder/v2/coderd/database"
1315
"github.com/coder/coder/v2/coderd/httpapi"
16+
"github.com/coder/coder/v2/coderd/httpmw/loggermw"
1417
"github.com/coder/coder/v2/codersdk"
1518
)
1619

@@ -48,6 +51,11 @@ func ExtractWorkspaceParam(db database.Store) func(http.Handler) http.Handler {
4851
}
4952

5053
ctx = context.WithValue(ctx, workspaceParamContextKey{}, workspace)
54+
55+
if rlogger := loggermw.RequestLoggerFromContext(ctx); rlogger != nil {
56+
rlogger.WithFields(slog.F("workspace_name", workspace.Name))
57+
}
58+
5159
next.ServeHTTP(rw, r.WithContext(ctx))
5260
})
5361
}
@@ -154,6 +162,13 @@ func ExtractWorkspaceAndAgentParam(db database.Store) func(http.Handler) http.Ha
154162

155163
ctx = context.WithValue(ctx, workspaceParamContextKey{}, workspace)
156164
ctx = context.WithValue(ctx, workspaceAgentParamContextKey{}, agent)
165+
166+
if rlogger := loggermw.RequestLoggerFromContext(ctx); rlogger != nil {
167+
rlogger.WithFields(
168+
slog.F("workspace_name", workspace.Name),
169+
slog.F("agent_name", agent.Name),
170+
)
171+
}
157172
next.ServeHTTP(rw, r.WithContext(ctx))
158173
})
159174
}

0 commit comments

Comments
 (0)