sugar.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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 uses fmt.Sprint to construct and log a message.
  114. func (s *SugaredLogger) Debug(args ...interface{}) {
  115. s.log(DebugLevel, "", args, nil)
  116. }
  117. // Info uses fmt.Sprint to construct and log a message.
  118. func (s *SugaredLogger) Info(args ...interface{}) {
  119. s.log(InfoLevel, "", args, nil)
  120. }
  121. // Warn uses fmt.Sprint to construct and log a message.
  122. func (s *SugaredLogger) Warn(args ...interface{}) {
  123. s.log(WarnLevel, "", args, nil)
  124. }
  125. // Error uses fmt.Sprint to construct and log a message.
  126. func (s *SugaredLogger) Error(args ...interface{}) {
  127. s.log(ErrorLevel, "", args, nil)
  128. }
  129. // DPanic uses fmt.Sprint to construct and log a message. In development, the
  130. // logger then panics. (See DPanicLevel for details.)
  131. func (s *SugaredLogger) DPanic(args ...interface{}) {
  132. s.log(DPanicLevel, "", args, nil)
  133. }
  134. // Panic uses fmt.Sprint to construct and log a message, then panics.
  135. func (s *SugaredLogger) Panic(args ...interface{}) {
  136. s.log(PanicLevel, "", args, nil)
  137. }
  138. // Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit.
  139. func (s *SugaredLogger) Fatal(args ...interface{}) {
  140. s.log(FatalLevel, "", args, nil)
  141. }
  142. // Debugf uses fmt.Sprintf to log a templated message.
  143. func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
  144. s.log(DebugLevel, template, args, nil)
  145. }
  146. // Infof uses fmt.Sprintf to log a templated message.
  147. func (s *SugaredLogger) Infof(template string, args ...interface{}) {
  148. s.log(InfoLevel, template, args, nil)
  149. }
  150. // Warnf uses fmt.Sprintf to log a templated message.
  151. func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
  152. s.log(WarnLevel, template, args, nil)
  153. }
  154. // Errorf uses fmt.Sprintf to log a templated message.
  155. func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
  156. s.log(ErrorLevel, template, args, nil)
  157. }
  158. // DPanicf uses fmt.Sprintf to log a templated message. In development, the
  159. // logger then panics. (See DPanicLevel for details.)
  160. func (s *SugaredLogger) DPanicf(template string, args ...interface{}) {
  161. s.log(DPanicLevel, template, args, nil)
  162. }
  163. // Panicf uses fmt.Sprintf to log a templated message, then panics.
  164. func (s *SugaredLogger) Panicf(template string, args ...interface{}) {
  165. s.log(PanicLevel, template, args, nil)
  166. }
  167. // Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit.
  168. func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
  169. s.log(FatalLevel, template, args, nil)
  170. }
  171. // Debugw logs a message with some additional context. The variadic key-value
  172. // pairs are treated as they are in With.
  173. //
  174. // When debug-level logging is disabled, this is much faster than
  175. //
  176. // s.With(keysAndValues).Debug(msg)
  177. func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {
  178. s.log(DebugLevel, msg, nil, keysAndValues)
  179. }
  180. // Infow logs a message with some additional context. The variadic key-value
  181. // pairs are treated as they are in With.
  182. func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) {
  183. s.log(InfoLevel, msg, nil, keysAndValues)
  184. }
  185. // Warnw logs a message with some additional context. The variadic key-value
  186. // pairs are treated as they are in With.
  187. func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) {
  188. s.log(WarnLevel, msg, nil, keysAndValues)
  189. }
  190. // Errorw logs a message with some additional context. The variadic key-value
  191. // pairs are treated as they are in With.
  192. func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {
  193. s.log(ErrorLevel, msg, nil, keysAndValues)
  194. }
  195. // DPanicw logs a message with some additional context. In development, the
  196. // logger then panics. (See DPanicLevel for details.) The variadic key-value
  197. // pairs are treated as they are in With.
  198. func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) {
  199. s.log(DPanicLevel, msg, nil, keysAndValues)
  200. }
  201. // Panicw logs a message with some additional context, then panics. The
  202. // variadic key-value pairs are treated as they are in With.
  203. func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) {
  204. s.log(PanicLevel, msg, nil, keysAndValues)
  205. }
  206. // Fatalw logs a message with some additional context, then calls os.Exit. The
  207. // variadic key-value pairs are treated as they are in With.
  208. func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
  209. s.log(FatalLevel, msg, nil, keysAndValues)
  210. }
  211. // Debugln uses fmt.Sprintln to construct and log a message.
  212. func (s *SugaredLogger) Debugln(args ...interface{}) {
  213. s.logln(DebugLevel, args, nil)
  214. }
  215. // Infoln uses fmt.Sprintln to construct and log a message.
  216. func (s *SugaredLogger) Infoln(args ...interface{}) {
  217. s.logln(InfoLevel, args, nil)
  218. }
  219. // Warnln uses fmt.Sprintln to construct and log a message.
  220. func (s *SugaredLogger) Warnln(args ...interface{}) {
  221. s.logln(WarnLevel, args, nil)
  222. }
  223. // Errorln uses fmt.Sprintln to construct and log a message.
  224. func (s *SugaredLogger) Errorln(args ...interface{}) {
  225. s.logln(ErrorLevel, args, nil)
  226. }
  227. // DPanicln uses fmt.Sprintln to construct and log a message. In development, the
  228. // logger then panics. (See DPanicLevel for details.)
  229. func (s *SugaredLogger) DPanicln(args ...interface{}) {
  230. s.logln(DPanicLevel, args, nil)
  231. }
  232. // Panicln uses fmt.Sprintln to construct and log a message, then panics.
  233. func (s *SugaredLogger) Panicln(args ...interface{}) {
  234. s.logln(PanicLevel, args, nil)
  235. }
  236. // Fatalln uses fmt.Sprintln to construct and log a message, then calls os.Exit.
  237. func (s *SugaredLogger) Fatalln(args ...interface{}) {
  238. s.logln(FatalLevel, args, nil)
  239. }
  240. // Sync flushes any buffered log entries.
  241. func (s *SugaredLogger) Sync() error {
  242. return s.base.Sync()
  243. }
  244. // log message with Sprint, Sprintf, or neither.
  245. func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
  246. // If logging at this level is completely disabled, skip the overhead of
  247. // string formatting.
  248. if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
  249. return
  250. }
  251. msg := getMessage(template, fmtArgs)
  252. if ce := s.base.Check(lvl, msg); ce != nil {
  253. ce.Write(s.sweetenFields(context)...)
  254. }
  255. }
  256. // logln message with Sprintln
  257. func (s *SugaredLogger) logln(lvl zapcore.Level, fmtArgs []interface{}, context []interface{}) {
  258. if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
  259. return
  260. }
  261. msg := getMessageln(fmtArgs)
  262. if ce := s.base.Check(lvl, msg); ce != nil {
  263. ce.Write(s.sweetenFields(context)...)
  264. }
  265. }
  266. // getMessage format with Sprint, Sprintf, or neither.
  267. func getMessage(template string, fmtArgs []interface{}) string {
  268. if len(fmtArgs) == 0 {
  269. return template
  270. }
  271. if template != "" {
  272. return fmt.Sprintf(template, fmtArgs...)
  273. }
  274. if len(fmtArgs) == 1 {
  275. if str, ok := fmtArgs[0].(string); ok {
  276. return str
  277. }
  278. }
  279. return fmt.Sprint(fmtArgs...)
  280. }
  281. // getMessageln format with Sprintln.
  282. func getMessageln(fmtArgs []interface{}) string {
  283. msg := fmt.Sprintln(fmtArgs...)
  284. return msg[:len(msg)-1]
  285. }
  286. func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
  287. if len(args) == 0 {
  288. return nil
  289. }
  290. var (
  291. // Allocate enough space for the worst case; if users pass only structured
  292. // fields, we shouldn't penalize them with extra allocations.
  293. fields = make([]Field, 0, len(args))
  294. invalid invalidPairs
  295. seenError bool
  296. )
  297. for i := 0; i < len(args); {
  298. // This is a strongly-typed field. Consume it and move on.
  299. if f, ok := args[i].(Field); ok {
  300. fields = append(fields, f)
  301. i++
  302. continue
  303. }
  304. // If it is an error, consume it and move on.
  305. if err, ok := args[i].(error); ok {
  306. if !seenError {
  307. seenError = true
  308. fields = append(fields, Error(err))
  309. } else {
  310. s.base.Error(_multipleErrMsg, Error(err))
  311. }
  312. i++
  313. continue
  314. }
  315. // Make sure this element isn't a dangling key.
  316. if i == len(args)-1 {
  317. s.base.Error(_oddNumberErrMsg, Any("ignored", args[i]))
  318. break
  319. }
  320. // Consume this value and the next, treating them as a key-value pair. If the
  321. // key isn't a string, add this pair to the slice of invalid pairs.
  322. key, val := args[i], args[i+1]
  323. if keyStr, ok := key.(string); !ok {
  324. // Subsequent errors are likely, so allocate once up front.
  325. if cap(invalid) == 0 {
  326. invalid = make(invalidPairs, 0, len(args)/2)
  327. }
  328. invalid = append(invalid, invalidPair{i, key, val})
  329. } else {
  330. fields = append(fields, Any(keyStr, val))
  331. }
  332. i += 2
  333. }
  334. // If we encountered any invalid key-value pairs, log an error.
  335. if len(invalid) > 0 {
  336. s.base.Error(_nonStringKeyErrMsg, Array("invalid", invalid))
  337. }
  338. return fields
  339. }
  340. type invalidPair struct {
  341. position int
  342. key, value interface{}
  343. }
  344. func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error {
  345. enc.AddInt64("position", int64(p.position))
  346. Any("key", p.key).AddTo(enc)
  347. Any("value", p.value).AddTo(enc)
  348. return nil
  349. }
  350. type invalidPairs []invalidPair
  351. func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error {
  352. var err error
  353. for i := range ps {
  354. err = multierr.Append(err, enc.AppendObject(ps[i]))
  355. }
  356. return err
  357. }