stdr.go (4855B)
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 }