github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/tool/logger/README.md (about) 1 # Example 2 ```go 3 package logger_test 4 5 import ( 6 "bytes" 7 "context" 8 "log" 9 10 "github.com/facebookincubator/go-belt" 11 "github.com/facebookincubator/go-belt/tool/logger" 12 "github.com/facebookincubator/go-belt/tool/logger/implementation/glog" 13 xlogrus "github.com/facebookincubator/go-belt/tool/logger/implementation/logrus" 14 "github.com/facebookincubator/go-belt/tool/logger/implementation/stdlib" 15 "github.com/facebookincubator/go-belt/tool/logger/implementation/zap" 16 "github.com/sirupsen/logrus" 17 ) 18 19 func Example() { 20 // easy to use: 21 l := xlogrus.Default() 22 someFunction(1, l) 23 24 // implementation agnostic: 25 l = zap.Default() 26 someFunction(2, l) 27 28 // various implementations: 29 l = glog.New() 30 someFunction(3, l) 31 32 // one may still reuse all the features of the backend logger: 33 logrusInstance := logrus.New() 34 logrusInstance.Formatter = &logrus.JSONFormatter{} 35 l = xlogrus.New(logrusInstance) 36 someFunction(4, l) 37 38 // just another example: 39 var buf bytes.Buffer 40 stdLogInstance := log.New(&buf, "", log.Llongfile) 41 l = stdlib.New(stdLogInstance, logger.LevelDebug) 42 someFunction(5, l) 43 44 // use go-belt to manage the logger 45 obs := belt.New() 46 obs = logger.BeltWithLogger(obs, l) 47 someBeltyFunction(6, obs) 48 49 // use context to manage the logger 50 ctx := context.Background() 51 ctx = logger.CtxWithLogger(ctx, l) 52 someContextyFunction(ctx, 7) 53 54 // use a singletony Logger: 55 logger.Default = func() logger.Logger { 56 return l 57 } 58 oneMoreFunction(8) 59 } 60 61 func someFunction(arg int, l logger.Logger) { 62 // experience close to logrus/zap: 63 l = l.WithField("arg", arg) 64 anotherFunction(l) 65 } 66 67 func anotherFunction(l logger.Logger) { 68 l.Debugf("hello world, %T!", l) 69 } 70 71 func someBeltyFunction(arg int, obs *belt.Belt) { 72 obs = obs.WithField("arg", arg) 73 anotherBeltyFunction(obs) 74 } 75 76 func anotherBeltyFunction(obs *belt.Belt) { 77 logger.FromBelt(obs).Debugf("hello world!") 78 } 79 80 func someContextyFunction(ctx context.Context, arg int) { 81 ctx = belt.WithField(ctx, "arg", arg) 82 anotherContextyFunction(ctx) 83 } 84 85 func anotherContextyFunction(ctx context.Context) { 86 logger.FromCtx(ctx).Debugf("hello world!") 87 // or a shorter form: 88 logger.Debugf(ctx, "hello world!") 89 } 90 91 func oneMoreFunction(arg int) { 92 logger.Default().WithField("arg", arg).Debugf("hello world!") 93 } 94 ``` 95 96 # Interface 97 ```go 98 type Logger interface { 99 // Logf logs an unstructured message. Though, of course, all 100 // contextual structured fields will also be logged. 101 // 102 // This method exists mostly for convenience, for people who 103 // has not got used to proper structured logging, yet. 104 // See `LogFields` and `Log`. If one have variables they want to 105 // log, it is better for scalable observability to log them 106 // as structured values, instead of injecting them into a 107 // non-structured string. 108 Logf(level Level, format string, args ...any) 109 110 // LogFields logs structured fields with a explanation message. 111 // 112 // Anything that implements field.AbstractFields might be used 113 // as a collection of fields to be logged. 114 // 115 // Examples: 116 // 117 // l.LogFields(logger.LevelDebug, "new_request", field.Fields{{Key: "user_id", Value: userID}, {Key: "group_id", Value: groupID}}) 118 // l.LogFields(logger.LevelInfo, "affected entries", field.Field{Key: "mysql_affected", Value: affectedRows}) 119 // l.LogFields(logger.LevelError, "unable to fetch user info", request) // where `request` implements field.AbstractFields 120 // 121 // Sometimes it is inconvenient to manually describe each field, 122 // and for such cases see method `Log`. 123 LogFields(level Level, message string, fields field.AbstractFields) 124 125 // Log extracts structured fields from provided values, joins 126 // the rest into an unstructured message and logs the result. 127 // 128 // This function provides convenience (relatively to LogFields) 129 // at cost of a bit of performance. 130 // 131 // There are few ways to extract structured fields, which are 132 // applied for each value from `values` (in descending priority order): 133 // 1. If a `value` is an `*Entry` then the Entry is used (with its fields) 134 // 2. If a `value` implements field.AbstractFields then ForEachField method 135 // is used (so it is become similar to LogFields). 136 // 3. If a `value` is a structure (or a pointer to a structure) then 137 // fields of the structure are interpreted as structured fields 138 // to be logged (see explanation below). 139 // 4. If a `value` is a map then fields a constructed out of this map. 140 // 141 // Everything that does not fit into any of the rules above is just 142 // joined into an nonstructured message (and works the same way 143 // as `message` in LogFields). 144 // 145 // How structures are parsed: 146 // Structures are parsed recursively. Each field name of the path in a tree 147 // of structures is added to the resulting field name (for example int "struct{A struct{B int}}" 148 // the field name will be `A.B`). 149 // To enforce another name use tag `log` (for example "struct{A int `log:"anotherName"`}"), 150 // to prevent a field from logging use tag `log:"-"`. 151 // 152 // Examples: 153 // 154 // user, err := getUser() 155 // if err != nil { 156 // l.Log(logger.LevelError, err) 157 // return err 158 // } 159 // l.Log(logger.LevelDebug, "current user", user) // fields of structure "user" will be logged 160 // l.Log(logger.LevelDebug, map[string]any{"user_id": user.ID, "group_id", user.GroupID}) 161 // l.Log(logger.LevelDebug, field.Fields{{Key: "user_id", Value: user.ID}, {Key: "group_id", Value: user.GroupID}}) 162 // l.Log(logger.LevelDebug, "current user ID is ", user.ID, " and group ID is ", user.GroupID) // will result into message "current user ID is 1234 and group ID is 5678". 163 Log(level Level, values ...any) 164 165 // Emitter returns the Emitter (see the description of interface "Emitter"). 166 Emitter() Emitter 167 168 // Level returns the current logging level (see description of "Level"). 169 Level() Level 170 171 // TraceFields is just a shorthand for LogFields(logger.LevelTrace, ...) 172 TraceFields(message string, fields field.AbstractFields) 173 174 // DebugFields is just a shorthand for LogFields(logger.LevelDebug, ...) 175 DebugFields(message string, fields field.AbstractFields) 176 177 // InfoFields is just a shorthand for LogFields(logger.LevelInfo, ...) 178 InfoFields(message string, fields field.AbstractFields) 179 180 // WarnFields is just a shorthand for LogFields(logger.LevelWarn, ...) 181 WarnFields(message string, fields field.AbstractFields) 182 183 // ErrorFields is just a shorthand for LogFields(logger.LevelError, ...) 184 ErrorFields(message string, fields field.AbstractFields) 185 186 // PanicFields is just a shorthand for LogFields(logger.LevelPanic, ...) 187 // 188 // Be aware: Panic level also triggers a `panic`. 189 PanicFields(message string, fields field.AbstractFields) 190 191 // FatalFields is just a shorthand for LogFields(logger.LevelFatal, ...) 192 // 193 // Be aware: Panic level also triggers an `os.Exit`. 194 FatalFields(message string, fields field.AbstractFields) 195 196 // Trace is just a shorthand for Log(logger.LevelTrace, ...) 197 Trace(values ...any) 198 199 // Debug is just a shorthand for Log(logger.LevelDebug, ...) 200 Debug(values ...any) 201 202 // Info is just a shorthand for Log(logger.LevelInfo, ...) 203 Info(values ...any) 204 205 // Warn is just a shorthand for Log(logger.LevelWarn, ...) 206 Warn(values ...any) 207 208 // Error is just a shorthand for Log(logger.LevelError, ...) 209 Error(values ...any) 210 211 // Panic is just a shorthand for Log(logger.LevelPanic, ...) 212 // 213 // Be aware: Panic level also triggers a `panic`. 214 Panic(values ...any) 215 216 // Fatal is just a shorthand for Log(logger.LevelFatal, ...) 217 // 218 // Be aware: Fatal level also triggers an `os.Exit`. 219 Fatal(values ...any) 220 221 // Tracef is just a shorthand for Logf(logger.LevelTrace, ...) 222 Tracef(format string, args ...any) 223 224 // Debugf is just a shorthand for Logf(logger.LevelDebug, ...) 225 Debugf(format string, args ...any) 226 227 // Infof is just a shorthand for Logf(logger.LevelInfo, ...) 228 Infof(format string, args ...any) 229 230 // Warnf is just a shorthand for Logf(logger.LevelWarn, ...) 231 Warnf(format string, args ...any) 232 233 // Errorf is just a shorthand for Logf(logger.LevelError, ...) 234 Errorf(format string, args ...any) 235 236 // Panicf is just a shorthand for Logf(logger.LevelPanic, ...) 237 // 238 // Be aware: Panic level also triggers a `panic`. 239 Panicf(format string, args ...any) 240 241 // Fatalf is just a shorthand for Logf(logger.LevelFatal, ...) 242 // 243 // Be aware: Fatal level also triggers an `os.Exit`. 244 Fatalf(format string, args ...any) 245 246 // WithLevel returns a logger with logger level set to the given argument. 247 // 248 // See also the description of type "Level". 249 WithLevel(Level) Logger 250 251 // WithPreHooks returns a Logger which includes/appends pre-hooks from the arguments. 252 // 253 // See also description of "PreHook". 254 // 255 // Special case: to reset hooks use `WithHooks()` (without any arguments). 256 WithPreHooks(...PreHook) Logger 257 258 // WithHooks returns a Logger which includes/appends hooks from the arguments. 259 // 260 // See also description of "Hook". 261 // 262 // Special case: to reset hooks use `WithHooks()` (without any arguments). 263 WithHooks(...Hook) Logger 264 265 // WithField returns the logger with the added field (used for structured logging). 266 WithField(key string, value any, props ...field.Property) Logger 267 268 // WithMoreFields returns the logger with the added fields (used for structured logging) 269 WithMoreFields(fields field.AbstractFields) Logger 270 271 // WithMessagePrefix adds a string to all messages logged through the derived logger. 272 WithMessagePrefix(prefix string) Logger 273 274 // WithContextFields sets new context-defined fields. Supposed to be called 275 // only by an Belt. 276 // 277 // allFields contains all fields as a chain of additions in a reverse-chronological order, 278 // while newFieldsCount tells about how much of the fields are new (since last 279 // call of WithContextFields). Thus if one will call 280 // field.Slice(allFields, 0, newFieldsCount) they will get only the new fields. 281 // At the same time some Tool-s may prefer just to re-set all the fields instead of adding 282 // only new fields (due to performance reasons) and they may just use `allFields`. 283 WithContextFields(allFields *field.FieldsChain, newFieldsCount int) Tool 284 285 // WithTraceIDs sets new context-defined TraceIDs. Supposed to be called 286 // only by an Belt. 287 // 288 // traceIDs and newTraceIDsCount has similar properties as allFields and newFieldsCount 289 // in the WithContextFields method. 290 WithTraceIDs(traceIDs TraceIDs, newTraceIDsCount int) Tool 291 292 // Flush forces to flush all buffers. 293 Flush() 294 } 295 ``` 296 297 # Implementations 298 299 These implementations are provided out of the box: 300 301 * [`zap`](https://pkg.go.dev/github.com/facebookincubator/go-belt/tool/logger/implementation/zap) -- is based on Uber's [`zap`](https://github.com/uber-go/zap). 302 * [`logrus`](https://pkg.go.dev/github.com/facebookincubator/go-belt/tool/logger/implementation/logrus) -- is based on [`github.com/sirupsen/logrus`](https://github.com/sirupsen/logrus). 303 * [`glog`](https://pkg.go.dev/github.com/facebookincubator/go-belt/tool/logger/implementation/glog) -- is based on Google's [`glog`](github.com/golang/glog). 304 * [`stdlib`](https://pkg.go.dev/github.com/facebookincubator/go-belt/tool/logger/implementation/glog) -- is based on standard Go's [`log`](https://pkg.go.dev/log) package. 305 306 # Custom implementation 307 308 Depending on how many features your logger is ready to provide it should implement one of: 309 * `interface{ Printf(format string, args ...any)` or just be of type `func(string, ...any)` 310 * [`Emitter`](https://github.com/facebookincubator/go-belt/blob/a80187cd561e4c30237aff5fccd46f06981d41e2/tool/logger/types/logger.go#L225) 311 * [`CompactLogger`](https://github.com/facebookincubator/go-belt/blob/a80187cd561e4c30237aff5fccd46f06981d41e2/tool/logger/adapter/compact_logger.go#L17) 312 * [`Logger`](https://github.com/facebookincubator/go-belt/blob/a80187cd561e4c30237aff5fccd46f06981d41e2/tool/logger/types/logger.go#L25) 313 314 315 And then you may call `adapter.LoggerFromAny` and it will convert your logger to `Logger` by adding everything what is missing in a naive generic way. 316 317 So the easiest implementation of a logger might be for example: 318 ```go 319 import ( 320 "github.com/facebookincubator/go-belt/tool/logger" 321 "github.com/facebookincubator/go-belt/tool/logger/adapter" 322 ) 323 324 func getLogger() logger.Logger { 325 return adapter.LoggerFromAny(func(format string, args ...any) { 326 fmt.Printf("[log]"+format+"\n", args...) 327 }) 328 } 329 ``` 330 331 If a more complicated logger is required then take a look at existing [implementations](https://github.com/facebookincubator/go-belt/tree/main/tool/logger/implementation). 332