1
2
3 package grpc_logrus
4
5 import (
6 "context"
7 "path"
8 "time"
9
10 grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
11 "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
12 "github.com/sirupsen/logrus"
13 "google.golang.org/grpc"
14 )
15
16 var (
17
18 SystemField = "system"
19
20
21 KindField = "span.kind"
22 )
23
24
25 func UnaryServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryServerInterceptor {
26 o := evaluateServerOpt(opts)
27 return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
28 startTime := time.Now()
29 newCtx := newLoggerForCall(ctx, entry, info.FullMethod, startTime, o.timestampFormat)
30
31 resp, err := handler(newCtx, req)
32
33 if !o.shouldLog(info.FullMethod, err) {
34 return resp, err
35 }
36 code := o.codeFunc(err)
37 level := o.levelFunc(code)
38 durField, durVal := o.durationFunc(time.Since(startTime))
39 fields := logrus.Fields{
40 "grpc.code": code.String(),
41 durField: durVal,
42 }
43 if err != nil {
44 fields[logrus.ErrorKey] = err
45 }
46
47 o.messageFunc(newCtx, "finished unary call with code "+code.String(), level, code, err, fields)
48 return resp, err
49 }
50 }
51
52
53 func StreamServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamServerInterceptor {
54 o := evaluateServerOpt(opts)
55 return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
56 startTime := time.Now()
57 newCtx := newLoggerForCall(stream.Context(), entry, info.FullMethod, startTime, o.timestampFormat)
58 wrapped := grpc_middleware.WrapServerStream(stream)
59 wrapped.WrappedContext = newCtx
60
61 err := handler(srv, wrapped)
62
63 if !o.shouldLog(info.FullMethod, err) {
64 return err
65 }
66 code := o.codeFunc(err)
67 level := o.levelFunc(code)
68 durField, durVal := o.durationFunc(time.Since(startTime))
69 fields := logrus.Fields{
70 "grpc.code": code.String(),
71 durField: durVal,
72 }
73
74 o.messageFunc(newCtx, "finished streaming call with code "+code.String(), level, code, err, fields)
75 return err
76 }
77 }
78
79 func newLoggerForCall(ctx context.Context, entry *logrus.Entry, fullMethodString string, start time.Time, timestampFormat string) context.Context {
80 service := path.Dir(fullMethodString)[1:]
81 method := path.Base(fullMethodString)
82 callLog := entry.WithFields(
83 logrus.Fields{
84 SystemField: "grpc",
85 KindField: "server",
86 "grpc.service": service,
87 "grpc.method": method,
88 "grpc.start_time": start.Format(timestampFormat),
89 })
90
91 if d, ok := ctx.Deadline(); ok {
92 callLog = callLog.WithFields(
93 logrus.Fields{
94 "grpc.request.deadline": d.Format(timestampFormat),
95 })
96 }
97
98 callLog = callLog.WithFields(ctxlogrus.Extract(ctx).Data)
99 return ctxlogrus.ToContext(ctx, callLog)
100 }
101
View as plain text