-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlogger.go
168 lines (134 loc) · 4.07 KB
/
logger.go
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//go:build go1.21
package middlewares
import (
"context"
"io"
"log/slog"
"os"
"time"
"github.com/mojixcoder/kid"
)
type (
// LoggerConfig is the config used to build logger middleware.
LoggerConfig struct {
// Logger is the logger instance.
// Optional. If set, Out, Level and Type configs won't be used.
Logger *slog.Logger
// Out is the writer that logs will be written at.
// Defaults to os.Stdout.
Out io.Writer
// Level is the log level used for initializing a logger instance.
// Defaults to slog.LevelInfo.
Level slog.Leveler
// SuccessLevel is the log level when status code < 400.
// Defaults to slog.LevelInfo.
SuccessLevel slog.Leveler
// ClientErrorLevel is the log level when status code is between 400 and 499.
// Defaults to slog.LevelWarn.
ClientErrorLevel slog.Leveler
// ServerErrorLevel is the log level when status code >= 500.
// Defaults to slog.LevelError.
ServerErrorLevel slog.Leveler
// Type is the logger type.
// Defaults to JSON.
Type LoggerType
// Skipper is a function used for skipping middleware execution.
// Defaults to nil.
Skipper func(c *kid.Context) bool
}
// LoggerType is the type for specifying logger type.
LoggerType string
)
const (
// JSONLogger is the JSON logger type.
TypeJSON LoggerType = "JSON"
// TextLogger is the text logger type.
TypeText LoggerType = "TEXT"
)
// DefaultLoggerConfig is the default logger config.
var DefaultLoggerConfig = LoggerConfig{
Out: os.Stdout,
Level: slog.LevelInfo,
SuccessLevel: slog.LevelInfo,
ClientErrorLevel: slog.LevelWarn,
ServerErrorLevel: slog.LevelError,
Type: TypeJSON,
}
// NewLogger returns a new logger middleware.
func NewLogger() kid.MiddlewareFunc {
return NewLoggerWithConfig(DefaultLoggerConfig)
}
// NewLoggerWithConfig returns a new logger middleware with the given config.
func NewLoggerWithConfig(cfg LoggerConfig) kid.MiddlewareFunc {
setLoggerDefaults(&cfg)
logger := cfg.getLogger()
successLvl := cfg.SuccessLevel.Level()
clientErrLvl := cfg.ClientErrorLevel.Level()
serverErrLvl := cfg.ServerErrorLevel.Level()
return func(next kid.HandlerFunc) kid.HandlerFunc {
return func(c *kid.Context) {
// Skip if necessary.
if cfg.Skipper != nil && cfg.Skipper(c) {
next(c)
return
}
start := time.Now()
next(c)
end := time.Now()
elapsed := end.Sub(start)
status := c.Response().Status()
attrs := []slog.Attr{
slog.Time("time", end),
slog.Int64("latency_ms", elapsed.Milliseconds()),
slog.String("latency", elapsed.String()),
slog.Int("status", status),
slog.String("route", c.Route()),
slog.String("path", c.Path()),
slog.String("method", c.Method()),
slog.String("user_agent", c.GetRequestHeader("User-Agent")),
}
if status < 400 {
logger.LogAttrs(context.Background(), successLvl, "SUCCESS", attrs...)
} else if status <= 499 {
logger.LogAttrs(context.Background(), clientErrLvl, "CLIENT ERROR", attrs...)
} else { // 5xx status codes.
logger.LogAttrs(context.Background(), serverErrLvl, "SERVER ERROR", attrs...)
}
}
}
}
// getLogger returns the appropriate logger instance.
func (cfg LoggerConfig) getLogger() *slog.Logger {
if cfg.Logger != nil {
return cfg.Logger
}
switch cfg.Type {
case TypeJSON:
return slog.New(slog.NewJSONHandler(cfg.Out, &slog.HandlerOptions{Level: cfg.Level}))
case TypeText:
return slog.New(slog.NewTextHandler(cfg.Out, &slog.HandlerOptions{Level: cfg.Level}))
default:
panic("invalid logger type")
}
}
// setLoggerDefaults sets logger default values.
func setLoggerDefaults(cfg *LoggerConfig) {
if cfg.Out == nil {
cfg.Out = DefaultLoggerConfig.Out
}
if cfg.Level == nil {
cfg.Level = DefaultLoggerConfig.Level
}
if cfg.SuccessLevel == nil {
cfg.SuccessLevel = DefaultLoggerConfig.SuccessLevel
}
if cfg.ClientErrorLevel == nil {
cfg.ClientErrorLevel = DefaultLoggerConfig.ClientErrorLevel
}
if cfg.ServerErrorLevel == nil {
cfg.ServerErrorLevel = DefaultLoggerConfig.ServerErrorLevel
}
if cfg.Type == "" {
cfg.Type = DefaultLoggerConfig.Type
}
}