1 /*
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.
21 package binarylog
22 23 import (
24 "fmt"
25 "os"
26 27 "google.golang.org/grpc/grpclog"
28 "google.golang.org/grpc/internal/grpcutil"
29 )
30 31 var grpclogLogger = grpclog.Component("binarylog")
32 33 // Logger specifies MethodLoggers for method names with a Log call that
34 // takes a context.
35 //
36 // This is used in the 1.0 release of gcp/observability, and thus must not be
37 // deleted or changed.
38 type Logger interface {
39 GetMethodLogger(methodName string) MethodLogger
40 }
41 42 // binLogger is the global binary logger for the binary. One of this should be
43 // built at init time from the configuration (environment variable or flags).
44 //
45 // It is used to get a MethodLogger for each individual method.
46 var binLogger Logger
47 48 // SetLogger sets the binary logger.
49 //
50 // Only call this at init time.
51 func SetLogger(l Logger) {
52 binLogger = l
53 }
54 55 // GetLogger gets the binary logger.
56 //
57 // Only call this at init time.
58 func GetLogger() Logger {
59 return binLogger
60 }
61 62 // GetMethodLogger returns the MethodLogger for the given methodName.
63 //
64 // methodName should be in the format of "/service/method".
65 //
66 // Each MethodLogger returned by this method is a new instance. This is to
67 // generate sequence id within the call.
68 func GetMethodLogger(methodName string) MethodLogger {
69 if binLogger == nil {
70 return nil
71 }
72 return binLogger.GetMethodLogger(methodName)
73 }
74 75 func init() {
76 const envStr = "GRPC_BINARY_LOG_FILTER"
77 configStr := os.Getenv(envStr)
78 binLogger = NewLoggerFromConfigString(configStr)
79 }
80 81 // MethodLoggerConfig contains the setting for logging behavior of a method
82 // logger. Currently, it contains the max length of header and message.
83 type MethodLoggerConfig struct {
84 // Max length of header and message.
85 Header, Message uint64
86 }
87 88 // LoggerConfig contains the config for loggers to create method loggers.
89 type LoggerConfig struct {
90 All *MethodLoggerConfig
91 Services map[string]*MethodLoggerConfig
92 Methods map[string]*MethodLoggerConfig
93 94 Blacklist map[string]struct{}
95 }
96 97 type logger struct {
98 config LoggerConfig
99 }
100 101 // NewLoggerFromConfig builds a logger with the given LoggerConfig.
102 func NewLoggerFromConfig(config LoggerConfig) Logger {
103 return &logger{config: config}
104 }
105 106 // newEmptyLogger creates an empty logger. The map fields need to be filled in
107 // using the set* functions.
108 func newEmptyLogger() *logger {
109 return &logger{}
110 }
111 112 // Set method logger for "*".
113 func (l *logger) setDefaultMethodLogger(ml *MethodLoggerConfig) error {
114 if l.config.All != nil {
115 return fmt.Errorf("conflicting global rules found")
116 }
117 l.config.All = ml
118 return nil
119 }
120 121 // Set method logger for "service/*".
122 //
123 // New MethodLogger with same service overrides the old one.
124 func (l *logger) setServiceMethodLogger(service string, ml *MethodLoggerConfig) error {
125 if _, ok := l.config.Services[service]; ok {
126 return fmt.Errorf("conflicting service rules for service %v found", service)
127 }
128 if l.config.Services == nil {
129 l.config.Services = make(map[string]*MethodLoggerConfig)
130 }
131 l.config.Services[service] = ml
132 return nil
133 }
134 135 // Set method logger for "service/method".
136 //
137 // New MethodLogger with same method overrides the old one.
138 func (l *logger) setMethodMethodLogger(method string, ml *MethodLoggerConfig) error {
139 if _, ok := l.config.Blacklist[method]; ok {
140 return fmt.Errorf("conflicting blacklist rules for method %v found", method)
141 }
142 if _, ok := l.config.Methods[method]; ok {
143 return fmt.Errorf("conflicting method rules for method %v found", method)
144 }
145 if l.config.Methods == nil {
146 l.config.Methods = make(map[string]*MethodLoggerConfig)
147 }
148 l.config.Methods[method] = ml
149 return nil
150 }
151 152 // Set blacklist method for "-service/method".
153 func (l *logger) setBlacklist(method string) error {
154 if _, ok := l.config.Blacklist[method]; ok {
155 return fmt.Errorf("conflicting blacklist rules for method %v found", method)
156 }
157 if _, ok := l.config.Methods[method]; ok {
158 return fmt.Errorf("conflicting method rules for method %v found", method)
159 }
160 if l.config.Blacklist == nil {
161 l.config.Blacklist = make(map[string]struct{})
162 }
163 l.config.Blacklist[method] = struct{}{}
164 return nil
165 }
166 167 // getMethodLogger returns the MethodLogger for the given methodName.
168 //
169 // methodName should be in the format of "/service/method".
170 //
171 // Each MethodLogger returned by this method is a new instance. This is to
172 // generate sequence id within the call.
173 func (l *logger) GetMethodLogger(methodName string) MethodLogger {
174 s, m, err := grpcutil.ParseMethod(methodName)
175 if err != nil {
176 grpclogLogger.Infof("binarylogging: failed to parse %q: %v", methodName, err)
177 return nil
178 }
179 if ml, ok := l.config.Methods[s+"/"+m]; ok {
180 return NewTruncatingMethodLogger(ml.Header, ml.Message)
181 }
182 if _, ok := l.config.Blacklist[s+"/"+m]; ok {
183 return nil
184 }
185 if ml, ok := l.config.Services[s]; ok {
186 return NewTruncatingMethodLogger(ml.Header, ml.Message)
187 }
188 if l.config.All == nil {
189 return nil
190 }
191 return NewTruncatingMethodLogger(l.config.All.Header, l.config.All.Message)
192 }
193