go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/logging/logging.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package logging defines Logger interface and context.Context helpers to
    16  // put\get logger from context.Context.
    17  //
    18  // Unfortunately standard library doesn't define any Logger interface (only
    19  // struct). And even worse: GAE logger is exposing different set of methods.
    20  // Some additional layer is needed to unify the logging. Package logging is
    21  // intended to be used from packages that support both local and GAE
    22  // environments. Such packages should not use global logger but must accept
    23  // instances of Logger interface (or even more generally context.Context) as
    24  // parameters. Then callers can pass appropriate Logger implementation (or
    25  // inject appropriate logger into context.Context) depending on where the code
    26  // is running.
    27  package logging
    28  
    29  import "context"
    30  
    31  // Logger interface is ultimately implemented by underlying logging libraries
    32  // (like go-logging or GAE logging). It is the least common denominator among
    33  // logger implementations.
    34  //
    35  // Logger instance is bound to some particular context that defines logging
    36  // level and extra message fields.
    37  //
    38  // Implementations register factories that produce Loggers (using 'SetFactory'
    39  // function), and top level functions (like 'Infof') use them to grab instances
    40  // of Logger bound to passed contexts. That's how they know what logging level
    41  // to use and what extra fields to add.
    42  type Logger interface {
    43  	// Debugf formats its arguments according to the format, analogous to
    44  	// fmt.Printf and records the text as a log message at Debug level.
    45  	Debugf(format string, args ...any)
    46  
    47  	// Infof is like Debugf, but logs at Info level.
    48  	Infof(format string, args ...any)
    49  
    50  	// Warningf is like Debugf, but logs at Warning level.
    51  	Warningf(format string, args ...any)
    52  
    53  	// Errorf is like Debugf, but logs at Error level.
    54  	Errorf(format string, args ...any)
    55  
    56  	// LogCall is a generic logging function. This is oriented more towards
    57  	// utility functions than direct end-user usage.
    58  	LogCall(l Level, calldepth int, format string, args []any)
    59  }
    60  
    61  // Factory is a function that returns a Logger instance bound to the specified
    62  // context.
    63  //
    64  // The given context will be used to detect logging level and fields.
    65  type Factory func(context.Context) Logger
    66  
    67  type key int
    68  
    69  const (
    70  	loggerKey key = iota
    71  	fieldsKey
    72  	levelKey
    73  )
    74  
    75  // SetFactory sets the Logger factory for this context.
    76  //
    77  // The factory will be called each time Get(context) is used.
    78  func SetFactory(ctx context.Context, f Factory) context.Context {
    79  	return context.WithValue(ctx, loggerKey, f)
    80  }
    81  
    82  // GetFactory returns the currently-configured logging factory (or nil).
    83  func GetFactory(ctx context.Context) Factory {
    84  	if f, ok := ctx.Value(loggerKey).(Factory); ok {
    85  		return f
    86  	}
    87  	return nil
    88  }
    89  
    90  // Get the current Logger, or a logger that ignores all messages if none
    91  // is defined.
    92  func Get(ctx context.Context) Logger {
    93  	if f := GetFactory(ctx); f != nil {
    94  		return f(ctx)
    95  	}
    96  	return Null
    97  }