| // Copyright 2015 The LUCI Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Package logging defines Logger interface and context.Context helpers to |
| // put\get logger from context.Context. |
| // |
| // Unfortunately standard library doesn't define any Logger interface (only |
| // struct). And even worse: GAE logger is exposing different set of methods. |
| // Some additional layer is needed to unify the logging. Package logging is |
| // intended to be used from packages that support both local and GAE |
| // environments. Such packages should not use global logger but must accept |
| // instances of Logger interface (or even more generally context.Context) as |
| // parameters. Then callers can pass appropriate Logger implementation (or |
| // inject appropriate logger into context.Context) depending on where the code |
| // is running. |
| package logging |
| |
| import "context" |
| |
| // Logger interface is ultimately implemented by underlying logging libraries |
| // (like go-logging or GAE logging). It is the least common denominator among |
| // logger implementations. |
| // |
| // Logger instance is bound to some particular context that defines logging |
| // level and extra message fields. |
| // |
| // Implementations register factories that produce Loggers (using 'SetFactory' |
| // function), and top level functions (like 'Infof') use them to grab instances |
| // of Logger bound to passed contexts. That's how they know what logging level |
| // to use and what extra fields to add. |
| type Logger interface { |
| // Debugf formats its arguments according to the format, analogous to |
| // fmt.Printf and records the text as a log message at Debug level. |
| Debugf(format string, args ...any) |
| |
| // Infof is like Debugf, but logs at Info level. |
| Infof(format string, args ...any) |
| |
| // Warningf is like Debugf, but logs at Warning level. |
| Warningf(format string, args ...any) |
| |
| // Errorf is like Debugf, but logs at Error level. |
| Errorf(format string, args ...any) |
| |
| // LogCall is a generic logging function. This is oriented more towards |
| // utility functions than direct end-user usage. |
| LogCall(l Level, calldepth int, format string, args []any) |
| } |
| |
| // Factory is a function that returns a Logger instance bound to the specified |
| // context. |
| // |
| // The given context will be used to detect logging level and fields. |
| type Factory func(context.Context) Logger |
| |
| type key int |
| |
| const ( |
| loggerKey key = iota |
| fieldsKey |
| levelKey |
| ) |
| |
| // SetFactory sets the Logger factory for this context. |
| // |
| // The factory will be called each time Get(context) is used. |
| func SetFactory(ctx context.Context, f Factory) context.Context { |
| return context.WithValue(ctx, loggerKey, f) |
| } |
| |
| // GetFactory returns the currently-configured logging factory (or nil). |
| func GetFactory(ctx context.Context) Factory { |
| if f, ok := ctx.Value(loggerKey).(Factory); ok { |
| return f |
| } |
| return nil |
| } |
| |
| // Get the current Logger, or a logger that ignores all messages if none |
| // is defined. |
| func Get(ctx context.Context) Logger { |
| if f := GetFactory(ctx); f != nil { |
| return f(ctx) |
| } |
| return Null |
| } |