diff --git a/context.go b/context.go index c2f4d05..969ea8c 100644 --- a/context.go +++ b/context.go @@ -3,6 +3,7 @@ package httplog import ( "context" "log/slog" + "sync" ) const ( @@ -15,16 +16,35 @@ func (c *ctxKeyLogAttrs) String() string { return "httplog attrs context" } +type logData struct { + mu sync.RWMutex + attrs []slog.Attr +} + // SetAttrs sets the attributes on the request log. func SetAttrs(ctx context.Context, attrs ...slog.Attr) { - if ptr, ok := ctx.Value(ctxKeyLogAttrs{}).(*[]slog.Attr); ok && ptr != nil { - *ptr = append(*ptr, attrs...) + if ptr, ok := ctx.Value(ctxKeyLogAttrs{}).(*logData); ok && ptr != nil { + ptr.mu.Lock() + defer ptr.mu.Unlock() + ptr.attrs = append(ptr.attrs, attrs...) + } +} + +func lockData(ctx context.Context) { + if ptr, ok := ctx.Value(ctxKeyLogAttrs{}).(*logData); ok && ptr != nil { + ptr.mu.RLock() + } +} + +func unlockData(ctx context.Context) { + if ptr, ok := ctx.Value(ctxKeyLogAttrs{}).(*logData); ok && ptr != nil { + ptr.mu.RUnlock() } } func getAttrs(ctx context.Context) []slog.Attr { - if ptr, ok := ctx.Value(ctxKeyLogAttrs{}).(*[]slog.Attr); ok && ptr != nil { - return *ptr + if ptr, ok := ctx.Value(ctxKeyLogAttrs{}).(*logData); ok && ptr != nil { + return ptr.attrs } return nil diff --git a/middleware.go b/middleware.go index a31f3d8..29b6150 100644 --- a/middleware.go +++ b/middleware.go @@ -36,7 +36,7 @@ func RequestLogger(logger *slog.Logger, o *Options) func(http.Handler) http.Hand return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := context.WithValue(r.Context(), ctxKeyLogAttrs{}, &[]slog.Attr{}) + ctx := context.WithValue(r.Context(), ctxKeyLogAttrs{}, &logData{}) logReqBody := o.LogRequestBody != nil && o.LogRequestBody(r) logRespBody := o.LogResponseBody != nil && o.LogResponseBody(r) @@ -56,6 +56,9 @@ func RequestLogger(logger *slog.Logger, o *Options) func(http.Handler) http.Hand start := time.Now() defer func() { + lockData(ctx) + defer unlockData(ctx) + var logAttrs []slog.Attr if rec := recover(); rec != nil {