/
logger_zap.go
118 lines (100 loc) · 3.56 KB
/
logger_zap.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
package router
import (
"context"
"log"
"net/http"
"os"
"time"
chimiddleware "github.com/go-chi/chi/v5/middleware"
gorillacontext "github.com/gorilla/context"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var (
// ZapRequestLogger is called by the Logger middleware handler to log each request.
// Its made a package-level variable so that it can be reconfigured for custom
// logging configurations.
ZapRequestLogger = CustomZapRequestLogger(&CustomZapLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: false})
)
// CustomZapLogger is a middleware that logs the start and end of each request, along
// with some useful data about what was requested, what the response status was,
// and how long it took to return. When standard output is a TTY, Logger will
// print in color, otherwise it will print in black and white. Logger prints a
// request ID if one is provided.
//
// Alternatively, look at https://github.com/pressly/lg and the `lg.RequestLogger`
// middleware pkg.
func CustomZapLogger(next http.Handler) http.Handler {
return ZapRequestLogger(next)
}
// CustomZapRequestLogger returns a logger handler using a custom LogFormatter.
func CustomZapRequestLogger(f chimiddleware.LogFormatter) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
entry := f.NewLogEntry(r)
ww := chimiddleware.NewWrapResponseWriter(w, r.ProtoMajor)
t1 := time.Now()
defer func() {
user := gorillacontext.Get(r, UserLogin)
gorillacontext.Clear(r)
zapFields := entry.(*customZapLogEntry).ZapFields
if user != nil {
zapFields = append(zapFields, zap.String("user", user.(string)))
}
zapFields = append(zapFields,
zap.Duration("lat", time.Since(t1)),
zap.Int("status", ww.Status()),
zap.Int("size", ww.BytesWritten()),
)
entry.(*customZapLogEntry).ZapFields = zapFields
entry.Write(ww.Status(), ww.BytesWritten(), ww.Header(), time.Since(t1), nil)
}()
ctx := context.WithValue(r.Context(), ContextKeyLoggerR, r)
next.ServeHTTP(ww, chimiddleware.WithLogEntry(r.WithContext(ctx), entry))
}
return http.HandlerFunc(fn)
}
}
// CustomZapLogFormatter is a simple logger that implements a LogFormatter.
type CustomZapLogFormatter struct {
Logger chimiddleware.LoggerInterface
NoColor bool
}
// NewLogEntry creates a new LogEntry for the request.
func (l *CustomZapLogFormatter) NewLogEntry(r *http.Request) chimiddleware.LogEntry {
entry := &customZapLogEntry{
CustomZapLogFormatter: l,
request: r,
ZapLogger: zap.L(),
ZapFields: make([]zapcore.Field, 0),
}
reqID := chimiddleware.GetReqID(r.Context())
if reqID != "" {
entry.ZapFields = append(entry.ZapFields, zap.String("requestid", reqID))
}
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
entry.ZapFields = append(entry.ZapFields,
zap.String("method", r.Method),
zap.String("scheme", scheme),
zap.String("host", r.Host),
zap.String("path", r.RequestURI),
zap.String("proto", r.Proto),
zap.String("remoteaddr", r.RemoteAddr),
)
return entry
}
type customZapLogEntry struct {
*CustomZapLogFormatter
request *http.Request
ZapLogger *zap.Logger
ZapFields []zap.Field
}
func (l *customZapLogEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) {
l.ZapLogger.Info("request served", l.ZapFields...)
}
func (l *customZapLogEntry) Panic(v interface{}, stack []byte) {
l.ZapLogger.Panic("request served", zap.Any("reason", v), zap.String("stack", string(stack)))
}