...
1 package grpc_zap
2
3 import (
4 "context"
5 "time"
6
7 grpc_logging "github.com/grpc-ecosystem/go-grpc-middleware/logging"
8 "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
9 "go.uber.org/zap"
10 "go.uber.org/zap/zapcore"
11 "google.golang.org/grpc/codes"
12 )
13
14 var (
15 defaultOptions = &options{
16 levelFunc: DefaultCodeToLevel,
17 shouldLog: grpc_logging.DefaultDeciderMethod,
18 codeFunc: grpc_logging.DefaultErrorToCode,
19 durationFunc: DefaultDurationToField,
20 messageFunc: DefaultMessageProducer,
21 timestampFormat: time.RFC3339,
22 }
23 )
24
25 type options struct {
26 levelFunc CodeToLevel
27 shouldLog grpc_logging.Decider
28 codeFunc grpc_logging.ErrorToCode
29 durationFunc DurationToField
30 messageFunc MessageProducer
31 timestampFormat string
32 }
33
34 func evaluateServerOpt(opts []Option) *options {
35 optCopy := &options{}
36 *optCopy = *defaultOptions
37 optCopy.levelFunc = DefaultCodeToLevel
38 for _, o := range opts {
39 o(optCopy)
40 }
41 return optCopy
42 }
43
44 func evaluateClientOpt(opts []Option) *options {
45 optCopy := &options{}
46 *optCopy = *defaultOptions
47 optCopy.levelFunc = DefaultClientCodeToLevel
48 for _, o := range opts {
49 o(optCopy)
50 }
51 return optCopy
52 }
53
54 type Option func(*options)
55
56
57 type CodeToLevel func(code codes.Code) zapcore.Level
58
59
60 type DurationToField func(duration time.Duration) zapcore.Field
61
62
63 func WithDecider(f grpc_logging.Decider) Option {
64 return func(o *options) {
65 o.shouldLog = f
66 }
67 }
68
69
70 func WithLevels(f CodeToLevel) Option {
71 return func(o *options) {
72 o.levelFunc = f
73 }
74 }
75
76
77 func WithCodes(f grpc_logging.ErrorToCode) Option {
78 return func(o *options) {
79 o.codeFunc = f
80 }
81 }
82
83
84 func WithDurationField(f DurationToField) Option {
85 return func(o *options) {
86 o.durationFunc = f
87 }
88 }
89
90
91 func WithMessageProducer(f MessageProducer) Option {
92 return func(o *options) {
93 o.messageFunc = f
94 }
95 }
96
97
98 func WithTimestampFormat(format string) Option {
99 return func(o *options) {
100 o.timestampFormat = format
101 }
102 }
103
104
105 func DefaultCodeToLevel(code codes.Code) zapcore.Level {
106 switch code {
107 case codes.OK:
108 return zap.InfoLevel
109 case codes.Canceled:
110 return zap.InfoLevel
111 case codes.Unknown:
112 return zap.ErrorLevel
113 case codes.InvalidArgument:
114 return zap.InfoLevel
115 case codes.DeadlineExceeded:
116 return zap.WarnLevel
117 case codes.NotFound:
118 return zap.InfoLevel
119 case codes.AlreadyExists:
120 return zap.InfoLevel
121 case codes.PermissionDenied:
122 return zap.WarnLevel
123 case codes.Unauthenticated:
124 return zap.InfoLevel
125 case codes.ResourceExhausted:
126 return zap.WarnLevel
127 case codes.FailedPrecondition:
128 return zap.WarnLevel
129 case codes.Aborted:
130 return zap.WarnLevel
131 case codes.OutOfRange:
132 return zap.WarnLevel
133 case codes.Unimplemented:
134 return zap.ErrorLevel
135 case codes.Internal:
136 return zap.ErrorLevel
137 case codes.Unavailable:
138 return zap.WarnLevel
139 case codes.DataLoss:
140 return zap.ErrorLevel
141 default:
142 return zap.ErrorLevel
143 }
144 }
145
146
147 func DefaultClientCodeToLevel(code codes.Code) zapcore.Level {
148 switch code {
149 case codes.OK:
150 return zap.DebugLevel
151 case codes.Canceled:
152 return zap.DebugLevel
153 case codes.Unknown:
154 return zap.InfoLevel
155 case codes.InvalidArgument:
156 return zap.DebugLevel
157 case codes.DeadlineExceeded:
158 return zap.InfoLevel
159 case codes.NotFound:
160 return zap.DebugLevel
161 case codes.AlreadyExists:
162 return zap.DebugLevel
163 case codes.PermissionDenied:
164 return zap.InfoLevel
165 case codes.Unauthenticated:
166 return zap.InfoLevel
167 case codes.ResourceExhausted:
168 return zap.DebugLevel
169 case codes.FailedPrecondition:
170 return zap.DebugLevel
171 case codes.Aborted:
172 return zap.DebugLevel
173 case codes.OutOfRange:
174 return zap.DebugLevel
175 case codes.Unimplemented:
176 return zap.WarnLevel
177 case codes.Internal:
178 return zap.WarnLevel
179 case codes.Unavailable:
180 return zap.WarnLevel
181 case codes.DataLoss:
182 return zap.WarnLevel
183 default:
184 return zap.InfoLevel
185 }
186 }
187
188
189 var DefaultDurationToField = DurationToTimeMillisField
190
191
192 func DurationToTimeMillisField(duration time.Duration) zapcore.Field {
193 return zap.Float32("grpc.time_ms", durationToMilliseconds(duration))
194 }
195
196
197
198 func DurationToDurationField(duration time.Duration) zapcore.Field {
199 return zap.Duration("grpc.duration", duration)
200 }
201
202 func durationToMilliseconds(duration time.Duration) float32 {
203 return float32(duration.Nanoseconds()/1000) / 1000
204 }
205
206
207 type MessageProducer func(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field)
208
209
210 func DefaultMessageProducer(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field) {
211
212 ctxzap.Extract(ctx).Check(level, msg).Write(
213 zap.Error(err),
214 zap.String("grpc.code", code.String()),
215 duration,
216 )
217 }
218
View as plain text