forked from grpc-ecosystem/go-grpc-middleware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
options.go
208 lines (186 loc) · 5.81 KB
/
options.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package grpc_zap
import (
"context"
"time"
grpc_logging "github.com/grpc-ecosystem/go-grpc-middleware/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc/codes"
)
var (
defaultOptions = &options{
levelFunc: DefaultCodeToLevel,
shouldLog: grpc_logging.DefaultDeciderMethod,
codeFunc: grpc_logging.DefaultErrorToCode,
durationFunc: DefaultDurationToField,
messageFunc: DefaultMessageProducer,
}
)
type options struct {
levelFunc CodeToLevel
shouldLog grpc_logging.Decider
codeFunc grpc_logging.ErrorToCode
durationFunc DurationToField
messageFunc MessageProducer
}
func evaluateServerOpt(opts []Option) *options {
optCopy := &options{}
*optCopy = *defaultOptions
optCopy.levelFunc = DefaultCodeToLevel
for _, o := range opts {
o(optCopy)
}
return optCopy
}
func evaluateClientOpt(opts []Option) *options {
optCopy := &options{}
*optCopy = *defaultOptions
optCopy.levelFunc = DefaultClientCodeToLevel
for _, o := range opts {
o(optCopy)
}
return optCopy
}
type Option func(*options)
// CodeToLevel function defines the mapping between gRPC return codes and interceptor log level.
type CodeToLevel func(code codes.Code) zapcore.Level
// DurationToField function defines how to produce duration fields for logging
type DurationToField func(duration time.Duration) zapcore.Field
// WithDecider customizes the function for deciding if the gRPC interceptor logs should log.
func WithDecider(f grpc_logging.Decider) Option {
return func(o *options) {
o.shouldLog = f
}
}
// WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements.
func WithLevels(f CodeToLevel) Option {
return func(o *options) {
o.levelFunc = f
}
}
// WithCodes customizes the function for mapping errors to error codes.
func WithCodes(f grpc_logging.ErrorToCode) Option {
return func(o *options) {
o.codeFunc = f
}
}
// WithDurationField customizes the function for mapping request durations to Zap fields.
func WithDurationField(f DurationToField) Option {
return func(o *options) {
o.durationFunc = f
}
}
// WithMessageProducer customizes the function for message formation.
func WithMessageProducer(f MessageProducer) Option {
return func(o *options) {
o.messageFunc = f
}
}
// DefaultCodeToLevel is the default implementation of gRPC return codes and interceptor log level for server side.
func DefaultCodeToLevel(code codes.Code) zapcore.Level {
switch code {
case codes.OK:
return zap.InfoLevel
case codes.Canceled:
return zap.InfoLevel
case codes.Unknown:
return zap.ErrorLevel
case codes.InvalidArgument:
return zap.InfoLevel
case codes.DeadlineExceeded:
return zap.WarnLevel
case codes.NotFound:
return zap.InfoLevel
case codes.AlreadyExists:
return zap.InfoLevel
case codes.PermissionDenied:
return zap.WarnLevel
case codes.Unauthenticated:
return zap.InfoLevel // unauthenticated requests can happen
case codes.ResourceExhausted:
return zap.WarnLevel
case codes.FailedPrecondition:
return zap.WarnLevel
case codes.Aborted:
return zap.WarnLevel
case codes.OutOfRange:
return zap.WarnLevel
case codes.Unimplemented:
return zap.ErrorLevel
case codes.Internal:
return zap.ErrorLevel
case codes.Unavailable:
return zap.WarnLevel
case codes.DataLoss:
return zap.ErrorLevel
default:
return zap.ErrorLevel
}
}
// DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side.
func DefaultClientCodeToLevel(code codes.Code) zapcore.Level {
switch code {
case codes.OK:
return zap.DebugLevel
case codes.Canceled:
return zap.DebugLevel
case codes.Unknown:
return zap.InfoLevel
case codes.InvalidArgument:
return zap.DebugLevel
case codes.DeadlineExceeded:
return zap.InfoLevel
case codes.NotFound:
return zap.DebugLevel
case codes.AlreadyExists:
return zap.DebugLevel
case codes.PermissionDenied:
return zap.InfoLevel
case codes.Unauthenticated:
return zap.InfoLevel // unauthenticated requests can happen
case codes.ResourceExhausted:
return zap.DebugLevel
case codes.FailedPrecondition:
return zap.DebugLevel
case codes.Aborted:
return zap.DebugLevel
case codes.OutOfRange:
return zap.DebugLevel
case codes.Unimplemented:
return zap.WarnLevel
case codes.Internal:
return zap.WarnLevel
case codes.Unavailable:
return zap.WarnLevel
case codes.DataLoss:
return zap.WarnLevel
default:
return zap.InfoLevel
}
}
// DefaultDurationToField is the default implementation of converting request duration to a Zap field.
var DefaultDurationToField = DurationToTimeMillisField
// DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`.
func DurationToTimeMillisField(duration time.Duration) zapcore.Field {
return zap.Float32("grpc.time_ms", durationToMilliseconds(duration))
}
// DurationToDurationField uses a Duration field to log the request duration
// and leaves it up to Zap's encoder settings to determine how that is output.
func DurationToDurationField(duration time.Duration) zapcore.Field {
return zap.Duration("grpc.duration", duration)
}
func durationToMilliseconds(duration time.Duration) float32 {
return float32(duration.Nanoseconds()/1000) / 1000
}
// MessageProducer produces a user defined log message
type MessageProducer func(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field)
// DefaultMessageProducer writes the default message
func DefaultMessageProducer(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field) {
// re-extract logger from newCtx, as it may have extra fields that changed in the holder.
ctxzap.Extract(ctx).Check(level, msg).Write(
zap.Error(err),
zap.String("grpc.code", code.String()),
duration,
)
}