blob: 4062c025c79197d83b7ffed77b76013b933673c4 [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001/*
2 *
3 * Copyright 2018 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19// Package binarylog implementation binary logging as defined in
20// https://github.com/grpc/proposal/blob/master/A16-binary-logging.md.
21package binarylog
22
23import (
24 "fmt"
25 "os"
26
27 "google.golang.org/grpc/grpclog"
28)
29
30// Logger is the global binary logger. It can be used to get binary logger for
31// each method.
32type Logger interface {
33 getMethodLogger(methodName string) *MethodLogger
34}
35
36// binLogger is the global binary logger for the binary. One of this should be
Arjun E K57a7fcb2020-01-30 06:44:45 +000037// built at init time from the configuration (environment variable or flags).
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070038//
39// It is used to get a methodLogger for each individual method.
40var binLogger Logger
41
42// SetLogger sets the binarg logger.
43//
44// Only call this at init time.
45func SetLogger(l Logger) {
46 binLogger = l
47}
48
49// GetMethodLogger returns the methodLogger for the given methodName.
50//
51// methodName should be in the format of "/service/method".
52//
53// Each methodLogger returned by this method is a new instance. This is to
54// generate sequence id within the call.
55func GetMethodLogger(methodName string) *MethodLogger {
56 if binLogger == nil {
57 return nil
58 }
59 return binLogger.getMethodLogger(methodName)
60}
61
62func init() {
63 const envStr = "GRPC_BINARY_LOG_FILTER"
64 configStr := os.Getenv(envStr)
65 binLogger = NewLoggerFromConfigString(configStr)
66}
67
68type methodLoggerConfig struct {
69 // Max length of header and message.
70 hdr, msg uint64
71}
72
73type logger struct {
74 all *methodLoggerConfig
75 services map[string]*methodLoggerConfig
76 methods map[string]*methodLoggerConfig
77
78 blacklist map[string]struct{}
79}
80
81// newEmptyLogger creates an empty logger. The map fields need to be filled in
82// using the set* functions.
83func newEmptyLogger() *logger {
84 return &logger{}
85}
86
87// Set method logger for "*".
88func (l *logger) setDefaultMethodLogger(ml *methodLoggerConfig) error {
89 if l.all != nil {
90 return fmt.Errorf("conflicting global rules found")
91 }
92 l.all = ml
93 return nil
94}
95
96// Set method logger for "service/*".
97//
98// New methodLogger with same service overrides the old one.
99func (l *logger) setServiceMethodLogger(service string, ml *methodLoggerConfig) error {
100 if _, ok := l.services[service]; ok {
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000101 return fmt.Errorf("conflicting rules for service %v found", service)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700102 }
103 if l.services == nil {
104 l.services = make(map[string]*methodLoggerConfig)
105 }
106 l.services[service] = ml
107 return nil
108}
109
110// Set method logger for "service/method".
111//
112// New methodLogger with same method overrides the old one.
113func (l *logger) setMethodMethodLogger(method string, ml *methodLoggerConfig) error {
114 if _, ok := l.blacklist[method]; ok {
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000115 return fmt.Errorf("conflicting rules for method %v found", method)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700116 }
117 if _, ok := l.methods[method]; ok {
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000118 return fmt.Errorf("conflicting rules for method %v found", method)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700119 }
120 if l.methods == nil {
121 l.methods = make(map[string]*methodLoggerConfig)
122 }
123 l.methods[method] = ml
124 return nil
125}
126
127// Set blacklist method for "-service/method".
128func (l *logger) setBlacklist(method string) error {
129 if _, ok := l.blacklist[method]; ok {
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000130 return fmt.Errorf("conflicting rules for method %v found", method)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700131 }
132 if _, ok := l.methods[method]; ok {
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000133 return fmt.Errorf("conflicting rules for method %v found", method)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700134 }
135 if l.blacklist == nil {
136 l.blacklist = make(map[string]struct{})
137 }
138 l.blacklist[method] = struct{}{}
139 return nil
140}
141
142// getMethodLogger returns the methodLogger for the given methodName.
143//
144// methodName should be in the format of "/service/method".
145//
146// Each methodLogger returned by this method is a new instance. This is to
147// generate sequence id within the call.
148func (l *logger) getMethodLogger(methodName string) *MethodLogger {
149 s, m, err := parseMethodName(methodName)
150 if err != nil {
151 grpclog.Infof("binarylogging: failed to parse %q: %v", methodName, err)
152 return nil
153 }
154 if ml, ok := l.methods[s+"/"+m]; ok {
155 return newMethodLogger(ml.hdr, ml.msg)
156 }
157 if _, ok := l.blacklist[s+"/"+m]; ok {
158 return nil
159 }
160 if ml, ok := l.services[s]; ok {
161 return newMethodLogger(ml.hdr, ml.msg)
162 }
163 if l.all == nil {
164 return nil
165 }
166 return newMethodLogger(l.all.hdr, l.all.msg)
167}