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