sugar.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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. "go.uber.org/zap/zapcore"
  24. "go.uber.org/multierr"
  25. )
  26. const (
  27. _oddNumberErrMsg = "Ignored key without a value."
  28. _nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys."
  29. )
  30. // A SugaredLogger wraps the base Logger functionality in a slower, but less
  31. // verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar
  32. // method.
  33. //
  34. // Unlike the Logger, the SugaredLogger doesn't insist on structured logging.
  35. // For each log level, it exposes three methods: one for loosely-typed
  36. // structured logging, one for println-style formatting, and one for
  37. // printf-style formatting. For example, SugaredLoggers can produce InfoLevel
  38. // output with Infow ("info with" structured context), Info, or Infof.
  39. type SugaredLogger struct {
  40. base *Logger
  41. }
  42. // Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring
  43. // is quite inexpensive, so it's reasonable for a single application to use
  44. // both Loggers and SugaredLoggers, converting between them on the boundaries
  45. // of performance-sensitive code.
  46. func (s *SugaredLogger) Desugar() *Logger {
  47. base := s.base.clone()
  48. base.callerSkip -= 2
  49. return base
  50. }
  51. // Named adds a sub-scope to the logger's name. See Logger.Named for details.
  52. func (s *SugaredLogger) Named(name string) *SugaredLogger {
  53. return &SugaredLogger{base: s.base.Named(name)}
  54. }
  55. // With adds a variadic number of fields to the logging context. It accepts a
  56. // mix of strongly-typed Field objects and loosely-typed key-value pairs. When
  57. // processing pairs, the first element of the pair is used as the field key
  58. // and the second as the field value.
  59. //
  60. // For example,
  61. // sugaredLogger.With(
  62. // "hello", "world",
  63. // "failure", errors.New("oh no"),
  64. // Stack(),
  65. // "count", 42,
  66. // "user", User{Name: "alice"},
  67. // )
  68. // is the equivalent of
  69. // unsugared.With(
  70. // String("hello", "world"),
  71. // String("failure", "oh no"),
  72. // Stack(),
  73. // Int("count", 42),
  74. // Object("user", User{Name: "alice"}),
  75. // )
  76. //
  77. // Note that the keys in key-value pairs should be strings. In development,
  78. // passing a non-string key panics. In production, the logger is more
  79. // forgiving: a separate error is logged, but the key-value pair is skipped
  80. // and execution continues. Passing an orphaned key triggers similar behavior:
  81. // panics in development and errors in production.
  82. func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger {
  83. return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)}
  84. }
  85. // Debug uses fmt.Sprint to construct and log a message.
  86. func (s *SugaredLogger) Debug(args ...interface{}) {
  87. s.log(DebugLevel, "", args, nil)
  88. }
  89. // Info uses fmt.Sprint to construct and log a message.
  90. func (s *SugaredLogger) Info(args ...interface{}) {
  91. s.log(InfoLevel, "", args, nil)
  92. }
  93. // Warn uses fmt.Sprint to construct and log a message.
  94. func (s *SugaredLogger) Warn(args ...interface{}) {
  95. s.log(WarnLevel, "", args, nil)
  96. }
  97. // Error uses fmt.Sprint to construct and log a message.
  98. func (s *SugaredLogger) Error(args ...interface{}) {
  99. s.log(ErrorLevel, "", args, nil)
  100. }
  101. // DPanic uses fmt.Sprint to construct and log a message. In development, the
  102. // logger then panics. (See DPanicLevel for details.)
  103. func (s *SugaredLogger) DPanic(args ...interface{}) {
  104. s.log(DPanicLevel, "", args, nil)
  105. }
  106. // Panic uses fmt.Sprint to construct and log a message, then panics.
  107. func (s *SugaredLogger) Panic(args ...interface{}) {
  108. s.log(PanicLevel, "", args, nil)
  109. }
  110. // Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit.
  111. func (s *SugaredLogger) Fatal(args ...interface{}) {
  112. s.log(FatalLevel, "", args, nil)
  113. }
  114. // Debugf uses fmt.Sprintf to log a templated message.
  115. func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
  116. s.log(DebugLevel, template, args, nil)
  117. }
  118. // Infof uses fmt.Sprintf to log a templated message.
  119. func (s *SugaredLogger) Infof(template string, args ...interface{}) {
  120. s.log(InfoLevel, template, args, nil)
  121. }
  122. // Warnf uses fmt.Sprintf to log a templated message.
  123. func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
  124. s.log(WarnLevel, template, args, nil)
  125. }
  126. // Errorf uses fmt.Sprintf to log a templated message.
  127. func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
  128. s.log(ErrorLevel, template, args, nil)
  129. }
  130. // DPanicf uses fmt.Sprintf to log a templated message. In development, the
  131. // logger then panics. (See DPanicLevel for details.)
  132. func (s *SugaredLogger) DPanicf(template string, args ...interface{}) {
  133. s.log(DPanicLevel, template, args, nil)
  134. }
  135. // Panicf uses fmt.Sprintf to log a templated message, then panics.
  136. func (s *SugaredLogger) Panicf(template string, args ...interface{}) {
  137. s.log(PanicLevel, template, args, nil)
  138. }
  139. // Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit.
  140. func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
  141. s.log(FatalLevel, template, args, nil)
  142. }
  143. // Debugw logs a message with some additional context. The variadic key-value
  144. // pairs are treated as they are in With.
  145. //
  146. // When debug-level logging is disabled, this is much faster than
  147. // s.With(keysAndValues).Debug(msg)
  148. func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {
  149. s.log(DebugLevel, msg, nil, keysAndValues)
  150. }
  151. // Infow logs a message with some additional context. The variadic key-value
  152. // pairs are treated as they are in With.
  153. func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) {
  154. s.log(InfoLevel, msg, nil, keysAndValues)
  155. }
  156. // Warnw logs a message with some additional context. The variadic key-value
  157. // pairs are treated as they are in With.
  158. func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) {
  159. s.log(WarnLevel, msg, nil, keysAndValues)
  160. }
  161. // Errorw logs a message with some additional context. The variadic key-value
  162. // pairs are treated as they are in With.
  163. func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {
  164. s.log(ErrorLevel, msg, nil, keysAndValues)
  165. }
  166. // DPanicw logs a message with some additional context. In development, the
  167. // logger then panics. (See DPanicLevel for details.) The variadic key-value
  168. // pairs are treated as they are in With.
  169. func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) {
  170. s.log(DPanicLevel, msg, nil, keysAndValues)
  171. }
  172. // Panicw logs a message with some additional context, then panics. The
  173. // variadic key-value pairs are treated as they are in With.
  174. func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) {
  175. s.log(PanicLevel, msg, nil, keysAndValues)
  176. }
  177. // Fatalw logs a message with some additional context, then calls os.Exit. The
  178. // variadic key-value pairs are treated as they are in With.
  179. func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
  180. s.log(FatalLevel, msg, nil, keysAndValues)
  181. }
  182. // Sync flushes any buffered log entries.
  183. func (s *SugaredLogger) Sync() error {
  184. return s.base.Sync()
  185. }
  186. func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
  187. // If logging at this level is completely disabled, skip the overhead of
  188. // string formatting.
  189. if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
  190. return
  191. }
  192. // Format with Sprint, Sprintf, or neither.
  193. msg := template
  194. if msg == "" && len(fmtArgs) > 0 {
  195. msg = fmt.Sprint(fmtArgs...)
  196. } else if msg != "" && len(fmtArgs) > 0 {
  197. msg = fmt.Sprintf(template, fmtArgs...)
  198. }
  199. if ce := s.base.Check(lvl, msg); ce != nil {
  200. ce.Write(s.sweetenFields(context)...)
  201. }
  202. }
  203. func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
  204. if len(args) == 0 {
  205. return nil
  206. }
  207. // Allocate enough space for the worst case; if users pass only structured
  208. // fields, we shouldn't penalize them with extra allocations.
  209. fields := make([]Field, 0, len(args))
  210. var invalid invalidPairs
  211. for i := 0; i < len(args); {
  212. // This is a strongly-typed field. Consume it and move on.
  213. if f, ok := args[i].(Field); ok {
  214. fields = append(fields, f)
  215. i++
  216. continue
  217. }
  218. // Make sure this element isn't a dangling key.
  219. if i == len(args)-1 {
  220. s.base.DPanic(_oddNumberErrMsg, Any("ignored", args[i]))
  221. break
  222. }
  223. // Consume this value and the next, treating them as a key-value pair. If the
  224. // key isn't a string, add this pair to the slice of invalid pairs.
  225. key, val := args[i], args[i+1]
  226. if keyStr, ok := key.(string); !ok {
  227. // Subsequent errors are likely, so allocate once up front.
  228. if cap(invalid) == 0 {
  229. invalid = make(invalidPairs, 0, len(args)/2)
  230. }
  231. invalid = append(invalid, invalidPair{i, key, val})
  232. } else {
  233. fields = append(fields, Any(keyStr, val))
  234. }
  235. i += 2
  236. }
  237. // If we encountered any invalid key-value pairs, log an error.
  238. if len(invalid) > 0 {
  239. s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid))
  240. }
  241. return fields
  242. }
  243. type invalidPair struct {
  244. position int
  245. key, value interface{}
  246. }
  247. func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error {
  248. enc.AddInt64("position", int64(p.position))
  249. Any("key", p.key).AddTo(enc)
  250. Any("value", p.value).AddTo(enc)
  251. return nil
  252. }
  253. type invalidPairs []invalidPair
  254. func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error {
  255. var err error
  256. for i := range ps {
  257. err = multierr.Append(err, enc.AppendObject(ps[i]))
  258. }
  259. return err
  260. }