...

Source file src/sigs.k8s.io/controller-runtime/pkg/log/deleg.go

Documentation: sigs.k8s.io/controller-runtime/pkg/log

     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  }
   209  

View as plain text