-
Notifications
You must be signed in to change notification settings - Fork 0
/
slog.go
119 lines (99 loc) · 2.7 KB
/
slog.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
package log
import (
"context"
"log/slog"
"net/http"
"strings"
"time"
"github.com/99designs/gqlgen/graphql"
"github.com/go-chi/chi/middleware"
)
// NewSLogChiMiddleware is used to log http request information. It takes
// a pointer to an slog.Logger to use. If `l` is nil, it uses the
// default logger
func NewSLogChiMiddleware(l *slog.Logger) func(http.Handler) http.Handler {
if l == nil {
l = slog.Default()
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
defer func(start time.Time) {
l.LogAttrs(
r.Context(),
slog.LevelInfo,
"HTTP Request Served",
slog.String("proto", r.Proto),
slog.String("path", r.URL.Path),
slog.Duration("duration", time.Since(start)),
slog.Int("status", ww.Status()),
slog.Int("size", ww.BytesWritten()),
slog.String("ip", r.RemoteAddr),
)
}(time.Now())
next.ServeHTTP(ww, r)
})
}
}
type VariablesScrubber interface {
Scrub(map[string]any) map[string]any
}
type noopVariablesScrubber struct{}
var _ VariablesScrubber = (*noopVariablesScrubber)(nil)
func (noopVariablesScrubber) Scrub(vars map[string]any) map[string]any {
return nil
}
var _ VariablesScrubber = (*DevScrubber)(nil)
// DevScrubber is a VariablesScrubber that passes all var information from
// GraphQL variables. It is intended for use in development environments.
type DevScrubber struct{}
func (DevScrubber) Scrub(vars map[string]any) map[string]any {
return vars
}
// NewSLogGraphQLResponseMiddleware is used to log GraphQL requests and responses.
func NewSLogGraphQLResponseMiddleware(l *slog.Logger, s VariablesScrubber) graphql.ResponseMiddleware {
if l == nil {
l = slog.Default()
}
if s == nil {
s = noopVariablesScrubber{}
}
return func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
var (
start = time.Now()
res = next(ctx)
oc = graphql.GetOperationContext(ctx)
)
if !strings.Contains(oc.RawQuery, "__ApolloGetServiceDefinition__") {
l.LogAttrs(
ctx,
slog.LevelInfo,
"GraphQL Request Served",
slog.Group(
"graphql",
slog.Group(
"req",
slog.String("query", oc.RawQuery),
slog.Any("variables", s.Scrub(oc.Variables)),
),
slog.Group(
"res",
slog.String("errors", res.Errors.Error()),
),
slog.Duration("duration", time.Since(start)),
),
)
}
return res
}
}
func SLogReplaceAttr(_ []string, a slog.Attr) slog.Attr {
switch a.Value.Kind() {
case slog.KindAny:
switch v := a.Value.Any().(type) {
case error:
a.Value = slog.GroupValue(slog.String("message", v.Error()))
}
}
return a
}