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 21package zap_test 22 23import ( 24 "encoding/json" 25 "io/ioutil" 26 "log" 27 "os" 28 "time" 29 30 "go.uber.org/zap" 31 "go.uber.org/zap/zapcore" 32) 33 34func 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 68func 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, err := cfg.Build() 95 if err != nil { 96 panic(err) 97 } 98 defer logger.Sync() 99 100 logger.Info("logger construction succeeded") 101 // Output: 102 // {"level":"info","message":"logger construction succeeded","foo":"bar"} 103} 104 105func Example_advancedConfiguration() { 106 // The bundled Config struct only supports the most common configuration 107 // options. More complex needs, like splitting logs between multiple files 108 // or writing to non-file outputs, require use of the zapcore package. 109 // 110 // In this example, imagine we're both sending our logs to Kafka and writing 111 // them to the console. We'd like to encode the console output and the Kafka 112 // topics differently, and we'd also like special treatment for 113 // high-priority logs. 114 115 // First, define our level-handling logic. 116 highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 117 return lvl >= zapcore.ErrorLevel 118 }) 119 lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 120 return lvl < zapcore.ErrorLevel 121 }) 122 123 // Assume that we have clients for two Kafka topics. The clients implement 124 // zapcore.WriteSyncer and are safe for concurrent use. (If they only 125 // implement io.Writer, we can use zapcore.AddSync to add a no-op Sync 126 // method. If they're not safe for concurrent use, we can add a protecting 127 // mutex with zapcore.Lock.) 128 topicDebugging := zapcore.AddSync(ioutil.Discard) 129 topicErrors := zapcore.AddSync(ioutil.Discard) 130 131 // High-priority output should also go to standard error, and low-priority 132 // output should also go to standard out. 133 consoleDebugging := zapcore.Lock(os.Stdout) 134 consoleErrors := zapcore.Lock(os.Stderr) 135 136 // Optimize the Kafka output for machine consumption and the console output 137 // for human operators. 138 kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) 139 consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) 140 141 // Join the outputs, encoders, and level-handling functions into 142 // zapcore.Cores, then tee the four cores together. 143 core := zapcore.NewTee( 144 zapcore.NewCore(kafkaEncoder, topicErrors, highPriority), 145 zapcore.NewCore(consoleEncoder, consoleErrors, highPriority), 146 zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority), 147 zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority), 148 ) 149 150 // From a zapcore.Core, it's easy to construct a Logger. 151 logger := zap.New(core) 152 defer logger.Sync() 153 logger.Info("constructed a logger") 154} 155 156func ExampleNamespace() { 157 logger := zap.NewExample() 158 defer logger.Sync() 159 160 logger.With( 161 zap.Namespace("metrics"), 162 zap.Int("counter", 1), 163 ).Info("tracked some metrics") 164 // Output: 165 // {"level":"info","msg":"tracked some metrics","metrics":{"counter":1}} 166} 167 168func ExampleNewStdLog() { 169 logger := zap.NewExample() 170 defer logger.Sync() 171 172 std := zap.NewStdLog(logger) 173 std.Print("standard logger wrapper") 174 // Output: 175 // {"level":"info","msg":"standard logger wrapper"} 176} 177 178func ExampleRedirectStdLog() { 179 logger := zap.NewExample() 180 defer logger.Sync() 181 182 undo := zap.RedirectStdLog(logger) 183 defer undo() 184 185 log.Print("redirected standard library") 186 // Output: 187 // {"level":"info","msg":"redirected standard library"} 188} 189 190func ExampleReplaceGlobals() { 191 logger := zap.NewExample() 192 defer logger.Sync() 193 194 undo := zap.ReplaceGlobals(logger) 195 defer undo() 196 197 zap.L().Info("replaced zap's global loggers") 198 // Output: 199 // {"level":"info","msg":"replaced zap's global loggers"} 200} 201 202func ExampleAtomicLevel() { 203 atom := zap.NewAtomicLevel() 204 205 // To keep the example deterministic, disable timestamps in the output. 206 encoderCfg := zap.NewProductionEncoderConfig() 207 encoderCfg.TimeKey = "" 208 209 logger := zap.New(zapcore.NewCore( 210 zapcore.NewJSONEncoder(encoderCfg), 211 zapcore.Lock(os.Stdout), 212 atom, 213 )) 214 defer logger.Sync() 215 216 logger.Info("info logging enabled") 217 218 atom.SetLevel(zap.ErrorLevel) 219 logger.Info("info logging disabled") 220 // Output: 221 // {"level":"info","msg":"info logging enabled"} 222} 223 224func ExampleAtomicLevel_config() { 225 // The zap.Config struct includes an AtomicLevel. To use it, keep a 226 // reference to the Config. 227 rawJSON := []byte(`{ 228 "level": "info", 229 "outputPaths": ["stdout"], 230 "errorOutputPaths": ["stderr"], 231 "encoding": "json", 232 "encoderConfig": { 233 "messageKey": "message", 234 "levelKey": "level", 235 "levelEncoder": "lowercase" 236 } 237 }`) 238 var cfg zap.Config 239 if err := json.Unmarshal(rawJSON, &cfg); err != nil { 240 panic(err) 241 } 242 logger, err := cfg.Build() 243 if err != nil { 244 panic(err) 245 } 246 defer logger.Sync() 247 248 logger.Info("info logging enabled") 249 250 cfg.Level.SetLevel(zap.ErrorLevel) 251 logger.Info("info logging disabled") 252 // Output: 253 // {"level":"info","message":"info logging enabled"} 254} 255 256func ExampleLogger_Check() { 257 logger := zap.NewExample() 258 defer logger.Sync() 259 260 if ce := logger.Check(zap.DebugLevel, "debugging"); ce != nil { 261 // If debug-level log output isn't enabled or if zap's sampling would have 262 // dropped this log entry, we don't allocate the slice that holds these 263 // fields. 264 ce.Write( 265 zap.String("foo", "bar"), 266 zap.String("baz", "quux"), 267 ) 268 } 269 270 // Output: 271 // {"level":"debug","msg":"debugging","foo":"bar","baz":"quux"} 272} 273 274func ExampleLogger_Named() { 275 logger := zap.NewExample() 276 defer logger.Sync() 277 278 // By default, Loggers are unnamed. 279 logger.Info("no name") 280 281 // The first call to Named sets the Logger name. 282 main := logger.Named("main") 283 main.Info("main logger") 284 285 // Additional calls to Named create a period-separated path. 286 main.Named("subpackage").Info("sub-logger") 287 // Output: 288 // {"level":"info","msg":"no name"} 289 // {"level":"info","logger":"main","msg":"main logger"} 290 // {"level":"info","logger":"main.subpackage","msg":"sub-logger"} 291} 292 293func ExampleWrapCore_replace() { 294 // Replacing a Logger's core can alter fundamental behaviors. 295 // For example, it can convert a Logger to a no-op. 296 nop := zap.WrapCore(func(zapcore.Core) zapcore.Core { 297 return zapcore.NewNopCore() 298 }) 299 300 logger := zap.NewExample() 301 defer logger.Sync() 302 303 logger.Info("working") 304 logger.WithOptions(nop).Info("no-op") 305 logger.Info("original logger still works") 306 // Output: 307 // {"level":"info","msg":"working"} 308 // {"level":"info","msg":"original logger still works"} 309} 310 311func ExampleWrapCore_wrap() { 312 // Wrapping a Logger's core can extend its functionality. As a trivial 313 // example, it can double-write all logs. 314 doubled := zap.WrapCore(func(c zapcore.Core) zapcore.Core { 315 return zapcore.NewTee(c, c) 316 }) 317 318 logger := zap.NewExample() 319 defer logger.Sync() 320 321 logger.Info("single") 322 logger.WithOptions(doubled).Info("doubled") 323 // Output: 324 // {"level":"info","msg":"single"} 325 // {"level":"info","msg":"doubled"} 326 // {"level":"info","msg":"doubled"} 327} 328