forked from mercari/go-grpc-interceptor
/
dump.go
131 lines (114 loc) · 2.58 KB
/
dump.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
package requestdump
import (
"bytes"
"fmt"
"net"
"strings"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"github.com/uber-go/zap"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/transport"
)
func init() {
marshaler = jsonpb.Marshaler{
EnumsAsInts: false,
EmitDefaults: true,
OrigName: true,
}
}
var marshaler jsonpb.Marshaler
type protoMessage struct {
msg proto.Message
}
func (m protoMessage) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
if err := marshaler.Marshal(&buf, m.msg); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
type metadataMarshaller metadata.MD
func (mm metadataMarshaller) MarshalLog(kv zap.KeyValue) error {
for k, v := range mm {
// TODO: array handling
if len(v) == 0 {
continue
}
value := v[0]
if strings.HasSuffix(k, "-bin") {
zap.Base64(k, []byte(value)).AddTo(kv)
} else {
kv.AddString(k, value)
}
}
return nil
}
func dump(ctx context.Context, opts options, logger zap.Logger, info *grpc.UnaryServerInfo, request bool, msg interface{}, err error) {
direction := "request"
code := zap.Skip()
header := zap.Skip()
trailer := zap.Skip()
if request {
if md, ok := metadata.FromContext(ctx); ok {
header = zap.Marshaler("header", metadataMarshaller(md))
}
} else {
direction = "response"
code = zap.String("code", grpc.Code(err).String())
// NOTE: not sure this is public API, will be broken in futre
if stream, ok := transport.StreamFromContext(ctx); ok {
md := stream.Trailer()
trailer = zap.Marshaler("trailer", metadataMarshaller(md))
}
// NOTE: there is no API to get response header
}
var addr string
if pr, ok := peer.FromContext(ctx); ok {
if tcpAddr, ok := pr.Addr.(*net.TCPAddr); ok {
addr = tcpAddr.IP.String()
} else {
addr = pr.Addr.String()
}
}
fields := []zap.Field{
zap.String("method", info.FullMethod),
zap.String("direction", direction),
zap.String("addr", addr),
code,
header,
trailer,
}
if err != nil {
logger.Info("request dump",
zap.Nest(opts.rootKey,
append(fields,
zap.String("error", err.Error()),
)...,
),
)
return
}
protoMsg, ok := msg.(proto.Message)
if !ok {
logger.Info("request dump",
zap.Nest(opts.rootKey,
append(fields,
zap.String("error", fmt.Sprintf("not proto.Message: %v", msg)),
)...,
),
)
return
}
logger.Info("request dump",
zap.Nest(opts.rootKey,
append(fields,
zap.Object("message", protoMessage{msg: protoMsg}),
)...,
),
)
return
}