config.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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. FunctionKey: zapcore.OmitKey,
  99. MessageKey: "msg",
  100. StacktraceKey: "stacktrace",
  101. LineEnding: zapcore.DefaultLineEnding,
  102. EncodeLevel: zapcore.LowercaseLevelEncoder,
  103. EncodeTime: zapcore.EpochTimeEncoder,
  104. EncodeDuration: zapcore.SecondsDurationEncoder,
  105. EncodeCaller: zapcore.ShortCallerEncoder,
  106. }
  107. }
  108. // NewProductionConfig is a reasonable production logging configuration.
  109. // Logging is enabled at InfoLevel and above.
  110. //
  111. // It uses a JSON encoder, writes to standard error, and enables sampling.
  112. // Stacktraces are automatically included on logs of ErrorLevel and above.
  113. func NewProductionConfig() Config {
  114. return Config{
  115. Level: NewAtomicLevelAt(InfoLevel),
  116. Development: false,
  117. Sampling: &SamplingConfig{
  118. Initial: 100,
  119. Thereafter: 100,
  120. },
  121. Encoding: "json",
  122. EncoderConfig: NewProductionEncoderConfig(),
  123. OutputPaths: []string{"stderr"},
  124. ErrorOutputPaths: []string{"stderr"},
  125. }
  126. }
  127. // NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
  128. // development environments.
  129. func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
  130. return zapcore.EncoderConfig{
  131. // Keys can be anything except the empty string.
  132. TimeKey: "T",
  133. LevelKey: "L",
  134. NameKey: "N",
  135. CallerKey: "C",
  136. FunctionKey: zapcore.OmitKey,
  137. MessageKey: "M",
  138. StacktraceKey: "S",
  139. LineEnding: zapcore.DefaultLineEnding,
  140. EncodeLevel: zapcore.CapitalLevelEncoder,
  141. EncodeTime: zapcore.ISO8601TimeEncoder,
  142. EncodeDuration: zapcore.StringDurationEncoder,
  143. EncodeCaller: zapcore.ShortCallerEncoder,
  144. }
  145. }
  146. // NewDevelopmentConfig is a reasonable development logging configuration.
  147. // Logging is enabled at DebugLevel and above.
  148. //
  149. // It enables development mode (which makes DPanicLevel logs panic), uses a
  150. // console encoder, writes to standard error, and disables sampling.
  151. // Stacktraces are automatically included on logs of WarnLevel and above.
  152. func NewDevelopmentConfig() Config {
  153. return Config{
  154. Level: NewAtomicLevelAt(DebugLevel),
  155. Development: true,
  156. Encoding: "console",
  157. EncoderConfig: NewDevelopmentEncoderConfig(),
  158. OutputPaths: []string{"stderr"},
  159. ErrorOutputPaths: []string{"stderr"},
  160. }
  161. }
  162. // Build constructs a logger from the Config and Options.
  163. func (cfg Config) Build(opts ...Option) (*Logger, error) {
  164. enc, err := cfg.buildEncoder()
  165. if err != nil {
  166. return nil, err
  167. }
  168. sink, errSink, err := cfg.openSinks()
  169. if err != nil {
  170. return nil, err
  171. }
  172. if cfg.Level == (AtomicLevel{}) {
  173. return nil, fmt.Errorf("missing Level")
  174. }
  175. log := New(
  176. zapcore.NewCore(enc, sink, cfg.Level),
  177. cfg.buildOptions(errSink)...,
  178. )
  179. if len(opts) > 0 {
  180. log = log.WithOptions(opts...)
  181. }
  182. return log, nil
  183. }
  184. func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
  185. opts := []Option{ErrorOutput(errSink)}
  186. if cfg.Development {
  187. opts = append(opts, Development())
  188. }
  189. if !cfg.DisableCaller {
  190. opts = append(opts, AddCaller())
  191. }
  192. stackLevel := ErrorLevel
  193. if cfg.Development {
  194. stackLevel = WarnLevel
  195. }
  196. if !cfg.DisableStacktrace {
  197. opts = append(opts, AddStacktrace(stackLevel))
  198. }
  199. if scfg := cfg.Sampling; scfg != nil {
  200. opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core {
  201. var samplerOpts []zapcore.SamplerOption
  202. if scfg.Hook != nil {
  203. samplerOpts = append(samplerOpts, zapcore.SamplerHook(scfg.Hook))
  204. }
  205. return zapcore.NewSamplerWithOptions(
  206. core,
  207. time.Second,
  208. cfg.Sampling.Initial,
  209. cfg.Sampling.Thereafter,
  210. samplerOpts...,
  211. )
  212. }))
  213. }
  214. if len(cfg.InitialFields) > 0 {
  215. fs := make([]Field, 0, len(cfg.InitialFields))
  216. keys := make([]string, 0, len(cfg.InitialFields))
  217. for k := range cfg.InitialFields {
  218. keys = append(keys, k)
  219. }
  220. sort.Strings(keys)
  221. for _, k := range keys {
  222. fs = append(fs, Any(k, cfg.InitialFields[k]))
  223. }
  224. opts = append(opts, Fields(fs...))
  225. }
  226. return opts
  227. }
  228. func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
  229. sink, closeOut, err := Open(cfg.OutputPaths...)
  230. if err != nil {
  231. return nil, nil, err
  232. }
  233. errSink, _, err := Open(cfg.ErrorOutputPaths...)
  234. if err != nil {
  235. closeOut()
  236. return nil, nil, err
  237. }
  238. return sink, errSink, nil
  239. }
  240. func (cfg Config) buildEncoder() (zapcore.Encoder, error) {
  241. return newEncoder(cfg.Encoding, cfg.EncoderConfig)
  242. }