config.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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. package zap
  21. import (
  22. "fmt"
  23. "sort"
  24. "time"
  25. "go.uber.org/zap/zapcore"
  26. )
  27. // SamplingConfig sets a sampling strategy for the logger. Sampling caps the
  28. // global CPU and I/O load that logging puts on your process while attempting
  29. // to preserve a representative subset of your logs.
  30. //
  31. // If specified, the Sampler will invoke the Hook after each decision.
  32. //
  33. // Values configured here are per-second. See zapcore.NewSamplerWithOptions for
  34. // details.
  35. type SamplingConfig struct {
  36. Initial int `json:"initial" yaml:"initial"`
  37. Thereafter int `json:"thereafter" yaml:"thereafter"`
  38. Hook func(zapcore.Entry, zapcore.SamplingDecision) `json:"-" yaml:"-"`
  39. }
  40. // Config offers a declarative way to construct a logger. It doesn't do
  41. // anything that can't be done with New, Options, and the various
  42. // zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to
  43. // toggle common options.
  44. //
  45. // Note that Config intentionally supports only the most common options. More
  46. // unusual logging setups (logging to network connections or message queues,
  47. // splitting output between multiple files, etc.) are possible, but require
  48. // direct use of the zapcore package. For sample code, see the package-level
  49. // BasicConfiguration and AdvancedConfiguration examples.
  50. //
  51. // For an example showing runtime log level changes, see the documentation for
  52. // AtomicLevel.
  53. type Config struct {
  54. // Level is the minimum enabled logging level. Note that this is a dynamic
  55. // level, so calling Config.Level.SetLevel will atomically change the log
  56. // level of all loggers descended from this config.
  57. Level AtomicLevel `json:"level" yaml:"level"`
  58. // Development puts the logger in development mode, which changes the
  59. // behavior of DPanicLevel and takes stacktraces more liberally.
  60. Development bool `json:"development" yaml:"development"`
  61. // DisableCaller stops annotating logs with the calling function's file
  62. // name and line number. By default, all logs are annotated.
  63. DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
  64. // DisableStacktrace completely disables automatic stacktrace capturing. By
  65. // default, stacktraces are captured for WarnLevel and above logs in
  66. // development and ErrorLevel and above in production.
  67. DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
  68. // Sampling sets a sampling policy. A nil SamplingConfig disables sampling.
  69. Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
  70. // Encoding sets the logger's encoding. Valid values are "json" and
  71. // "console", as well as any third-party encodings registered via
  72. // RegisterEncoder.
  73. Encoding string `json:"encoding" yaml:"encoding"`
  74. // EncoderConfig sets options for the chosen encoder. See
  75. // zapcore.EncoderConfig for details.
  76. EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
  77. // OutputPaths is a list of URLs or file paths to write logging output to.
  78. // See Open for details.
  79. OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
  80. // ErrorOutputPaths is a list of URLs to write internal logger errors to.
  81. // The default is standard error.
  82. //
  83. // Note that this setting only affects internal errors; for sample code that
  84. // sends error-level logs to a different location from info- and debug-level
  85. // logs, see the package-level AdvancedConfiguration example.
  86. ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
  87. // InitialFields is a collection of fields to add to the root logger.
  88. InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
  89. }
  90. // NewProductionEncoderConfig returns an opinionated EncoderConfig for
  91. // production environments.
  92. func NewProductionEncoderConfig() zapcore.EncoderConfig {
  93. return zapcore.EncoderConfig{
  94. TimeKey: "ts",
  95. LevelKey: "level",
  96. NameKey: "logger",
  97. CallerKey: "caller",
  98. MessageKey: "msg",
  99. StacktraceKey: "stacktrace",
  100. LineEnding: zapcore.DefaultLineEnding,
  101. EncodeLevel: zapcore.LowercaseLevelEncoder,
  102. EncodeTime: zapcore.EpochTimeEncoder,
  103. EncodeDuration: zapcore.SecondsDurationEncoder,
  104. EncodeCaller: zapcore.ShortCallerEncoder,
  105. }
  106. }
  107. // NewProductionConfig is a reasonable production logging configuration.
  108. // Logging is enabled at InfoLevel and above.
  109. //
  110. // It uses a JSON encoder, writes to standard error, and enables sampling.
  111. // Stacktraces are automatically included on logs of ErrorLevel and above.
  112. func NewProductionConfig() Config {
  113. return Config{
  114. Level: NewAtomicLevelAt(InfoLevel),
  115. Development: false,
  116. Sampling: &SamplingConfig{
  117. Initial: 100,
  118. Thereafter: 100,
  119. },
  120. Encoding: "json",
  121. EncoderConfig: NewProductionEncoderConfig(),
  122. OutputPaths: []string{"stderr"},
  123. ErrorOutputPaths: []string{"stderr"},
  124. }
  125. }
  126. // NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
  127. // development environments.
  128. func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
  129. return zapcore.EncoderConfig{
  130. // Keys can be anything except the empty string.
  131. TimeKey: "T",
  132. LevelKey: "L",
  133. NameKey: "N",
  134. CallerKey: "C",
  135. MessageKey: "M",
  136. StacktraceKey: "S",
  137. LineEnding: zapcore.DefaultLineEnding,
  138. EncodeLevel: zapcore.CapitalLevelEncoder,
  139. EncodeTime: zapcore.ISO8601TimeEncoder,
  140. EncodeDuration: zapcore.StringDurationEncoder,
  141. EncodeCaller: zapcore.ShortCallerEncoder,
  142. }
  143. }
  144. // NewDevelopmentConfig is a reasonable development logging configuration.
  145. // Logging is enabled at DebugLevel and above.
  146. //
  147. // It enables development mode (which makes DPanicLevel logs panic), uses a
  148. // console encoder, writes to standard error, and disables sampling.
  149. // Stacktraces are automatically included on logs of WarnLevel and above.
  150. func NewDevelopmentConfig() Config {
  151. return Config{
  152. Level: NewAtomicLevelAt(DebugLevel),
  153. Development: true,
  154. Encoding: "console",
  155. EncoderConfig: NewDevelopmentEncoderConfig(),
  156. OutputPaths: []string{"stderr"},
  157. ErrorOutputPaths: []string{"stderr"},
  158. }
  159. }
  160. // Build constructs a logger from the Config and Options.
  161. func (cfg Config) Build(opts ...Option) (*Logger, error) {
  162. enc, err := cfg.buildEncoder()
  163. if err != nil {
  164. return nil, err
  165. }
  166. sink, errSink, err := cfg.openSinks()
  167. if err != nil {
  168. return nil, err
  169. }
  170. if cfg.Level == (AtomicLevel{}) {
  171. return nil, fmt.Errorf("missing Level")
  172. }
  173. log := New(
  174. zapcore.NewCore(enc, sink, cfg.Level),
  175. cfg.buildOptions(errSink)...,
  176. )
  177. if len(opts) > 0 {
  178. log = log.WithOptions(opts...)
  179. }
  180. return log, nil
  181. }
  182. func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
  183. opts := []Option{ErrorOutput(errSink)}
  184. if cfg.Development {
  185. opts = append(opts, Development())
  186. }
  187. if !cfg.DisableCaller {
  188. opts = append(opts, AddCaller())
  189. }
  190. stackLevel := ErrorLevel
  191. if cfg.Development {
  192. stackLevel = WarnLevel
  193. }
  194. if !cfg.DisableStacktrace {
  195. opts = append(opts, AddStacktrace(stackLevel))
  196. }
  197. if scfg := cfg.Sampling; scfg != nil {
  198. opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core {
  199. var samplerOpts []zapcore.SamplerOption
  200. if scfg.Hook != nil {
  201. samplerOpts = append(samplerOpts, zapcore.SamplerHook(scfg.Hook))
  202. }
  203. return zapcore.NewSamplerWithOptions(
  204. core,
  205. time.Second,
  206. cfg.Sampling.Initial,
  207. cfg.Sampling.Thereafter,
  208. samplerOpts...,
  209. )
  210. }))
  211. }
  212. if len(cfg.InitialFields) > 0 {
  213. fs := make([]Field, 0, len(cfg.InitialFields))
  214. keys := make([]string, 0, len(cfg.InitialFields))
  215. for k := range cfg.InitialFields {
  216. keys = append(keys, k)
  217. }
  218. sort.Strings(keys)
  219. for _, k := range keys {
  220. fs = append(fs, Any(k, cfg.InitialFields[k]))
  221. }
  222. opts = append(opts, Fields(fs...))
  223. }
  224. return opts
  225. }
  226. func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
  227. sink, closeOut, err := Open(cfg.OutputPaths...)
  228. if err != nil {
  229. return nil, nil, err
  230. }
  231. errSink, _, err := Open(cfg.ErrorOutputPaths...)
  232. if err != nil {
  233. closeOut()
  234. return nil, nil, err
  235. }
  236. return sink, errSink, nil
  237. }
  238. func (cfg Config) buildEncoder() (zapcore.Encoder, error) {
  239. return newEncoder(cfg.Encoding, cfg.EncoderConfig)
  240. }