Go 1.21 在标准库log/slog
包中新增了对结构化日志的支持。
1
2
3
4
| slog.Debug("S")
slog.Info(" L")
slog.Warn(" O")
slog.Error("G")
|
1
2
3
| slog.Info("hello world", "time", time.Now().Unix())
2024/01/03 22:05:04 INFO hello world time=1704290704
|
改变slog默认logger的格式,使用JSONHandler
输出json对象。
1
2
3
4
5
6
| logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)
slog.Info("hello world", "unix_time", time.Now().Unix())
{"time":"2024-01-03T22:11:24.739111+08:00","level":"INFO","msg":"hello world","unix_time":1704291084}
|
HandlerOptions
提供了我们可以用来修改内置TextHandler
和JSONHandler
的 main hooks。
1
2
3
| opts := &slog.HandlerOptions{
Level: slog.LevelWarn,
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| opts := &slog.HandlerOptions{
// Use the ReplaceAttr function on the handler options
// to be able to replace any single attribute in the log output
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// check that we are handling the time key
if a.Key != slog.TimeKey {
return a
}
t := a.Value.Time()
// change the value from a time.Time to a String
// where the string has the correct time format.
a.Value = slog.StringValue(t.Format(time.DateTime))
return a
},
}
logger := slog.New(slog.NewJSONHandler(os.Stdout, opts))
slog.SetDefault(logger)
slog.Info("hello world", "foo", 42)
{"time":"2024-01-03 22:27:53","level":"INFO","msg":"hello world","foo":42}
|
1
2
3
4
5
6
7
8
9
10
11
12
| opts := &slog.HandlerOptions{
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key != slog.MessageKey {
return a
}
// change the key from "msg" to "message"
a.Key = "message"
return a
},
}
|
使用slog.Group
方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| textLogger := slog.New(slog.NewTextHandler(os.Stdout, nil))
jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(textLogger)
slog.Info("outside the group",
// The Group method takes the outer key, then kv pairs
slog.Group("request",
"method", "GET",
"url", "https://berylyvos.icu/",
),
)
slog.SetDefault(jsonLogger)
slog.Info("outside the group",
slog.Group("request",
"method", "GET",
"url", "https://berylyvos.icu/",
),
)
|
textLogger 输出:
1
| time=2024-01-03T22:40:04.665+08:00 level=INFO msg="outside the group" request.method=GET request.url=https://berylyvos.icu/
|
jsonLogger 输出:
1
| {"time":"2024-01-03T22:40:04.665753+08:00","level":"INFO","msg":"outside the group","request":{"method":"GET","url":"https://berylyvos.icu/"}}
|
访问context,需要创建自定义handler。
slog.Handler
接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| type Handler interface {
// Enabled reports if the specific log event should
// be printed for this Handler and the given log level.
// We can also choose to use info from the Context that
// we passed in the InfoContext(...) call. If we used
// Info(...) not InfoContext(...) then the context will
// be context.Background().
Enabled(context.Context, slog.Level) bool
// Handle takes the log record and prints it. The handle
// function has access to the context, so we can extract
// request specific info that we want to include in our
// log event here.
Handle(context.Context, Record) error
// WithAttrs are the key value pairs that we're including in the structured logging
// e.g. user=5
WithAttrs(attrs []Attr) Handler
// WithGroup is a sub-handler, with the group/object name
WithGroup(name string) Handler
}
|
定义包含context的自定义JSON handler:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| // myHandler embeds the anonymous struct member
// *slog.JSONHandler. This gives us method overriding
// where all methods of the handler interface we don't
// implement will be dispatched to the built-in handler
type myHandler struct {
*slog.JSONHandler
}
type contextKey string
const userIDKey contextKey = "userid"
// Handle passes the userID key and value as an extra
// attribute to the standard json handler then calls handle.
func (h *myHandler) Handle(ctx context.Context, r slog.Record) error {
userID := ctx.Value(userIDKey).(string)
return h.
JSONHandler.
WithAttrs([]slog.Attr{slog.String("userID", userID)}).
Handle(ctx, r)
}
func main() {
// make the default json handler
jsonHandler := slog.NewJSONHandler(os.Stdout, nil)
// wrap the default json handler in a handler that logs context
myHandler := &myHandler{JSONHandler: jsonHandler}
logger := slog.New(myHandler)
slog.SetDefault(logger)
// add our context fields
ctx := context.Background()
ctx = context.WithValue(ctx, userIDKey, "14")
// call the context log method
slog.InfoContext(ctx, "hello world", "foo", 42)
}
|
输出携带了context中的userID
作为额外的key:
1
| {"time":"2024-01-03T23:05:22.844931+08:00","level":"INFO","msg":"hello world","userID":"14","foo":42}
|
github.com/zknill/slogmw对log/slog
包进行了封装,让其更易使用。
原文:https://zknill.io/posts/go-1-21-slog/