sigs.k8s.io/controller-runtime@v0.18.2/pkg/log/deleg.go (about) 1 /* 2 Copyright 2018 The Kubernetes 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 log 18 19 import ( 20 "sync" 21 22 "github.com/go-logr/logr" 23 ) 24 25 // loggerPromise knows how to populate a concrete logr.Logger 26 // with options, given an actual base logger later on down the line. 27 type loggerPromise struct { 28 logger *delegatingLogSink 29 childPromises []*loggerPromise 30 promisesLock sync.Mutex 31 32 name *string 33 tags []interface{} 34 } 35 36 func (p *loggerPromise) WithName(l *delegatingLogSink, name string) *loggerPromise { 37 res := &loggerPromise{ 38 logger: l, 39 name: &name, 40 promisesLock: sync.Mutex{}, 41 } 42 43 p.promisesLock.Lock() 44 defer p.promisesLock.Unlock() 45 p.childPromises = append(p.childPromises, res) 46 return res 47 } 48 49 // WithValues provides a new Logger with the tags appended. 50 func (p *loggerPromise) WithValues(l *delegatingLogSink, tags ...interface{}) *loggerPromise { 51 res := &loggerPromise{ 52 logger: l, 53 tags: tags, 54 promisesLock: sync.Mutex{}, 55 } 56 57 p.promisesLock.Lock() 58 defer p.promisesLock.Unlock() 59 p.childPromises = append(p.childPromises, res) 60 return res 61 } 62 63 // Fulfill instantiates the Logger with the provided logger. 64 func (p *loggerPromise) Fulfill(parentLogSink logr.LogSink) { 65 sink := parentLogSink 66 if p.name != nil { 67 sink = sink.WithName(*p.name) 68 } 69 70 if p.tags != nil { 71 sink = sink.WithValues(p.tags...) 72 } 73 74 p.logger.lock.Lock() 75 p.logger.logger = sink 76 if withCallDepth, ok := sink.(logr.CallDepthLogSink); ok { 77 p.logger.logger = withCallDepth.WithCallDepth(1) 78 } 79 p.logger.promise = nil 80 p.logger.lock.Unlock() 81 82 for _, childPromise := range p.childPromises { 83 childPromise.Fulfill(sink) 84 } 85 } 86 87 // delegatingLogSink is a logsink that delegates to another logr.LogSink. 88 // If the underlying promise is not nil, it registers calls to sub-loggers with 89 // the logging factory to be populated later, and returns a new delegating 90 // logger. It expects to have *some* logr.Logger set at all times (generally 91 // a no-op logger before the promises are fulfilled). 92 type delegatingLogSink struct { 93 lock sync.RWMutex 94 logger logr.LogSink 95 promise *loggerPromise 96 info logr.RuntimeInfo 97 } 98 99 // Init implements logr.LogSink. 100 func (l *delegatingLogSink) Init(info logr.RuntimeInfo) { 101 eventuallyFulfillRoot() 102 l.lock.Lock() 103 defer l.lock.Unlock() 104 l.info = info 105 } 106 107 // Enabled tests whether this Logger is enabled. For example, commandline 108 // flags might be used to set the logging verbosity and disable some info 109 // logs. 110 func (l *delegatingLogSink) Enabled(level int) bool { 111 eventuallyFulfillRoot() 112 l.lock.RLock() 113 defer l.lock.RUnlock() 114 return l.logger.Enabled(level) 115 } 116 117 // Info logs a non-error message with the given key/value pairs as context. 118 // 119 // The msg argument should be used to add some constant description to 120 // the log line. The key/value pairs can then be used to add additional 121 // variable information. The key/value pairs should alternate string 122 // keys and arbitrary values. 123 func (l *delegatingLogSink) Info(level int, msg string, keysAndValues ...interface{}) { 124 eventuallyFulfillRoot() 125 l.lock.RLock() 126 defer l.lock.RUnlock() 127 l.logger.Info(level, msg, keysAndValues...) 128 } 129 130 // Error logs an error, with the given message and key/value pairs as context. 131 // It functions similarly to calling Info with the "error" named value, but may 132 // have unique behavior, and should be preferred for logging errors (see the 133 // package documentations for more information). 134 // 135 // The msg field should be used to add context to any underlying error, 136 // while the err field should be used to attach the actual error that 137 // triggered this log line, if present. 138 func (l *delegatingLogSink) Error(err error, msg string, keysAndValues ...interface{}) { 139 eventuallyFulfillRoot() 140 l.lock.RLock() 141 defer l.lock.RUnlock() 142 l.logger.Error(err, msg, keysAndValues...) 143 } 144 145 // WithName provides a new Logger with the name appended. 146 func (l *delegatingLogSink) WithName(name string) logr.LogSink { 147 eventuallyFulfillRoot() 148 l.lock.RLock() 149 defer l.lock.RUnlock() 150 151 if l.promise == nil { 152 sink := l.logger.WithName(name) 153 if withCallDepth, ok := sink.(logr.CallDepthLogSink); ok { 154 sink = withCallDepth.WithCallDepth(-1) 155 } 156 return sink 157 } 158 159 res := &delegatingLogSink{logger: l.logger} 160 promise := l.promise.WithName(res, name) 161 res.promise = promise 162 163 return res 164 } 165 166 // WithValues provides a new Logger with the tags appended. 167 func (l *delegatingLogSink) WithValues(tags ...interface{}) logr.LogSink { 168 eventuallyFulfillRoot() 169 l.lock.RLock() 170 defer l.lock.RUnlock() 171 172 if l.promise == nil { 173 sink := l.logger.WithValues(tags...) 174 if withCallDepth, ok := sink.(logr.CallDepthLogSink); ok { 175 sink = withCallDepth.WithCallDepth(-1) 176 } 177 return sink 178 } 179 180 res := &delegatingLogSink{logger: l.logger} 181 promise := l.promise.WithValues(res, tags...) 182 res.promise = promise 183 184 return res 185 } 186 187 // Fulfill switches the logger over to use the actual logger 188 // provided, instead of the temporary initial one, if this method 189 // has not been previously called. 190 func (l *delegatingLogSink) Fulfill(actual logr.LogSink) { 191 if actual == nil { 192 actual = NullLogSink{} 193 } 194 if l.promise != nil { 195 l.promise.Fulfill(actual) 196 } 197 } 198 199 // newDelegatingLogSink constructs a new DelegatingLogSink which uses 200 // the given logger before its promise is fulfilled. 201 func newDelegatingLogSink(initial logr.LogSink) *delegatingLogSink { 202 l := &delegatingLogSink{ 203 logger: initial, 204 promise: &loggerPromise{promisesLock: sync.Mutex{}}, 205 } 206 l.promise.logger = l 207 return l 208 }