| // 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 |
| |
| import ( |
| "sort" |
| "time" |
| |
| "go.uber.org/zap/zapcore" |
| ) |
| |
| // SamplingConfig sets a sampling strategy for the logger. Sampling caps the |
| // global CPU and I/O load that logging puts on your process while attempting |
| // to preserve a representative subset of your logs. |
| // |
| // Values configured here are per-second. See zapcore.NewSampler for details. |
| type SamplingConfig struct { |
| Initial int `json:"initial" yaml:"initial"` |
| Thereafter int `json:"thereafter" yaml:"thereafter"` |
| } |
| |
| // Config offers a declarative way to construct a logger. It doesn't do |
| // anything that can't be done with New, Options, and the various |
| // zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to |
| // toggle common options. |
| // |
| // Note that Config intentionally supports only the most common options. More |
| // unusual logging setups (logging to network connections or message queues, |
| // splitting output between multiple files, etc.) are possible, but require |
| // direct use of the zapcore package. For sample code, see the package-level |
| // BasicConfiguration and AdvancedConfiguration examples. |
| // |
| // For an example showing runtime log level changes, see the documentation for |
| // AtomicLevel. |
| type Config struct { |
| // Level is the minimum enabled logging level. Note that this is a dynamic |
| // level, so calling Config.Level.SetLevel will atomically change the log |
| // level of all loggers descended from this config. |
| Level AtomicLevel `json:"level" yaml:"level"` |
| // Development puts the logger in development mode, which changes the |
| // behavior of DPanicLevel and takes stacktraces more liberally. |
| Development bool `json:"development" yaml:"development"` |
| // DisableCaller stops annotating logs with the calling function's file |
| // name and line number. By default, all logs are annotated. |
| DisableCaller bool `json:"disableCaller" yaml:"disableCaller"` |
| // DisableStacktrace completely disables automatic stacktrace capturing. By |
| // default, stacktraces are captured for WarnLevel and above logs in |
| // development and ErrorLevel and above in production. |
| DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"` |
| // Sampling sets a sampling policy. A nil SamplingConfig disables sampling. |
| Sampling *SamplingConfig `json:"sampling" yaml:"sampling"` |
| // Encoding sets the logger's encoding. Valid values are "json" and |
| // "console", as well as any third-party encodings registered via |
| // RegisterEncoder. |
| Encoding string `json:"encoding" yaml:"encoding"` |
| // EncoderConfig sets options for the chosen encoder. See |
| // zapcore.EncoderConfig for details. |
| EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"` |
| // OutputPaths is a list of URLs or file paths to write logging output to. |
| // See Open for details. |
| OutputPaths []string `json:"outputPaths" yaml:"outputPaths"` |
| // ErrorOutputPaths is a list of URLs to write internal logger errors to. |
| // The default is standard error. |
| // |
| // Note that this setting only affects internal errors; for sample code that |
| // sends error-level logs to a different location from info- and debug-level |
| // logs, see the package-level AdvancedConfiguration example. |
| ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"` |
| // InitialFields is a collection of fields to add to the root logger. |
| InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"` |
| } |
| |
| // NewProductionEncoderConfig returns an opinionated EncoderConfig for |
| // production environments. |
| func NewProductionEncoderConfig() zapcore.EncoderConfig { |
| return zapcore.EncoderConfig{ |
| TimeKey: "ts", |
| LevelKey: "level", |
| NameKey: "logger", |
| CallerKey: "caller", |
| MessageKey: "msg", |
| StacktraceKey: "stacktrace", |
| LineEnding: zapcore.DefaultLineEnding, |
| EncodeLevel: zapcore.LowercaseLevelEncoder, |
| EncodeTime: zapcore.EpochTimeEncoder, |
| EncodeDuration: zapcore.SecondsDurationEncoder, |
| EncodeCaller: zapcore.ShortCallerEncoder, |
| } |
| } |
| |
| // NewProductionConfig is a reasonable production logging configuration. |
| // Logging is enabled at InfoLevel and above. |
| // |
| // It uses a JSON encoder, writes to standard error, and enables sampling. |
| // Stacktraces are automatically included on logs of ErrorLevel and above. |
| func NewProductionConfig() Config { |
| return Config{ |
| Level: NewAtomicLevelAt(InfoLevel), |
| Development: false, |
| Sampling: &SamplingConfig{ |
| Initial: 100, |
| Thereafter: 100, |
| }, |
| Encoding: "json", |
| EncoderConfig: NewProductionEncoderConfig(), |
| OutputPaths: []string{"stderr"}, |
| ErrorOutputPaths: []string{"stderr"}, |
| } |
| } |
| |
| // NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for |
| // development environments. |
| func NewDevelopmentEncoderConfig() zapcore.EncoderConfig { |
| return zapcore.EncoderConfig{ |
| // Keys can be anything except the empty string. |
| TimeKey: "T", |
| LevelKey: "L", |
| NameKey: "N", |
| CallerKey: "C", |
| MessageKey: "M", |
| StacktraceKey: "S", |
| LineEnding: zapcore.DefaultLineEnding, |
| EncodeLevel: zapcore.CapitalLevelEncoder, |
| EncodeTime: zapcore.ISO8601TimeEncoder, |
| EncodeDuration: zapcore.StringDurationEncoder, |
| EncodeCaller: zapcore.ShortCallerEncoder, |
| } |
| } |
| |
| // NewDevelopmentConfig is a reasonable development logging configuration. |
| // Logging is enabled at DebugLevel and above. |
| // |
| // It enables development mode (which makes DPanicLevel logs panic), uses a |
| // console encoder, writes to standard error, and disables sampling. |
| // Stacktraces are automatically included on logs of WarnLevel and above. |
| func NewDevelopmentConfig() Config { |
| return Config{ |
| Level: NewAtomicLevelAt(DebugLevel), |
| Development: true, |
| Encoding: "console", |
| EncoderConfig: NewDevelopmentEncoderConfig(), |
| OutputPaths: []string{"stderr"}, |
| ErrorOutputPaths: []string{"stderr"}, |
| } |
| } |
| |
| // Build constructs a logger from the Config and Options. |
| func (cfg Config) Build(opts ...Option) (*Logger, error) { |
| enc, err := cfg.buildEncoder() |
| if err != nil { |
| return nil, err |
| } |
| |
| sink, errSink, err := cfg.openSinks() |
| if err != nil { |
| return nil, err |
| } |
| |
| log := New( |
| zapcore.NewCore(enc, sink, cfg.Level), |
| cfg.buildOptions(errSink)..., |
| ) |
| if len(opts) > 0 { |
| log = log.WithOptions(opts...) |
| } |
| return log, nil |
| } |
| |
| func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option { |
| opts := []Option{ErrorOutput(errSink)} |
| |
| if cfg.Development { |
| opts = append(opts, Development()) |
| } |
| |
| if !cfg.DisableCaller { |
| opts = append(opts, AddCaller()) |
| } |
| |
| stackLevel := ErrorLevel |
| if cfg.Development { |
| stackLevel = WarnLevel |
| } |
| if !cfg.DisableStacktrace { |
| opts = append(opts, AddStacktrace(stackLevel)) |
| } |
| |
| if cfg.Sampling != nil { |
| opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core { |
| return zapcore.NewSampler(core, time.Second, int(cfg.Sampling.Initial), int(cfg.Sampling.Thereafter)) |
| })) |
| } |
| |
| if len(cfg.InitialFields) > 0 { |
| fs := make([]Field, 0, len(cfg.InitialFields)) |
| keys := make([]string, 0, len(cfg.InitialFields)) |
| for k := range cfg.InitialFields { |
| keys = append(keys, k) |
| } |
| sort.Strings(keys) |
| for _, k := range keys { |
| fs = append(fs, Any(k, cfg.InitialFields[k])) |
| } |
| opts = append(opts, Fields(fs...)) |
| } |
| |
| return opts |
| } |
| |
| func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) { |
| sink, closeOut, err := Open(cfg.OutputPaths...) |
| if err != nil { |
| return nil, nil, err |
| } |
| errSink, _, err := Open(cfg.ErrorOutputPaths...) |
| if err != nil { |
| closeOut() |
| return nil, nil, err |
| } |
| return sink, errSink, nil |
| } |
| |
| func (cfg Config) buildEncoder() (zapcore.Encoder, error) { |
| return newEncoder(cfg.Encoding, cfg.EncoderConfig) |
| } |