stdr.go raw

   1  /*
   2  Copyright 2019 The logr Authors.
   3  
   4  Licensed under the Apache License, Version 2.0 (the "License");
   5  you may not use this file except in compliance with the License.
   6  You may obtain a copy of the License at
   7  
   8      http://www.apache.org/licenses/LICENSE-2.0
   9  
  10  Unless required by applicable law or agreed to in writing, software
  11  distributed under the License is distributed on an "AS IS" BASIS,
  12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  See the License for the specific language governing permissions and
  14  limitations under the License.
  15  */
  16  
  17  // Package stdr implements github.com/go-logr/logr.Logger in terms of
  18  // Go's standard log package.
  19  package stdr
  20  
  21  import (
  22  	"log"
  23  	"os"
  24  
  25  	"github.com/go-logr/logr"
  26  	"github.com/go-logr/logr/funcr"
  27  )
  28  
  29  // The global verbosity level.  See SetVerbosity().
  30  var globalVerbosity int
  31  
  32  // SetVerbosity sets the global level against which all info logs will be
  33  // compared.  If this is greater than or equal to the "V" of the logger, the
  34  // message will be logged.  A higher value here means more logs will be written.
  35  // The previous verbosity value is returned.  This is not concurrent-safe -
  36  // callers must be sure to call it from only one goroutine.
  37  func SetVerbosity(v int) int {
  38  	old := globalVerbosity
  39  	globalVerbosity = v
  40  	return old
  41  }
  42  
  43  // New returns a logr.Logger which is implemented by Go's standard log package,
  44  // or something like it.  If std is nil, this will use a default logger
  45  // instead.
  46  //
  47  // Example: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)))
  48  func New(std StdLogger) logr.Logger {
  49  	return NewWithOptions(std, Options{})
  50  }
  51  
  52  // NewWithOptions returns a logr.Logger which is implemented by Go's standard
  53  // log package, or something like it.  See New for details.
  54  func NewWithOptions(std StdLogger, opts Options) logr.Logger {
  55  	if std == nil {
  56  		// Go's log.Default() is only available in 1.16 and higher.
  57  		std = log.New(os.Stderr, "", log.LstdFlags)
  58  	}
  59  
  60  	if opts.Depth < 0 {
  61  		opts.Depth = 0
  62  	}
  63  
  64  	fopts := funcr.Options{
  65  		LogCaller: funcr.MessageClass(opts.LogCaller),
  66  	}
  67  
  68  	sl := &logger{
  69  		Formatter: funcr.NewFormatter(fopts),
  70  		std:       std,
  71  	}
  72  
  73  	// For skipping our own logger.Info/Error.
  74  	sl.Formatter.AddCallDepth(1 + opts.Depth)
  75  
  76  	return logr.New(sl)
  77  }
  78  
  79  // Options carries parameters which influence the way logs are generated.
  80  type Options struct {
  81  	// Depth biases the assumed number of call frames to the "true" caller.
  82  	// This is useful when the calling code calls a function which then calls
  83  	// stdr (e.g. a logging shim to another API).  Values less than zero will
  84  	// be treated as zero.
  85  	Depth int
  86  
  87  	// LogCaller tells stdr to add a "caller" key to some or all log lines.
  88  	// Go's log package has options to log this natively, too.
  89  	LogCaller MessageClass
  90  
  91  	// TODO: add an option to log the date/time
  92  }
  93  
  94  // MessageClass indicates which category or categories of messages to consider.
  95  type MessageClass int
  96  
  97  const (
  98  	// None ignores all message classes.
  99  	None MessageClass = iota
 100  	// All considers all message classes.
 101  	All
 102  	// Info only considers info messages.
 103  	Info
 104  	// Error only considers error messages.
 105  	Error
 106  )
 107  
 108  // StdLogger is the subset of the Go stdlib log.Logger API that is needed for
 109  // this adapter.
 110  type StdLogger interface {
 111  	// Output is the same as log.Output and log.Logger.Output.
 112  	Output(calldepth int, logline string) error
 113  }
 114  
 115  type logger struct {
 116  	funcr.Formatter
 117  	std StdLogger
 118  }
 119  
 120  var _ logr.LogSink = &logger{}
 121  var _ logr.CallDepthLogSink = &logger{}
 122  
 123  func (l logger) Enabled(level int) bool {
 124  	return globalVerbosity >= level
 125  }
 126  
 127  func (l logger) Info(level int, msg string, kvList ...interface{}) {
 128  	prefix, args := l.FormatInfo(level, msg, kvList)
 129  	if prefix != "" {
 130  		args = prefix + ": " + args
 131  	}
 132  	_ = l.std.Output(l.Formatter.GetDepth()+1, args)
 133  }
 134  
 135  func (l logger) Error(err error, msg string, kvList ...interface{}) {
 136  	prefix, args := l.FormatError(err, msg, kvList)
 137  	if prefix != "" {
 138  		args = prefix + ": " + args
 139  	}
 140  	_ = l.std.Output(l.Formatter.GetDepth()+1, args)
 141  }
 142  
 143  func (l logger) WithName(name string) logr.LogSink {
 144  	l.Formatter.AddName(name)
 145  	return &l
 146  }
 147  
 148  func (l logger) WithValues(kvList ...interface{}) logr.LogSink {
 149  	l.Formatter.AddValues(kvList)
 150  	return &l
 151  }
 152  
 153  func (l logger) WithCallDepth(depth int) logr.LogSink {
 154  	l.Formatter.AddCallDepth(depth)
 155  	return &l
 156  }
 157  
 158  // Underlier exposes access to the underlying logging implementation.  Since
 159  // callers only have a logr.Logger, they have to know which implementation is
 160  // in use, so this interface is less of an abstraction and more of way to test
 161  // type conversion.
 162  type Underlier interface {
 163  	GetUnderlying() StdLogger
 164  }
 165  
 166  // GetUnderlying returns the StdLogger underneath this logger.  Since StdLogger
 167  // is itself an interface, the result may or may not be a Go log.Logger.
 168  func (l logger) GetUnderlying() StdLogger {
 169  	return l.std
 170  }
 171