Skip to content

Commit 739f57d

Browse files
committed
Add wrapper for context logging (and reg)
1 parent 6d0ad9b commit 739f57d

3 files changed

Lines changed: 60 additions & 13 deletions

File tree

go/cmd/gitter/gitter.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const shutdownTimeout = 10 * time.Second
5454
// runCmd executes a command with context cancellation handled by sending SIGINT.
5555
// It logs cancellation errors separately as requested.
5656
func runCmd(ctx context.Context, dir string, env []string, name string, args ...string) error {
57-
logger.DebugContext(ctx, "Running command", slog.String("cmd", name), slog.String("url", ctx.Value(urlKey).(string)), slog.Any("args", args))
57+
logger.DebugContext(ctx, "Running command", slog.String("cmd", name), slog.Any("args", args))
5858
cmd := exec.CommandContext(ctx, name, args...)
5959
if dir != "" {
6060
cmd.Dir = dir
@@ -64,7 +64,7 @@ func runCmd(ctx context.Context, dir string, env []string, name string, args ...
6464
}
6565
// Use SIGINT instead of SIGKILL for graceful shutdown of subprocesses
6666
cmd.Cancel = func() error {
67-
logger.DebugContext(ctx, "SIGINT sent to command", slog.String("cmd", name), slog.String("url", ctx.Value(urlKey).(string)), slog.Any("args", args))
67+
logger.DebugContext(ctx, "SIGINT sent to command", slog.String("cmd", name), slog.Any("args", args))
6868
return cmd.Process.Signal(syscall.SIGINT)
6969
}
7070
// Ensure it eventually dies if it ignores SIGINT
@@ -74,13 +74,13 @@ func runCmd(ctx context.Context, dir string, env []string, name string, args ...
7474
if err != nil {
7575
if ctx.Err() != nil {
7676
// Log separately if cancelled
77-
logger.WarnContext(ctx, "Command cancelled", slog.String("cmd", name), slog.String("url", ctx.Value(urlKey).(string)), slog.Any("err", ctx.Err()))
77+
logger.WarnContext(ctx, "Command cancelled", slog.String("cmd", name), slog.Any("err", ctx.Err()))
7878
return fmt.Errorf("command %s cancelled: %w", name, ctx.Err())
7979
}
8080

8181
return fmt.Errorf("command %s failed: %w, output: %s", name, err, out)
8282
}
83-
logger.DebugContext(ctx, "Command completed successfully", slog.String("cmd", name), slog.String("url", ctx.Value(urlKey).(string)), slog.String("out", string(out)))
83+
logger.DebugContext(ctx, "Command completed successfully", slog.String("cmd", name), slog.String("out", string(out)))
8484

8585
return nil
8686
}
@@ -137,7 +137,7 @@ func fetchBlob(ctx context.Context, url string, forceUpdate bool) ([]byte, error
137137

138138
// Check if we need to fetch
139139
if forceUpdate || !ok || time.Since(accessTime) > fetchTimeout {
140-
logger.InfoContext(ctx, "Fetching git blob", slog.String("url", ctx.Value(urlKey).(string)), slog.Duration("sinceAccessTime", time.Since(accessTime)))
140+
logger.InfoContext(ctx, "Fetching git blob", slog.Duration("sinceAccessTime", time.Since(accessTime)))
141141
if _, err := os.Stat(path.Join(repoPath, ".git")); os.IsNotExist(err) {
142142
// Clone
143143
err := runCmd(ctx, "", []string{"GIT_TERMINAL_PROMPT=0"}, "git", "clone", "--", url, repoPath)
@@ -156,7 +156,7 @@ func fetchBlob(ctx context.Context, url string, forceUpdate bool) ([]byte, error
156156
if err != nil && isIndexLockError(err) {
157157
// index.lock exists, likely a previous git reset got terminated and wasn't cleaned up properly.
158158
// We can remove the file and retry the command
159-
logger.WarnContext(ctx, "index.lock exists, attempting to remove and retry", slog.String("url", ctx.Value(urlKey).(string)))
159+
logger.WarnContext(ctx, "index.lock exists, attempting to remove and retry")
160160
indexLockPath := filepath.Join(repoPath, ".git", "index.lock")
161161
if err := os.Remove(indexLockPath); err != nil {
162162
return nil, fmt.Errorf("failed to remove index.lock in %s: %w", repoPath, err)
@@ -169,7 +169,7 @@ func fetchBlob(ctx context.Context, url string, forceUpdate bool) ([]byte, error
169169
}
170170
}
171171

172-
logger.InfoContext(ctx, "Archiving git blob", slog.String("url", ctx.Value(urlKey).(string)))
172+
logger.InfoContext(ctx, "Archiving git blob")
173173
// Archive
174174
// tar --zstd -cf <archivePath> -C "<gitStorePath>/<repoDirName>" .
175175
// using -C to archive the relative path so it unzips nicely
@@ -201,6 +201,7 @@ func fetchBlob(ctx context.Context, url string, forceUpdate bool) ([]byte, error
201201

202202
func main() {
203203
logger.InitGlobalLogger()
204+
logger.RegisterContextKey(urlKey, "repoURL")
204205
defer logger.Close()
205206

206207
port := flag.Int("port", 8888, "Listen port")
@@ -283,7 +284,7 @@ func gitHandler(w http.ResponseWriter, r *http.Request) {
283284

284285
ctx := context.WithValue(r.Context(), urlKey, url)
285286

286-
logger.InfoContext(ctx, "Received request", slog.String("url", url), slog.Bool("forceUpdate", forceUpdate), slog.String("remoteAddr", r.RemoteAddr))
287+
logger.InfoContext(ctx, "Received request", slog.Bool("forceUpdate", forceUpdate), slog.String("remoteAddr", r.RemoteAddr))
287288
// If request came from a local ip, don't do the check
288289
if !isLocalRequest(r) {
289290
// Check if url starts with protocols: http(s)://, git://, ssh://, (s)ftp://
@@ -308,7 +309,7 @@ func gitHandler(w http.ResponseWriter, r *http.Request) {
308309
})
309310

310311
if err != nil {
311-
logger.ErrorContext(ctx, "Error fetching/archiving blob", slog.String("url", ctx.Value(urlKey).(string)), slog.Any("error", err))
312+
logger.ErrorContext(ctx, "Error fetching blob", slog.Any("error", err))
312313
if isAuthError(err) {
313314
http.Error(w, fmt.Sprintf("Error fetching blob: %v", err), http.StatusForbidden)
314315
return
@@ -322,11 +323,11 @@ func gitHandler(w http.ResponseWriter, r *http.Request) {
322323
w.Header().Set("Content-Disposition", "attachment; filename=\"git-blob.zst\"")
323324
w.WriteHeader(http.StatusOK)
324325
if _, err := io.Copy(w, bytes.NewReader(fileData.([]byte))); err != nil {
325-
logger.ErrorContext(ctx, "Error copying file", slog.String("url", url), slog.Any("error", err))
326+
logger.ErrorContext(ctx, "Error copying file", slog.Any("error", err))
326327
http.Error(w, "Error copying file", http.StatusInternalServerError)
327328

328329
return
329330
}
330331

331-
logger.InfoContext(ctx, "Request completed successfully", slog.String("url", ctx.Value(urlKey).(string)))
332+
logger.InfoContext(ctx, "Request completed successfully")
332333
}

go/logger/contexthandler.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// contextHandler is a wrapper around slog.Handler that automatically
2+
// extracts registered context keys and logs their values as part of the attributes.
3+
package logger
4+
5+
import (
6+
"context"
7+
"log/slog"
8+
"sync"
9+
)
10+
11+
var (
12+
ctxKeysMu sync.RWMutex
13+
ctxKeys = make(map[any]string)
14+
)
15+
16+
// RegisterContextKey registers a context key that should be automatically
17+
// extracted and logged if present in the context.
18+
func RegisterContextKey(ctxKey any, logKey string) {
19+
ctxKeysMu.Lock()
20+
defer ctxKeysMu.Unlock()
21+
ctxKeys[ctxKey] = logKey
22+
}
23+
24+
type contextHandler struct {
25+
slog.Handler
26+
}
27+
28+
func (h *contextHandler) Handle(ctx context.Context, r slog.Record) error {
29+
ctxKeysMu.RLock()
30+
for ctxKey, logKey := range ctxKeys {
31+
if val := ctx.Value(ctxKey); val != nil {
32+
r.AddAttrs(slog.Any(logKey, val))
33+
}
34+
}
35+
ctxKeysMu.RUnlock()
36+
37+
return h.Handler.Handle(ctx, r)
38+
}
39+
40+
func (h *contextHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
41+
return &contextHandler{h.Handler.WithAttrs(attrs)}
42+
}
43+
44+
func (h *contextHandler) WithGroup(name string) slog.Handler {
45+
return &contextHandler{h.Handler.WithGroup(name)}
46+
}

go/logger/init.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
)
2222

2323
var (
24-
slogLogger = slog.New(newLocalHandler(os.Stdout))
24+
slogLogger = slog.New(&contextHandler{newLocalHandler(os.Stdout)})
2525
errorClient *errorreporting.Client
2626
once sync.Once
2727
tp *sdktrace.TracerProvider
@@ -52,7 +52,7 @@ func InitGlobalLogger() {
5252
initTracing(context.Background(), projectID, serviceName)
5353
}
5454
handler := slog.NewJSONHandler(os.Stdout, cloudHandlerOptions())
55-
slogLogger = slog.New(handler)
55+
slogLogger = slog.New(&contextHandler{handler})
5656
})
5757
}
5858

0 commit comments

Comments
 (0)