// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package zap_test import ( "encoding/json" "io/ioutil" "log" "os" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) func Example_presets() { // Using zap's preset constructors is the simplest way to get a feel for the // package, but they don't allow much customization. logger := zap.NewExample() // or NewProduction, or NewDevelopment defer logger.Sync() const url = "http://example.com" // In most circumstances, use the SugaredLogger. It's 4-10x faster than most // other structured logging packages and has a familiar, loosely-typed API. sugar := logger.Sugar() sugar.Infow("Failed to fetch URL.", // Structured context as loosely typed key-value pairs. "url", url, "attempt", 3, "backoff", time.Second, ) sugar.Infof("Failed to fetch URL: %s", url) // In the unusual situations where every microsecond matters, use the // Logger. It's even faster than the SugaredLogger, but only supports // structured logging. logger.Info("Failed to fetch URL.", // Structured context as strongly typed fields. zap.String("url", url), zap.Int("attempt", 3), zap.Duration("backoff", time.Second), ) // Output: // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"} // {"level":"info","msg":"Failed to fetch URL: http://example.com"} // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"} } func Example_basicConfiguration() { // For some users, the presets offered by the NewProduction, NewDevelopment, // and NewExample constructors won't be appropriate. For most of those // users, the bundled Config struct offers the right balance of flexibility // and convenience. (For more complex needs, see the AdvancedConfiguration // example.) // // See the documentation for Config and zapcore.EncoderConfig for all the // available options. rawJSON := []byte(`{ "level": "debug", "encoding": "json", "outputPaths": ["stdout", "/tmp/logs"], "errorOutputPaths": ["stderr"], "initialFields": {"foo": "bar"}, "encoderConfig": { "messageKey": "message", "levelKey": "level", "levelEncoder": "lowercase" } }`) var cfg zap.Config if err := json.Unmarshal(rawJSON, &cfg); err != nil { panic(err) } logger, err := cfg.Build() if err != nil { panic(err) } defer logger.Sync() logger.Info("logger construction succeeded") // Output: // {"level":"info","message":"logger construction succeeded","foo":"bar"} } func Example_advancedConfiguration() { // The bundled Config struct only supports the most common configuration // options. More complex needs, like splitting logs between multiple files // or writing to non-file outputs, require use of the zapcore package. // // In this example, imagine we're both sending our logs to Kafka and writing // them to the console. We'd like to encode the console output and the Kafka // topics differently, and we'd also like special treatment for // high-priority logs. // First, define our level-handling logic. highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl >= zapcore.ErrorLevel }) lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl < zapcore.ErrorLevel }) // Assume that we have clients for two Kafka topics. The clients implement // zapcore.WriteSyncer and are safe for concurrent use. (If they only // implement io.Writer, we can use zapcore.AddSync to add a no-op Sync // method. If they're not safe for concurrent use, we can add a protecting // mutex with zapcore.Lock.) topicDebugging := zapcore.AddSync(ioutil.Discard) topicErrors := zapcore.AddSync(ioutil.Discard) // High-priority output should also go to standard error, and low-priority // output should also go to standard out. consoleDebugging := zapcore.Lock(os.Stdout) consoleErrors := zapcore.Lock(os.Stderr) // Optimize the Kafka output for machine consumption and the console output // for human operators. kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) // Join the outputs, encoders, and level-handling functions into // zapcore.Cores, then tee the four cores together. core := zapcore.NewTee( zapcore.NewCore(kafkaEncoder, topicErrors, highPriority), zapcore.NewCore(consoleEncoder, consoleErrors, highPriority), zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority), zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority), ) // From a zapcore.Core, it's easy to construct a Logger. logger := zap.New(core) defer logger.Sync() logger.Info("constructed a logger") } func ExampleNamespace() { logger := zap.NewExample() defer logger.Sync() logger.With( zap.Namespace("metrics"), zap.Int("counter", 1), ).Info("tracked some metrics") // Output: // {"level":"info","msg":"tracked some metrics","metrics":{"counter":1}} } type addr struct { IP string Port int } type request struct { URL string Listen addr Remote addr } func (a addr) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("ip", a.IP) enc.AddInt("port", a.Port) return nil } func (r request) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("url", r.URL) zap.Inline(r.Listen).AddTo(enc) return enc.AddObject("remote", r.Remote) } func ExampleObject() { logger := zap.NewExample() defer logger.Sync() req := &request{ URL: "/test", Listen: addr{"127.0.0.1", 8080}, Remote: addr{"127.0.0.1", 31200}, } logger.Info("new request, in nested object", zap.Object("req", req)) logger.Info("new request, inline", zap.Inline(req)) // Output: // {"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}}} // {"level":"info","msg":"new request, inline","url":"/test","ip":"127.0.0.1","port":8080,"remote":{"ip":"127.0.0.1","port":31200}} } func ExampleNewStdLog() { logger := zap.NewExample() defer logger.Sync() std := zap.NewStdLog(logger) std.Print("standard logger wrapper") // Output: // {"level":"info","msg":"standard logger wrapper"} } func ExampleRedirectStdLog() { logger := zap.NewExample() defer logger.Sync() undo := zap.RedirectStdLog(logger) defer undo() log.Print("redirected standard library") // Output: // {"level":"info","msg":"redirected standard library"} } func ExampleReplaceGlobals() { logger := zap.NewExample() defer logger.Sync() undo := zap.ReplaceGlobals(logger) defer undo() zap.L().Info("replaced zap's global loggers") // Output: // {"level":"info","msg":"replaced zap's global loggers"} } func ExampleAtomicLevel() { atom := zap.NewAtomicLevel() // To keep the example deterministic, disable timestamps in the output. encoderCfg := zap.NewProductionEncoderConfig() encoderCfg.TimeKey = "" logger := zap.New(zapcore.NewCore( zapcore.NewJSONEncoder(encoderCfg), zapcore.Lock(os.Stdout), atom, )) defer logger.Sync() logger.Info("info logging enabled") atom.SetLevel(zap.ErrorLevel) logger.Info("info logging disabled") // Output: // {"level":"info","msg":"info logging enabled"} } func ExampleAtomicLevel_config() { // The zap.Config struct includes an AtomicLevel. To use it, keep a // reference to the Config. rawJSON := []byte(`{ "level": "info", "outputPaths": ["stdout"], "errorOutputPaths": ["stderr"], "encoding": "json", "encoderConfig": { "messageKey": "message", "levelKey": "level", "levelEncoder": "lowercase" } }`) var cfg zap.Config if err := json.Unmarshal(rawJSON, &cfg); err != nil { panic(err) } logger, err := cfg.Build() if err != nil { panic(err) } defer logger.Sync() logger.Info("info logging enabled") cfg.Level.SetLevel(zap.ErrorLevel) logger.Info("info logging disabled") // Output: // {"level":"info","message":"info logging enabled"} } func ExampleLogger_Check() { logger := zap.NewExample() defer logger.Sync() if ce := logger.Check(zap.DebugLevel, "debugging"); ce != nil { // If debug-level log output isn't enabled or if zap's sampling would have // dropped this log entry, we don't allocate the slice that holds these // fields. ce.Write( zap.String("foo", "bar"), zap.String("baz", "quux"), ) } // Output: // {"level":"debug","msg":"debugging","foo":"bar","baz":"quux"} } func ExampleLogger_Named() { logger := zap.NewExample() defer logger.Sync() // By default, Loggers are unnamed. logger.Info("no name") // The first call to Named sets the Logger name. main := logger.Named("main") main.Info("main logger") // Additional calls to Named create a period-separated path. main.Named("subpackage").Info("sub-logger") // Output: // {"level":"info","msg":"no name"} // {"level":"info","logger":"main","msg":"main logger"} // {"level":"info","logger":"main.subpackage","msg":"sub-logger"} } func ExampleWrapCore_replace() { // Replacing a Logger's core can alter fundamental behaviors. // For example, it can convert a Logger to a no-op. nop := zap.WrapCore(func(zapcore.Core) zapcore.Core { return zapcore.NewNopCore() }) logger := zap.NewExample() defer logger.Sync() logger.Info("working") logger.WithOptions(nop).Info("no-op") logger.Info("original logger still works") // Output: // {"level":"info","msg":"working"} // {"level":"info","msg":"original logger still works"} } func ExampleWrapCore_wrap() { // Wrapping a Logger's core can extend its functionality. As a trivial // example, it can double-write all logs. doubled := zap.WrapCore(func(c zapcore.Core) zapcore.Core { return zapcore.NewTee(c, c) }) logger := zap.NewExample() defer logger.Sync() logger.Info("single") logger.WithOptions(doubled).Info("doubled") // Output: // {"level":"info","msg":"single"} // {"level":"info","msg":"doubled"} // {"level":"info","msg":"doubled"} }