1
1
package main
2
2
3
3
import (
4
+ "context"
4
5
"fmt"
5
6
"net/http"
6
7
"os"
@@ -12,15 +13,15 @@ import (
12
13
13
14
func LogHandler () * slog.JSONHandler {
14
15
// Setup a JSON handler for the new log/slog library
15
- return slog.HandlerOptions {
16
+ return slog .NewJSONHandler ( os . Stdout , & slog. HandlerOptions {
16
17
// Remove default time slog.Attr, we create our own later
17
18
ReplaceAttr : func (groups []string , a slog.Attr ) slog.Attr {
18
19
if a .Key == slog .TimeKey {
19
20
return slog.Attr {}
20
21
}
21
22
return a
22
23
},
23
- }. NewJSONHandler ( os . Stdout )
24
+ })
24
25
}
25
26
26
27
// StructuredLogger is a simple, but powerful implementation of a custom structured
@@ -37,6 +38,8 @@ type StructuredLogger struct {
37
38
}
38
39
39
40
func (l * StructuredLogger ) NewLogEntry (r * http.Request ) middleware.LogEntry {
41
+ ctx := r .Context ()
42
+
40
43
var logFields []slog.Attr
41
44
logFields = append (logFields , slog .String ("ts" , time .Now ().UTC ().Format (time .RFC1123 )))
42
45
@@ -58,54 +61,32 @@ func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
58
61
slog .String ("uri" , fmt .Sprintf ("%s://%s%s" , scheme , r .Host , r .RequestURI )),
59
62
))
60
63
61
- entry := StructuredLoggerEntry {Logger : slog .New (handler )}
64
+ entry := StructuredLoggerEntry {
65
+ ctx : ctx ,
66
+ Logger : slog .New (handler ),
67
+ }
62
68
63
- entry .Logger .LogAttrs (slog .LevelInfo , "request started" )
69
+ entry .Logger .LogAttrs (ctx , slog .LevelInfo , "request started" )
64
70
65
71
return & entry
66
72
}
67
73
68
74
type StructuredLoggerEntry struct {
75
+ ctx context.Context
69
76
Logger * slog.Logger
70
77
}
71
78
72
79
func (l * StructuredLoggerEntry ) Write (status , bytes int , header http.Header , elapsed time.Duration , extra interface {}) {
73
- l .Logger .LogAttrs (slog .LevelInfo , "request complete" ,
80
+ l .Logger .LogAttrs (l . ctx , slog .LevelInfo , "request complete" ,
74
81
slog .Int ("resp_status" , status ),
75
82
slog .Int ("resp_byte_length" , bytes ),
76
83
slog .Float64 ("resp_elapsed_ms" , float64 (elapsed .Nanoseconds ())/ 1000000.0 ),
77
84
)
78
85
}
79
86
80
- func (l * StructuredLoggerEntry ) Panic (v interface {} , stack []byte ) {
81
- l .Logger .LogAttrs (slog .LevelInfo , "" ,
87
+ func (l * StructuredLoggerEntry ) Panic (v any , stack []byte ) {
88
+ l .Logger .LogAttrs (l . ctx , slog .LevelInfo , "" ,
82
89
slog .String ("stack" , string (stack )),
83
90
slog .String ("panic" , fmt .Sprintf ("%+v" , v )),
84
91
)
85
92
}
86
-
87
- // Helper methods used by the application to get the request-scoped
88
- // logger entry and set additional fields between handlers.
89
- //
90
- // This is a useful pattern to use to set state on the entry as it
91
- // passes through the handler chain, which at any point can be logged
92
- // with a call to .Print(), .Info(), etc.
93
-
94
- func GetLogEntry (r * http.Request ) * slog.Logger {
95
- entry := middleware .GetLogEntry (r ).(* StructuredLoggerEntry )
96
- return entry .Logger
97
- }
98
-
99
- func LogEntrySetField (r * http.Request , key string , value interface {}) {
100
- if entry , ok := r .Context ().Value (middleware .LogEntryCtxKey ).(* StructuredLoggerEntry ); ok {
101
- entry .Logger = entry .Logger .With (key , value )
102
- }
103
- }
104
-
105
- func LogEntrySetFields (r * http.Request , fields map [string ]interface {}) {
106
- if entry , ok := r .Context ().Value (middleware .LogEntryCtxKey ).(* StructuredLoggerEntry ); ok {
107
- for k , v := range fields {
108
- entry .Logger = entry .Logger .With (k , v )
109
- }
110
- }
111
- }
0 commit comments