binarylog.go raw

   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