binarylog.go

  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.
 21package binarylog
 22
 23import (
 24	"fmt"
 25	"os"
 26
 27	"google.golang.org/grpc/grpclog"
 28	"google.golang.org/grpc/internal/grpcutil"
 29)
 30
 31var 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.
 38type 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.
 46var binLogger Logger
 47
 48// SetLogger sets the binary logger.
 49//
 50// Only call this at init time.
 51func SetLogger(l Logger) {
 52	binLogger = l
 53}
 54
 55// GetLogger gets the binary logger.
 56//
 57// Only call this at init time.
 58func 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.
 68func GetMethodLogger(methodName string) MethodLogger {
 69	if binLogger == nil {
 70		return nil
 71	}
 72	return binLogger.GetMethodLogger(methodName)
 73}
 74
 75func 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.
 83type 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.
 89type LoggerConfig struct {
 90	All      *MethodLoggerConfig
 91	Services map[string]*MethodLoggerConfig
 92	Methods  map[string]*MethodLoggerConfig
 93
 94	Blacklist map[string]struct{}
 95}
 96
 97type logger struct {
 98	config LoggerConfig
 99}
100
101// NewLoggerFromConfig builds a logger with the given LoggerConfig.
102func 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.
108func newEmptyLogger() *logger {
109	return &logger{}
110}
111
112// Set method logger for "*".
113func (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.
124func (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.
138func (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".
153func (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.
173func (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}