123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- // 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}}
- }
- 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"}
- }
|