github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/go-kit/kit/log/log.go (about)

     1  package log
     2  
     3  import "errors"
     4  
     5  // Logger is the fundamental interface for all log operations. Log creates a
     6  // log event from keyvals, a variadic sequence of alternating keys and values.
     7  // Implementations must be safe for concurrent use by multiple goroutines. In
     8  // particular, any implementation of Logger that appends to keyvals or
     9  // modifies or retains any of its elements must make a copy first.
    10  type Logger interface {
    11  	Log(keyvals ...interface{}) error
    12  }
    13  
    14  // ErrMissingValue is appended to keyvals slices with odd length to substitute
    15  // the missing value.
    16  var ErrMissingValue = errors.New("(MISSING)")
    17  
    18  // With returns a new contextual logger with keyvals prepended to those passed
    19  // to calls to Log. If logger is also a contextual logger created by With or
    20  // WithPrefix, keyvals is appended to the existing context.
    21  //
    22  // The returned Logger replaces all value elements (odd indexes) containing a
    23  // Valuer with their generated value for each call to its Log method.
    24  func With(logger Logger, keyvals ...interface{}) Logger {
    25  	if len(keyvals) == 0 {
    26  		return logger
    27  	}
    28  	l := newContext(logger)
    29  	kvs := append(l.keyvals, keyvals...)
    30  	if len(kvs)%2 != 0 {
    31  		kvs = append(kvs, ErrMissingValue)
    32  	}
    33  	return &context{
    34  		logger: l.logger,
    35  		// Limiting the capacity of the stored keyvals ensures that a new
    36  		// backing array is created if the slice must grow in Log or With.
    37  		// Using the extra capacity without copying risks a data race that
    38  		// would violate the Logger interface contract.
    39  		keyvals:   kvs[:len(kvs):len(kvs)],
    40  		hasValuer: l.hasValuer || containsValuer(keyvals),
    41  	}
    42  }
    43  
    44  // WithPrefix returns a new contextual logger with keyvals prepended to those
    45  // passed to calls to Log. If logger is also a contextual logger created by
    46  // With or WithPrefix, keyvals is prepended to the existing context.
    47  //
    48  // The returned Logger replaces all value elements (odd indexes) containing a
    49  // Valuer with their generated value for each call to its Log method.
    50  func WithPrefix(logger Logger, keyvals ...interface{}) Logger {
    51  	if len(keyvals) == 0 {
    52  		return logger
    53  	}
    54  	l := newContext(logger)
    55  	// Limiting the capacity of the stored keyvals ensures that a new
    56  	// backing array is created if the slice must grow in Log or With.
    57  	// Using the extra capacity without copying risks a data race that
    58  	// would violate the Logger interface contract.
    59  	n := len(l.keyvals) + len(keyvals)
    60  	if len(keyvals)%2 != 0 {
    61  		n++
    62  	}
    63  	kvs := make([]interface{}, 0, n)
    64  	kvs = append(kvs, keyvals...)
    65  	if len(kvs)%2 != 0 {
    66  		kvs = append(kvs, ErrMissingValue)
    67  	}
    68  	kvs = append(kvs, l.keyvals...)
    69  	return &context{
    70  		logger:    l.logger,
    71  		keyvals:   kvs,
    72  		hasValuer: l.hasValuer || containsValuer(keyvals),
    73  	}
    74  }
    75  
    76  // context is the Logger implementation returned by With and WithPrefix. It
    77  // wraps a Logger and holds keyvals that it includes in all log events. Its
    78  // Log method calls bindValues to generate values for each Valuer in the
    79  // context keyvals.
    80  //
    81  // A context must always have the same number of stack frames between calls to
    82  // its Log method and the eventual binding of Valuers to their value. This
    83  // requirement comes from the functional requirement to allow a context to
    84  // resolve application call site information for a Caller stored in the
    85  // context. To do this we must be able to predict the number of logging
    86  // functions on the stack when bindValues is called.
    87  //
    88  // Two implementation details provide the needed stack depth consistency.
    89  //
    90  //    1. newContext avoids introducing an additional layer when asked to
    91  //       wrap another context.
    92  //    2. With and WithPrefix avoid introducing an additional layer by
    93  //       returning a newly constructed context with a merged keyvals rather
    94  //       than simply wrapping the existing context.
    95  type context struct {
    96  	logger    Logger
    97  	keyvals   []interface{}
    98  	hasValuer bool
    99  }
   100  
   101  func newContext(logger Logger) *context {
   102  	if c, ok := logger.(*context); ok {
   103  		return c
   104  	}
   105  	return &context{logger: logger}
   106  }
   107  
   108  // Log replaces all value elements (odd indexes) containing a Valuer in the
   109  // stored context with their generated value, appends keyvals, and passes the
   110  // result to the wrapped Logger.
   111  func (l *context) Log(keyvals ...interface{}) error {
   112  	kvs := append(l.keyvals, keyvals...)
   113  	if len(kvs)%2 != 0 {
   114  		kvs = append(kvs, ErrMissingValue)
   115  	}
   116  	if l.hasValuer {
   117  		// If no keyvals were appended above then we must copy l.keyvals so
   118  		// that future log events will reevaluate the stored Valuers.
   119  		if len(keyvals) == 0 {
   120  			kvs = append([]interface{}{}, l.keyvals...)
   121  		}
   122  		bindValues(kvs[:len(l.keyvals)])
   123  	}
   124  	return l.logger.Log(kvs...)
   125  }
   126  
   127  // LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If
   128  // f is a function with the appropriate signature, LoggerFunc(f) is a Logger
   129  // object that calls f.
   130  type LoggerFunc func(...interface{}) error
   131  
   132  // Log implements Logger by calling f(keyvals...).
   133  func (f LoggerFunc) Log(keyvals ...interface{}) error {
   134  	return f(keyvals...)
   135  }