github.com/Laisky/zap@v1.27.0/example_test.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package zap_test 22 23 import ( 24 "encoding/json" 25 "io" 26 "log" 27 "os" 28 "time" 29 30 "github.com/Laisky/zap" 31 "github.com/Laisky/zap/zapcore" 32 ) 33 34 func Example_presets() { 35 // Using zap's preset constructors is the simplest way to get a feel for the 36 // package, but they don't allow much customization. 37 logger := zap.NewExample() // or NewProduction, or NewDevelopment 38 defer logger.Sync() 39 40 const url = "http://example.com" 41 42 // In most circumstances, use the SugaredLogger. It's 4-10x faster than most 43 // other structured logging packages and has a familiar, loosely-typed API. 44 sugar := logger.Sugar() 45 sugar.Infow("Failed to fetch URL.", 46 // Structured context as loosely typed key-value pairs. 47 "url", url, 48 "attempt", 3, 49 "backoff", time.Second, 50 ) 51 sugar.Infof("Failed to fetch URL: %s", url) 52 53 // In the unusual situations where every microsecond matters, use the 54 // Logger. It's even faster than the SugaredLogger, but only supports 55 // structured logging. 56 logger.Info("Failed to fetch URL.", 57 // Structured context as strongly typed fields. 58 zap.String("url", url), 59 zap.Int("attempt", 3), 60 zap.Duration("backoff", time.Second), 61 ) 62 // Output: 63 // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"} 64 // {"level":"info","msg":"Failed to fetch URL: http://example.com"} 65 // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"} 66 } 67 68 func Example_basicConfiguration() { 69 // For some users, the presets offered by the NewProduction, NewDevelopment, 70 // and NewExample constructors won't be appropriate. For most of those 71 // users, the bundled Config struct offers the right balance of flexibility 72 // and convenience. (For more complex needs, see the AdvancedConfiguration 73 // example.) 74 // 75 // See the documentation for Config and zapcore.EncoderConfig for all the 76 // available options. 77 rawJSON := []byte(`{ 78 "level": "debug", 79 "encoding": "json", 80 "outputPaths": ["stdout", "/tmp/logs"], 81 "errorOutputPaths": ["stderr"], 82 "initialFields": {"foo": "bar"}, 83 "encoderConfig": { 84 "messageKey": "message", 85 "levelKey": "level", 86 "levelEncoder": "lowercase" 87 } 88 }`) 89 90 var cfg zap.Config 91 if err := json.Unmarshal(rawJSON, &cfg); err != nil { 92 panic(err) 93 } 94 logger := zap.Must(cfg.Build()) 95 defer logger.Sync() 96 97 logger.Info("logger construction succeeded") 98 // Output: 99 // {"level":"info","message":"logger construction succeeded","foo":"bar"} 100 } 101 102 func Example_advancedConfiguration() { 103 // The bundled Config struct only supports the most common configuration 104 // options. More complex needs, like splitting logs between multiple files 105 // or writing to non-file outputs, require use of the zapcore package. 106 // 107 // In this example, imagine we're both sending our logs to Kafka and writing 108 // them to the console. We'd like to encode the console output and the Kafka 109 // topics differently, and we'd also like special treatment for 110 // high-priority logs. 111 112 // First, define our level-handling logic. 113 highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 114 return lvl >= zapcore.ErrorLevel 115 }) 116 lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 117 return lvl < zapcore.ErrorLevel 118 }) 119 120 // Assume that we have clients for two Kafka topics. The clients implement 121 // zapcore.WriteSyncer and are safe for concurrent use. (If they only 122 // implement io.Writer, we can use zapcore.AddSync to add a no-op Sync 123 // method. If they're not safe for concurrent use, we can add a protecting 124 // mutex with zapcore.Lock.) 125 topicDebugging := zapcore.AddSync(io.Discard) 126 topicErrors := zapcore.AddSync(io.Discard) 127 128 // High-priority output should also go to standard error, and low-priority 129 // output should also go to standard out. 130 consoleDebugging := zapcore.Lock(os.Stdout) 131 consoleErrors := zapcore.Lock(os.Stderr) 132 133 // Optimize the Kafka output for machine consumption and the console output 134 // for human operators. 135 kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) 136 consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) 137 138 // Join the outputs, encoders, and level-handling functions into 139 // zapcore.Cores, then tee the four cores together. 140 core := zapcore.NewTee( 141 zapcore.NewCore(kafkaEncoder, topicErrors, highPriority), 142 zapcore.NewCore(consoleEncoder, consoleErrors, highPriority), 143 zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority), 144 zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority), 145 ) 146 147 // From a zapcore.Core, it's easy to construct a Logger. 148 logger := zap.New(core) 149 defer logger.Sync() 150 logger.Info("constructed a logger") 151 } 152 153 func ExampleNamespace() { 154 logger := zap.NewExample() 155 defer logger.Sync() 156 157 logger.With( 158 zap.Namespace("metrics"), 159 zap.Int("counter", 1), 160 ).Info("tracked some metrics") 161 // Output: 162 // {"level":"info","msg":"tracked some metrics","metrics":{"counter":1}} 163 } 164 165 type addr struct { 166 IP string 167 Port int 168 } 169 170 type request struct { 171 URL string 172 Listen addr 173 Remote addr 174 } 175 176 func (a addr) MarshalLogObject(enc zapcore.ObjectEncoder) error { 177 enc.AddString("ip", a.IP) 178 enc.AddInt("port", a.Port) 179 return nil 180 } 181 182 func (r *request) MarshalLogObject(enc zapcore.ObjectEncoder) error { 183 enc.AddString("url", r.URL) 184 zap.Inline(r.Listen).AddTo(enc) 185 return enc.AddObject("remote", r.Remote) 186 } 187 188 func ExampleObject() { 189 logger := zap.NewExample() 190 defer logger.Sync() 191 192 req := &request{ 193 URL: "/test", 194 Listen: addr{"127.0.0.1", 8080}, 195 Remote: addr{"127.0.0.1", 31200}, 196 } 197 logger.Info("new request, in nested object", zap.Object("req", req)) 198 logger.Info("new request, inline", zap.Inline(req)) 199 // Output: 200 // {"level":"info","msg":"new request, in nested object","req":{"url":"/test","ip":"127.0.0.1","port":8080,"remote":{"ip":"127.0.0.1","port":31200}}} 201 // {"level":"info","msg":"new request, inline","url":"/test","ip":"127.0.0.1","port":8080,"remote":{"ip":"127.0.0.1","port":31200}} 202 } 203 204 func ExampleNewStdLog() { 205 logger := zap.NewExample() 206 defer logger.Sync() 207 208 std := zap.NewStdLog(logger) 209 std.Print("standard logger wrapper") 210 // Output: 211 // {"level":"info","msg":"standard logger wrapper"} 212 } 213 214 func ExampleRedirectStdLog() { 215 logger := zap.NewExample() 216 defer logger.Sync() 217 218 undo := zap.RedirectStdLog(logger) 219 defer undo() 220 221 log.Print("redirected standard library") 222 // Output: 223 // {"level":"info","msg":"redirected standard library"} 224 } 225 226 func ExampleReplaceGlobals() { 227 logger := zap.NewExample() 228 defer logger.Sync() 229 230 undo := zap.ReplaceGlobals(logger) 231 defer undo() 232 233 zap.L().Info("replaced zap's global loggers") 234 // Output: 235 // {"level":"info","msg":"replaced zap's global loggers"} 236 } 237 238 func ExampleAtomicLevel() { 239 atom := zap.NewAtomicLevel() 240 241 // To keep the example deterministic, disable timestamps in the output. 242 encoderCfg := zap.NewProductionEncoderConfig() 243 encoderCfg.TimeKey = "" 244 245 logger := zap.New(zapcore.NewCore( 246 zapcore.NewJSONEncoder(encoderCfg), 247 zapcore.Lock(os.Stdout), 248 atom, 249 )) 250 defer logger.Sync() 251 252 logger.Info("info logging enabled") 253 254 atom.SetLevel(zap.ErrorLevel) 255 logger.Info("info logging disabled") 256 // Output: 257 // {"level":"info","msg":"info logging enabled"} 258 } 259 260 func ExampleAtomicLevel_config() { 261 // The zap.Config struct includes an AtomicLevel. To use it, keep a 262 // reference to the Config. 263 rawJSON := []byte(`{ 264 "level": "info", 265 "outputPaths": ["stdout"], 266 "errorOutputPaths": ["stderr"], 267 "encoding": "json", 268 "encoderConfig": { 269 "messageKey": "message", 270 "levelKey": "level", 271 "levelEncoder": "lowercase" 272 } 273 }`) 274 var cfg zap.Config 275 if err := json.Unmarshal(rawJSON, &cfg); err != nil { 276 panic(err) 277 } 278 logger := zap.Must(cfg.Build()) 279 defer logger.Sync() 280 281 logger.Info("info logging enabled") 282 283 cfg.Level.SetLevel(zap.ErrorLevel) 284 logger.Info("info logging disabled") 285 // Output: 286 // {"level":"info","message":"info logging enabled"} 287 } 288 289 func ExampleLogger_Check() { 290 logger := zap.NewExample() 291 defer logger.Sync() 292 293 if ce := logger.Check(zap.DebugLevel, "debugging"); ce != nil { 294 // If debug-level log output isn't enabled or if zap's sampling would have 295 // dropped this log entry, we don't allocate the slice that holds these 296 // fields. 297 ce.Write( 298 zap.String("foo", "bar"), 299 zap.String("baz", "quux"), 300 ) 301 } 302 303 // Output: 304 // {"level":"debug","msg":"debugging","foo":"bar","baz":"quux"} 305 } 306 307 func ExampleLogger_Named() { 308 logger := zap.NewExample() 309 defer logger.Sync() 310 311 // By default, Loggers are unnamed. 312 logger.Info("no name") 313 314 // The first call to Named sets the Logger name. 315 main := logger.Named("main") 316 main.Info("main logger") 317 318 // Additional calls to Named create a period-separated path. 319 main.Named("subpackage").Info("sub-logger") 320 // Output: 321 // {"level":"info","msg":"no name"} 322 // {"level":"info","logger":"main","msg":"main logger"} 323 // {"level":"info","logger":"main.subpackage","msg":"sub-logger"} 324 } 325 326 func ExampleWrapCore_replace() { 327 // Replacing a Logger's core can alter fundamental behaviors. 328 // For example, it can convert a Logger to a no-op. 329 nop := zap.WrapCore(func(zapcore.Core) zapcore.Core { 330 return zapcore.NewNopCore() 331 }) 332 333 logger := zap.NewExample() 334 defer logger.Sync() 335 336 logger.Info("working") 337 logger.WithOptions(nop).Info("no-op") 338 logger.Info("original logger still works") 339 // Output: 340 // {"level":"info","msg":"working"} 341 // {"level":"info","msg":"original logger still works"} 342 } 343 344 func ExampleWrapCore_wrap() { 345 // Wrapping a Logger's core can extend its functionality. As a trivial 346 // example, it can double-write all logs. 347 doubled := zap.WrapCore(func(c zapcore.Core) zapcore.Core { 348 return zapcore.NewTee(c, c) 349 }) 350 351 logger := zap.NewExample() 352 defer logger.Sync() 353 354 logger.Info("single") 355 logger.WithOptions(doubled).Info("doubled") 356 // Output: 357 // {"level":"info","msg":"single"} 358 // {"level":"info","msg":"doubled"} 359 // {"level":"info","msg":"doubled"} 360 } 361 362 func ExampleDict() { 363 logger := zap.NewExample() 364 defer logger.Sync() 365 366 logger.Info("login event", 367 zap.Dict("event", 368 zap.Int("id", 123), 369 zap.String("name", "jane"), 370 zap.String("status", "pending"))) 371 // Output: 372 // {"level":"info","msg":"login event","event":{"id":123,"name":"jane","status":"pending"}} 373 } 374 375 func ExampleObjects() { 376 logger := zap.NewExample() 377 defer logger.Sync() 378 379 // Use the Objects field constructor when you have a list of objects, 380 // all of which implement zapcore.ObjectMarshaler. 381 logger.Debug("opening connections", 382 zap.Objects("addrs", []addr{ 383 {IP: "123.45.67.89", Port: 4040}, 384 {IP: "127.0.0.1", Port: 4041}, 385 {IP: "192.168.0.1", Port: 4042}, 386 })) 387 // Output: 388 // {"level":"debug","msg":"opening connections","addrs":[{"ip":"123.45.67.89","port":4040},{"ip":"127.0.0.1","port":4041},{"ip":"192.168.0.1","port":4042}]} 389 } 390 391 func ExampleObjectValues() { 392 logger := zap.NewExample() 393 defer logger.Sync() 394 395 // Use the ObjectValues field constructor when you have a list of 396 // objects that do not implement zapcore.ObjectMarshaler directly, 397 // but on their pointer receivers. 398 logger.Debug("starting tunnels", 399 zap.ObjectValues("addrs", []request{ 400 { 401 URL: "/foo", 402 Listen: addr{"127.0.0.1", 8080}, 403 Remote: addr{"123.45.67.89", 4040}, 404 }, 405 { 406 URL: "/bar", 407 Listen: addr{"127.0.0.1", 8080}, 408 Remote: addr{"127.0.0.1", 31200}, 409 }, 410 })) 411 // Output: 412 // {"level":"debug","msg":"starting tunnels","addrs":[{"url":"/foo","ip":"127.0.0.1","port":8080,"remote":{"ip":"123.45.67.89","port":4040}},{"url":"/bar","ip":"127.0.0.1","port":8080,"remote":{"ip":"127.0.0.1","port":31200}}]} 413 }