sugar.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. _multipleErrMsg = "Multiple errors without a key."
  30. )
  31. // A SugaredLogger wraps the base Logger functionality in a slower, but less
  32. // verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar
  33. // method.
  34. //
  35. // Unlike the Logger, the SugaredLogger doesn't insist on structured logging.
  36. // For each log level, it exposes four methods:
  37. //
  38. // - methods named after the log level for log.Print-style logging
  39. // - methods ending in "w" for loosely-typed structured logging
  40. // - methods ending in "f" for log.Printf-style logging
  41. // - methods ending in "ln" for log.Println-style logging
  42. //
  43. // For example, the methods for InfoLevel are:
  44. //
  45. // Info(...any) Print-style logging
  46. // Infow(...any) Structured logging (read as "info with")
  47. // Infof(string, ...any) Printf-style logging
  48. // Infoln(...any) Println-style logging
  49. type SugaredLogger struct {
  50. base *Logger
  51. }
  52. // Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring
  53. // is quite inexpensive, so it's reasonable for a single application to use
  54. // both Loggers and SugaredLoggers, converting between them on the boundaries
  55. // of performance-sensitive code.
  56. func (s *SugaredLogger) Desugar() *Logger {
  57. base := s.base.clone()
  58. base.callerSkip -= 2
  59. return base
  60. }
  61. // Named adds a sub-scope to the logger's name. See Logger.Named for details.
  62. func (s *SugaredLogger) Named(name string) *SugaredLogger {
  63. return &SugaredLogger{base: s.base.Named(name)}
  64. }
  65. // WithOptions clones the current SugaredLogger, applies the supplied Options,
  66. // and returns the result. It's safe to use concurrently.
  67. func (s *SugaredLogger) WithOptions(opts ...Option) *SugaredLogger {
  68. base := s.base.clone()
  69. for _, opt := range opts {
  70. opt.apply(base)
  71. }
  72. return &SugaredLogger{base: base}
  73. }
  74. // With adds a variadic number of fields to the logging context. It accepts a
  75. // mix of strongly-typed Field objects and loosely-typed key-value pairs. When
  76. // processing pairs, the first element of the pair is used as the field key
  77. // and the second as the field value.
  78. //
  79. // For example,
  80. //
  81. // sugaredLogger.With(
  82. // "hello", "world",
  83. // "failure", errors.New("oh no"),
  84. // Stack(),
  85. // "count", 42,
  86. // "user", User{Name: "alice"},
  87. // )
  88. //
  89. // is the equivalent of
  90. //
  91. // unsugared.With(
  92. // String("hello", "world"),
  93. // String("failure", "oh no"),
  94. // Stack(),
  95. // Int("count", 42),
  96. // Object("user", User{Name: "alice"}),
  97. // )
  98. //
  99. // Note that the keys in key-value pairs should be strings. In development,
  100. // passing a non-string key panics. In production, the logger is more
  101. // forgiving: a separate error is logged, but the key-value pair is skipped
  102. // and execution continues. Passing an orphaned key triggers similar behavior:
  103. // panics in development and errors in production.
  104. func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger {
  105. return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)}
  106. }
  107. // Level reports the minimum enabled level for this logger.
  108. //
  109. // For NopLoggers, this is [zapcore.InvalidLevel].
  110. func (s *SugaredLogger) Level() zapcore.Level {
  111. return zapcore.LevelOf(s.base.core)
  112. }
  113. // Debug logs the provided arguments at [DebugLevel].
  114. // Spaces are added between arguments when neither is a string.
  115. func (s *SugaredLogger) Debug(args ...interface{}) {
  116. s.log(DebugLevel, "", args, nil)
  117. }
  118. // Info logs the provided arguments at [InfoLevel].
  119. // Spaces are added between arguments when neither is a string.
  120. func (s *SugaredLogger) Info(args ...interface{}) {
  121. s.log(InfoLevel, "", args, nil)
  122. }
  123. // Warn logs the provided arguments at [WarnLevel].
  124. // Spaces are added between arguments when neither is a string.
  125. func (s *SugaredLogger) Warn(args ...interface{}) {
  126. s.log(WarnLevel, "", args, nil)
  127. }
  128. // Error logs the provided arguments at [ErrorLevel].
  129. // Spaces are added between arguments when neither is a string.
  130. func (s *SugaredLogger) Error(args ...interface{}) {
  131. s.log(ErrorLevel, "", args, nil)
  132. }
  133. // DPanic logs the provided arguments at [DPanicLevel].
  134. // In development, the logger then panics. (See [DPanicLevel] for details.)
  135. // Spaces are added between arguments when neither is a string.
  136. func (s *SugaredLogger) DPanic(args ...interface{}) {
  137. s.log(DPanicLevel, "", args, nil)
  138. }
  139. // Panic constructs a message with the provided arguments and panics.
  140. // Spaces are added between arguments when neither is a string.
  141. func (s *SugaredLogger) Panic(args ...interface{}) {
  142. s.log(PanicLevel, "", args, nil)
  143. }
  144. // Fatal constructs a message with the provided arguments and calls os.Exit.
  145. // Spaces are added between arguments when neither is a string.
  146. func (s *SugaredLogger) Fatal(args ...interface{}) {
  147. s.log(FatalLevel, "", args, nil)
  148. }
  149. // Debugf formats the message according to the format specifier
  150. // and logs it at [DebugLevel].
  151. func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
  152. s.log(DebugLevel, template, args, nil)
  153. }
  154. // Infof formats the message according to the format specifier
  155. // and logs it at [InfoLevel].
  156. func (s *SugaredLogger) Infof(template string, args ...interface{}) {
  157. s.log(InfoLevel, template, args, nil)
  158. }
  159. // Warnf formats the message according to the format specifier
  160. // and logs it at [WarnLevel].
  161. func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
  162. s.log(WarnLevel, template, args, nil)
  163. }
  164. // Errorf formats the message according to the format specifier
  165. // and logs it at [ErrorLevel].
  166. func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
  167. s.log(ErrorLevel, template, args, nil)
  168. }
  169. // DPanicf formats the message according to the format specifier
  170. // and logs it at [DPanicLevel].
  171. // In development, the logger then panics. (See [DPanicLevel] for details.)
  172. func (s *SugaredLogger) DPanicf(template string, args ...interface{}) {
  173. s.log(DPanicLevel, template, args, nil)
  174. }
  175. // Panicf formats the message according to the format specifier
  176. // and panics.
  177. func (s *SugaredLogger) Panicf(template string, args ...interface{}) {
  178. s.log(PanicLevel, template, args, nil)
  179. }
  180. // Fatalf formats the message according to the format specifier
  181. // and calls os.Exit.
  182. func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
  183. s.log(FatalLevel, template, args, nil)
  184. }
  185. // Debugw logs a message with some additional context. The variadic key-value
  186. // pairs are treated as they are in With.
  187. //
  188. // When debug-level logging is disabled, this is much faster than
  189. //
  190. // s.With(keysAndValues).Debug(msg)
  191. func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {
  192. s.log(DebugLevel, msg, nil, keysAndValues)
  193. }
  194. // Infow logs a message with some additional context. The variadic key-value
  195. // pairs are treated as they are in With.
  196. func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) {
  197. s.log(InfoLevel, msg, nil, keysAndValues)
  198. }
  199. // Warnw logs a message with some additional context. The variadic key-value
  200. // pairs are treated as they are in With.
  201. func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) {
  202. s.log(WarnLevel, msg, nil, keysAndValues)
  203. }
  204. // Errorw logs a message with some additional context. The variadic key-value
  205. // pairs are treated as they are in With.
  206. func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {
  207. s.log(ErrorLevel, msg, nil, keysAndValues)
  208. }
  209. // DPanicw logs a message with some additional context. In development, the
  210. // logger then panics. (See DPanicLevel for details.) The variadic key-value
  211. // pairs are treated as they are in With.
  212. func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) {
  213. s.log(DPanicLevel, msg, nil, keysAndValues)
  214. }
  215. // Panicw logs a message with some additional context, then panics. The
  216. // variadic key-value pairs are treated as they are in With.
  217. func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) {
  218. s.log(PanicLevel, msg, nil, keysAndValues)
  219. }
  220. // Fatalw logs a message with some additional context, then calls os.Exit. The
  221. // variadic key-value pairs are treated as they are in With.
  222. func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
  223. s.log(FatalLevel, msg, nil, keysAndValues)
  224. }
  225. // Debugln logs a message at [DebugLevel].
  226. // Spaces are always added between arguments.
  227. func (s *SugaredLogger) Debugln(args ...interface{}) {
  228. s.logln(DebugLevel, args, nil)
  229. }
  230. // Infoln logs a message at [InfoLevel].
  231. // Spaces are always added between arguments.
  232. func (s *SugaredLogger) Infoln(args ...interface{}) {
  233. s.logln(InfoLevel, args, nil)
  234. }
  235. // Warnln logs a message at [WarnLevel].
  236. // Spaces are always added between arguments.
  237. func (s *SugaredLogger) Warnln(args ...interface{}) {
  238. s.logln(WarnLevel, args, nil)
  239. }
  240. // Errorln logs a message at [ErrorLevel].
  241. // Spaces are always added between arguments.
  242. func (s *SugaredLogger) Errorln(args ...interface{}) {
  243. s.logln(ErrorLevel, args, nil)
  244. }
  245. // DPanicln logs a message at [DPanicLevel].
  246. // In development, the logger then panics. (See [DPanicLevel] for details.)
  247. // Spaces are always added between arguments.
  248. func (s *SugaredLogger) DPanicln(args ...interface{}) {
  249. s.logln(DPanicLevel, args, nil)
  250. }
  251. // Panicln logs a message at [PanicLevel] and panics.
  252. // Spaces are always added between arguments.
  253. func (s *SugaredLogger) Panicln(args ...interface{}) {
  254. s.logln(PanicLevel, args, nil)
  255. }
  256. // Fatalln logs a message at [FatalLevel] and calls os.Exit.
  257. // Spaces are always added between arguments.
  258. func (s *SugaredLogger) Fatalln(args ...interface{}) {
  259. s.logln(FatalLevel, args, nil)
  260. }
  261. // Sync flushes any buffered log entries.
  262. func (s *SugaredLogger) Sync() error {
  263. return s.base.Sync()
  264. }
  265. // log message with Sprint, Sprintf, or neither.
  266. func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
  267. // If logging at this level is completely disabled, skip the overhead of
  268. // string formatting.
  269. if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
  270. return
  271. }
  272. msg := getMessage(template, fmtArgs)
  273. if ce := s.base.Check(lvl, msg); ce != nil {
  274. ce.Write(s.sweetenFields(context)...)
  275. }
  276. }
  277. // logln message with Sprintln
  278. func (s *SugaredLogger) logln(lvl zapcore.Level, fmtArgs []interface{}, context []interface{}) {
  279. if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
  280. return
  281. }
  282. msg := getMessageln(fmtArgs)
  283. if ce := s.base.Check(lvl, msg); ce != nil {
  284. ce.Write(s.sweetenFields(context)...)
  285. }
  286. }
  287. // getMessage format with Sprint, Sprintf, or neither.
  288. func getMessage(template string, fmtArgs []interface{}) string {
  289. if len(fmtArgs) == 0 {
  290. return template
  291. }
  292. if template != "" {
  293. return fmt.Sprintf(template, fmtArgs...)
  294. }
  295. if len(fmtArgs) == 1 {
  296. if str, ok := fmtArgs[0].(string); ok {
  297. return str
  298. }
  299. }
  300. return fmt.Sprint(fmtArgs...)
  301. }
  302. // getMessageln format with Sprintln.
  303. func getMessageln(fmtArgs []interface{}) string {
  304. msg := fmt.Sprintln(fmtArgs...)
  305. return msg[:len(msg)-1]
  306. }
  307. func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
  308. if len(args) == 0 {
  309. return nil
  310. }
  311. var (
  312. // Allocate enough space for the worst case; if users pass only structured
  313. // fields, we shouldn't penalize them with extra allocations.
  314. fields = make([]Field, 0, len(args))
  315. invalid invalidPairs
  316. seenError bool
  317. )
  318. for i := 0; i < len(args); {
  319. // This is a strongly-typed field. Consume it and move on.
  320. if f, ok := args[i].(Field); ok {
  321. fields = append(fields, f)
  322. i++
  323. continue
  324. }
  325. // If it is an error, consume it and move on.
  326. if err, ok := args[i].(error); ok {
  327. if !seenError {
  328. seenError = true
  329. fields = append(fields, Error(err))
  330. } else {
  331. s.base.Error(_multipleErrMsg, Error(err))
  332. }
  333. i++
  334. continue
  335. }
  336. // Make sure this element isn't a dangling key.
  337. if i == len(args)-1 {
  338. s.base.Error(_oddNumberErrMsg, Any("ignored", args[i]))
  339. break
  340. }
  341. // Consume this value and the next, treating them as a key-value pair. If the
  342. // key isn't a string, add this pair to the slice of invalid pairs.
  343. key, val := args[i], args[i+1]
  344. if keyStr, ok := key.(string); !ok {
  345. // Subsequent errors are likely, so allocate once up front.
  346. if cap(invalid) == 0 {
  347. invalid = make(invalidPairs, 0, len(args)/2)
  348. }
  349. invalid = append(invalid, invalidPair{i, key, val})
  350. } else {
  351. fields = append(fields, Any(keyStr, val))
  352. }
  353. i += 2
  354. }
  355. // If we encountered any invalid key-value pairs, log an error.
  356. if len(invalid) > 0 {
  357. s.base.Error(_nonStringKeyErrMsg, Array("invalid", invalid))
  358. }
  359. return fields
  360. }
  361. type invalidPair struct {
  362. position int
  363. key, value interface{}
  364. }
  365. func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error {
  366. enc.AddInt64("position", int64(p.position))
  367. Any("key", p.key).AddTo(enc)
  368. Any("value", p.value).AddTo(enc)
  369. return nil
  370. }
  371. type invalidPairs []invalidPair
  372. func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error {
  373. var err error
  374. for i := range ps {
  375. err = multierr.Append(err, enc.AppendObject(ps[i]))
  376. }
  377. return err
  378. }