khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 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 | |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 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. |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 38 | type Logger interface { |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 39 | GetMethodLogger(methodName string) MethodLogger |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 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 | // |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 45 | // It is used to get a MethodLogger for each individual method. |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 46 | var binLogger Logger |
| 47 | |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 48 | // SetLogger sets the binary logger. |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 49 | // |
| 50 | // Only call this at init time. |
| 51 | func SetLogger(l Logger) { |
| 52 | binLogger = l |
| 53 | } |
| 54 | |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 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. |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 63 | // |
| 64 | // methodName should be in the format of "/service/method". |
| 65 | // |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 66 | // Each MethodLogger returned by this method is a new instance. This is to |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 67 | // generate sequence id within the call. |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 68 | func GetMethodLogger(methodName string) MethodLogger { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 69 | if binLogger == nil { |
| 70 | return nil |
| 71 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 72 | return binLogger.GetMethodLogger(methodName) |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | func init() { |
| 76 | const envStr = "GRPC_BINARY_LOG_FILTER" |
| 77 | configStr := os.Getenv(envStr) |
| 78 | binLogger = NewLoggerFromConfigString(configStr) |
| 79 | } |
| 80 | |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 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 { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 84 | // Max length of header and message. |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 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{} |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 95 | } |
| 96 | |
| 97 | type logger struct { |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 98 | config LoggerConfig |
| 99 | } |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 100 | |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 101 | // NewLoggerFromConfig builds a logger with the given LoggerConfig. |
| 102 | func NewLoggerFromConfig(config LoggerConfig) Logger { |
| 103 | return &logger{config: config} |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 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 "*". |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 113 | func (l *logger) setDefaultMethodLogger(ml *MethodLoggerConfig) error { |
| 114 | if l.config.All != nil { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 115 | return fmt.Errorf("conflicting global rules found") |
| 116 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 117 | l.config.All = ml |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 118 | return nil |
| 119 | } |
| 120 | |
| 121 | // Set method logger for "service/*". |
| 122 | // |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 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 { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 126 | return fmt.Errorf("conflicting service rules for service %v found", service) |
| 127 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 128 | if l.config.Services == nil { |
| 129 | l.config.Services = make(map[string]*MethodLoggerConfig) |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 130 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 131 | l.config.Services[service] = ml |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 132 | return nil |
| 133 | } |
| 134 | |
| 135 | // Set method logger for "service/method". |
| 136 | // |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 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 { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 140 | return fmt.Errorf("conflicting blacklist rules for method %v found", method) |
| 141 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 142 | if _, ok := l.config.Methods[method]; ok { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 143 | return fmt.Errorf("conflicting method rules for method %v found", method) |
| 144 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 145 | if l.config.Methods == nil { |
| 146 | l.config.Methods = make(map[string]*MethodLoggerConfig) |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 147 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 148 | l.config.Methods[method] = ml |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 149 | return nil |
| 150 | } |
| 151 | |
| 152 | // Set blacklist method for "-service/method". |
| 153 | func (l *logger) setBlacklist(method string) error { |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 154 | if _, ok := l.config.Blacklist[method]; ok { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 155 | return fmt.Errorf("conflicting blacklist rules for method %v found", method) |
| 156 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 157 | if _, ok := l.config.Methods[method]; ok { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 158 | return fmt.Errorf("conflicting method rules for method %v found", method) |
| 159 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 160 | if l.config.Blacklist == nil { |
| 161 | l.config.Blacklist = make(map[string]struct{}) |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 162 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 163 | l.config.Blacklist[method] = struct{}{} |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 164 | return nil |
| 165 | } |
| 166 | |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 167 | // getMethodLogger returns the MethodLogger for the given methodName. |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 168 | // |
| 169 | // methodName should be in the format of "/service/method". |
| 170 | // |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 171 | // Each MethodLogger returned by this method is a new instance. This is to |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 172 | // generate sequence id within the call. |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 173 | func (l *logger) GetMethodLogger(methodName string) MethodLogger { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 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 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 179 | if ml, ok := l.config.Methods[s+"/"+m]; ok { |
| 180 | return NewTruncatingMethodLogger(ml.Header, ml.Message) |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 181 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 182 | if _, ok := l.config.Blacklist[s+"/"+m]; ok { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 183 | return nil |
| 184 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 185 | if ml, ok := l.config.Services[s]; ok { |
| 186 | return NewTruncatingMethodLogger(ml.Header, ml.Message) |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 187 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 188 | if l.config.All == nil { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 189 | return nil |
| 190 | } |
Akash Kankanala | 761955c | 2024-02-21 19:32:20 +0530 | [diff] [blame^] | 191 | return NewTruncatingMethodLogger(l.config.All.Header, l.config.All.Message) |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 192 | } |