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