1 /* 2 Copyright The containerd 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 provides types and functions related to logging, passing 18 // loggers through a context, and attaching context to the logger. 19 // 20 // # Transitional types 21 // 22 // This package contains various types that are aliases for types in [logrus]. 23 // These aliases are intended for transitioning away from hard-coding logrus 24 // as logging implementation. Consumers of this package are encouraged to use 25 // the type-aliases from this package instead of directly using their logrus 26 // equivalent. 27 // 28 // The intent is to replace these aliases with locally defined types and 29 // interfaces once all consumers are no longer directly importing logrus 30 // types. 31 // 32 // IMPORTANT: due to the transitional purpose of this package, it is not 33 // guaranteed for the full logrus API to be provided in the future. As 34 // outlined, these aliases are provided as a step to transition away from 35 // a specific implementation which, as a result, exposes the full logrus API. 36 // While no decisions have been made on the ultimate design and interface 37 // provided by this package, we do not expect carrying "less common" features. 38 package log 39 40 import ( 41 "context" 42 "fmt" 43 44 "github.com/sirupsen/logrus" 45 ) 46 47 // G is a shorthand for [GetLogger]. 48 // 49 // We may want to define this locally to a package to get package tagged log 50 // messages. 51 var G = GetLogger 52 53 // L is an alias for the standard logger. 54 var L = &Entry{ 55 Logger: logrus.StandardLogger(), 56 // Default is three fields plus a little extra room. 57 Data: make(Fields, 6), 58 } 59 60 type loggerKey struct{} 61 62 // Fields type to pass to "WithFields". 63 type Fields = map[string]any 64 65 // Entry is a logging entry. It contains all the fields passed with 66 // [Entry.WithFields]. It's finally logged when Trace, Debug, Info, Warn, 67 // Error, Fatal or Panic is called on it. These objects can be reused and 68 // passed around as much as you wish to avoid field duplication. 69 // 70 // Entry is a transitional type, and currently an alias for [logrus.Entry]. 71 type Entry = logrus.Entry 72 73 // RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using 74 // zeros to ensure the formatted time is always the same number of 75 // characters. 76 const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" 77 78 // Level is a logging level. 79 type Level = logrus.Level 80 81 // Supported log levels. 82 const ( 83 // TraceLevel level. Designates finer-grained informational events 84 // than [DebugLevel]. 85 TraceLevel Level = logrus.TraceLevel 86 87 // DebugLevel level. Usually only enabled when debugging. Very verbose 88 // logging. 89 DebugLevel Level = logrus.DebugLevel 90 91 // InfoLevel level. General operational entries about what's going on 92 // inside the application. 93 InfoLevel Level = logrus.InfoLevel 94 95 // WarnLevel level. Non-critical entries that deserve eyes. 96 WarnLevel Level = logrus.WarnLevel 97 98 // ErrorLevel level. Logs errors that should definitely be noted. 99 // Commonly used for hooks to send errors to an error tracking service. 100 ErrorLevel Level = logrus.ErrorLevel 101 102 // FatalLevel level. Logs and then calls "logger.Exit(1)". It exits 103 // even if the logging level is set to Panic. 104 FatalLevel Level = logrus.FatalLevel 105 106 // PanicLevel level. This is the highest level of severity. Logs and 107 // then calls panic with the message passed to Debug, Info, ... 108 PanicLevel Level = logrus.PanicLevel 109 ) 110 111 // SetLevel sets log level globally. It returns an error if the given 112 // level is not supported. 113 // 114 // level can be one of: 115 // 116 // - "trace" ([TraceLevel]) 117 // - "debug" ([DebugLevel]) 118 // - "info" ([InfoLevel]) 119 // - "warn" ([WarnLevel]) 120 // - "error" ([ErrorLevel]) 121 // - "fatal" ([FatalLevel]) 122 // - "panic" ([PanicLevel]) 123 func SetLevel(level string) error { 124 lvl, err := logrus.ParseLevel(level) 125 if err != nil { 126 return err 127 } 128 129 L.Logger.SetLevel(lvl) 130 return nil 131 } 132 133 // GetLevel returns the current log level. 134 func GetLevel() Level { 135 return L.Logger.GetLevel() 136 } 137 138 // OutputFormat specifies a log output format. 139 type OutputFormat string 140 141 // Supported log output formats. 142 const ( 143 // TextFormat represents the text logging format. 144 TextFormat OutputFormat = "text" 145 146 // JSONFormat represents the JSON logging format. 147 JSONFormat OutputFormat = "json" 148 ) 149 150 // SetFormat sets the log output format ([TextFormat] or [JSONFormat]). 151 func SetFormat(format OutputFormat) error { 152 switch format { 153 case TextFormat: 154 L.Logger.SetFormatter(&logrus.TextFormatter{ 155 TimestampFormat: RFC3339NanoFixed, 156 FullTimestamp: true, 157 }) 158 return nil 159 case JSONFormat: 160 L.Logger.SetFormatter(&logrus.JSONFormatter{ 161 TimestampFormat: RFC3339NanoFixed, 162 }) 163 return nil 164 default: 165 return fmt.Errorf("unknown log format: %s", format) 166 } 167 } 168 169 // WithLogger returns a new context with the provided logger. Use in 170 // combination with logger.WithField(s) for great effect. 171 func WithLogger(ctx context.Context, logger *Entry) context.Context { 172 return context.WithValue(ctx, loggerKey{}, logger.WithContext(ctx)) 173 } 174 175 // GetLogger retrieves the current logger from the context. If no logger is 176 // available, the default logger is returned. 177 func GetLogger(ctx context.Context) *Entry { 178 if logger := ctx.Value(loggerKey{}); logger != nil { 179 return logger.(*Entry) 180 } 181 return L.WithContext(ctx) 182 } 183