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 }