logr.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*
  2. Copyright 2019 The logr Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. // This design derives from Dave Cheney's blog:
  14. // http://dave.cheney.net/2015/11/05/lets-talk-about-logging
  15. // Package logr defines a general-purpose logging API and abstract interfaces
  16. // to back that API. Packages in the Go ecosystem can depend on this package,
  17. // while callers can implement logging with whatever backend is appropriate.
  18. //
  19. // # Usage
  20. //
  21. // Logging is done using a Logger instance. Logger is a concrete type with
  22. // methods, which defers the actual logging to a LogSink interface. The main
  23. // methods of Logger are Info() and Error(). Arguments to Info() and Error()
  24. // are key/value pairs rather than printf-style formatted strings, emphasizing
  25. // "structured logging".
  26. //
  27. // With Go's standard log package, we might write:
  28. //
  29. // log.Printf("setting target value %s", targetValue)
  30. //
  31. // With logr's structured logging, we'd write:
  32. //
  33. // logger.Info("setting target", "value", targetValue)
  34. //
  35. // Errors are much the same. Instead of:
  36. //
  37. // log.Printf("failed to open the pod bay door for user %s: %v", user, err)
  38. //
  39. // We'd write:
  40. //
  41. // logger.Error(err, "failed to open the pod bay door", "user", user)
  42. //
  43. // Info() and Error() are very similar, but they are separate methods so that
  44. // LogSink implementations can choose to do things like attach additional
  45. // information (such as stack traces) on calls to Error(). Error() messages are
  46. // always logged, regardless of the current verbosity. If there is no error
  47. // instance available, passing nil is valid.
  48. //
  49. // # Verbosity
  50. //
  51. // Often we want to log information only when the application in "verbose
  52. // mode". To write log lines that are more verbose, Logger has a V() method.
  53. // The higher the V-level of a log line, the less critical it is considered.
  54. // Log-lines with V-levels that are not enabled (as per the LogSink) will not
  55. // be written. Level V(0) is the default, and logger.V(0).Info() has the same
  56. // meaning as logger.Info(). Negative V-levels have the same meaning as V(0).
  57. // Error messages do not have a verbosity level and are always logged.
  58. //
  59. // Where we might have written:
  60. //
  61. // if flVerbose >= 2 {
  62. // log.Printf("an unusual thing happened")
  63. // }
  64. //
  65. // We can write:
  66. //
  67. // logger.V(2).Info("an unusual thing happened")
  68. //
  69. // # Logger Names
  70. //
  71. // Logger instances can have name strings so that all messages logged through
  72. // that instance have additional context. For example, you might want to add
  73. // a subsystem name:
  74. //
  75. // logger.WithName("compactor").Info("started", "time", time.Now())
  76. //
  77. // The WithName() method returns a new Logger, which can be passed to
  78. // constructors or other functions for further use. Repeated use of WithName()
  79. // will accumulate name "segments". These name segments will be joined in some
  80. // way by the LogSink implementation. It is strongly recommended that name
  81. // segments contain simple identifiers (letters, digits, and hyphen), and do
  82. // not contain characters that could muddle the log output or confuse the
  83. // joining operation (e.g. whitespace, commas, periods, slashes, brackets,
  84. // quotes, etc).
  85. //
  86. // # Saved Values
  87. //
  88. // Logger instances can store any number of key/value pairs, which will be
  89. // logged alongside all messages logged through that instance. For example,
  90. // you might want to create a Logger instance per managed object:
  91. //
  92. // With the standard log package, we might write:
  93. //
  94. // log.Printf("decided to set field foo to value %q for object %s/%s",
  95. // targetValue, object.Namespace, object.Name)
  96. //
  97. // With logr we'd write:
  98. //
  99. // // Elsewhere: set up the logger to log the object name.
  100. // obj.logger = mainLogger.WithValues(
  101. // "name", obj.name, "namespace", obj.namespace)
  102. //
  103. // // later on...
  104. // obj.logger.Info("setting foo", "value", targetValue)
  105. //
  106. // # Best Practices
  107. //
  108. // Logger has very few hard rules, with the goal that LogSink implementations
  109. // might have a lot of freedom to differentiate. There are, however, some
  110. // things to consider.
  111. //
  112. // The log message consists of a constant message attached to the log line.
  113. // This should generally be a simple description of what's occurring, and should
  114. // never be a format string. Variable information can then be attached using
  115. // named values.
  116. //
  117. // Keys are arbitrary strings, but should generally be constant values. Values
  118. // may be any Go value, but how the value is formatted is determined by the
  119. // LogSink implementation.
  120. //
  121. // Logger instances are meant to be passed around by value. Code that receives
  122. // such a value can call its methods without having to check whether the
  123. // instance is ready for use.
  124. //
  125. // Calling methods with the null logger (Logger{}) as instance will crash
  126. // because it has no LogSink. Therefore this null logger should never be passed
  127. // around. For cases where passing a logger is optional, a pointer to Logger
  128. // should be used.
  129. //
  130. // # Key Naming Conventions
  131. //
  132. // Keys are not strictly required to conform to any specification or regex, but
  133. // it is recommended that they:
  134. // - be human-readable and meaningful (not auto-generated or simple ordinals)
  135. // - be constant (not dependent on input data)
  136. // - contain only printable characters
  137. // - not contain whitespace or punctuation
  138. // - use lower case for simple keys and lowerCamelCase for more complex ones
  139. //
  140. // These guidelines help ensure that log data is processed properly regardless
  141. // of the log implementation. For example, log implementations will try to
  142. // output JSON data or will store data for later database (e.g. SQL) queries.
  143. //
  144. // While users are generally free to use key names of their choice, it's
  145. // generally best to avoid using the following keys, as they're frequently used
  146. // by implementations:
  147. // - "caller": the calling information (file/line) of a particular log line
  148. // - "error": the underlying error value in the `Error` method
  149. // - "level": the log level
  150. // - "logger": the name of the associated logger
  151. // - "msg": the log message
  152. // - "stacktrace": the stack trace associated with a particular log line or
  153. // error (often from the `Error` message)
  154. // - "ts": the timestamp for a log line
  155. //
  156. // Implementations are encouraged to make use of these keys to represent the
  157. // above concepts, when necessary (for example, in a pure-JSON output form, it
  158. // would be necessary to represent at least message and timestamp as ordinary
  159. // named values).
  160. //
  161. // # Break Glass
  162. //
  163. // Implementations may choose to give callers access to the underlying
  164. // logging implementation. The recommended pattern for this is:
  165. //
  166. // // Underlier exposes access to the underlying logging implementation.
  167. // // Since callers only have a logr.Logger, they have to know which
  168. // // implementation is in use, so this interface is less of an abstraction
  169. // // and more of way to test type conversion.
  170. // type Underlier interface {
  171. // GetUnderlying() <underlying-type>
  172. // }
  173. //
  174. // Logger grants access to the sink to enable type assertions like this:
  175. //
  176. // func DoSomethingWithImpl(log logr.Logger) {
  177. // if underlier, ok := log.GetSink().(impl.Underlier); ok {
  178. // implLogger := underlier.GetUnderlying()
  179. // ...
  180. // }
  181. // }
  182. //
  183. // Custom `With*` functions can be implemented by copying the complete
  184. // Logger struct and replacing the sink in the copy:
  185. //
  186. // // WithFooBar changes the foobar parameter in the log sink and returns a
  187. // // new logger with that modified sink. It does nothing for loggers where
  188. // // the sink doesn't support that parameter.
  189. // func WithFoobar(log logr.Logger, foobar int) logr.Logger {
  190. // if foobarLogSink, ok := log.GetSink().(FoobarSink); ok {
  191. // log = log.WithSink(foobarLogSink.WithFooBar(foobar))
  192. // }
  193. // return log
  194. // }
  195. //
  196. // Don't use New to construct a new Logger with a LogSink retrieved from an
  197. // existing Logger. Source code attribution might not work correctly and
  198. // unexported fields in Logger get lost.
  199. //
  200. // Beware that the same LogSink instance may be shared by different logger
  201. // instances. Calling functions that modify the LogSink will affect all of
  202. // those.
  203. package logr
  204. import (
  205. "context"
  206. )
  207. // New returns a new Logger instance. This is primarily used by libraries
  208. // implementing LogSink, rather than end users. Passing a nil sink will create
  209. // a Logger which discards all log lines.
  210. func New(sink LogSink) Logger {
  211. logger := Logger{}
  212. logger.setSink(sink)
  213. if sink != nil {
  214. sink.Init(runtimeInfo)
  215. }
  216. return logger
  217. }
  218. // setSink stores the sink and updates any related fields. It mutates the
  219. // logger and thus is only safe to use for loggers that are not currently being
  220. // used concurrently.
  221. func (l *Logger) setSink(sink LogSink) {
  222. l.sink = sink
  223. }
  224. // GetSink returns the stored sink.
  225. func (l Logger) GetSink() LogSink {
  226. return l.sink
  227. }
  228. // WithSink returns a copy of the logger with the new sink.
  229. func (l Logger) WithSink(sink LogSink) Logger {
  230. l.setSink(sink)
  231. return l
  232. }
  233. // Logger is an interface to an abstract logging implementation. This is a
  234. // concrete type for performance reasons, but all the real work is passed on to
  235. // a LogSink. Implementations of LogSink should provide their own constructors
  236. // that return Logger, not LogSink.
  237. //
  238. // The underlying sink can be accessed through GetSink and be modified through
  239. // WithSink. This enables the implementation of custom extensions (see "Break
  240. // Glass" in the package documentation). Normally the sink should be used only
  241. // indirectly.
  242. type Logger struct {
  243. sink LogSink
  244. level int
  245. }
  246. // Enabled tests whether this Logger is enabled. For example, commandline
  247. // flags might be used to set the logging verbosity and disable some info logs.
  248. func (l Logger) Enabled() bool {
  249. return l.sink != nil && l.sink.Enabled(l.level)
  250. }
  251. // Info logs a non-error message with the given key/value pairs as context.
  252. //
  253. // The msg argument should be used to add some constant description to the log
  254. // line. The key/value pairs can then be used to add additional variable
  255. // information. The key/value pairs must alternate string keys and arbitrary
  256. // values.
  257. func (l Logger) Info(msg string, keysAndValues ...interface{}) {
  258. if l.sink == nil {
  259. return
  260. }
  261. if l.Enabled() {
  262. if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
  263. withHelper.GetCallStackHelper()()
  264. }
  265. l.sink.Info(l.level, msg, keysAndValues...)
  266. }
  267. }
  268. // Error logs an error, with the given message and key/value pairs as context.
  269. // It functions similarly to Info, but may have unique behavior, and should be
  270. // preferred for logging errors (see the package documentations for more
  271. // information). The log message will always be emitted, regardless of
  272. // verbosity level.
  273. //
  274. // The msg argument should be used to add context to any underlying error,
  275. // while the err argument should be used to attach the actual error that
  276. // triggered this log line, if present. The err parameter is optional
  277. // and nil may be passed instead of an error instance.
  278. func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) {
  279. if l.sink == nil {
  280. return
  281. }
  282. if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
  283. withHelper.GetCallStackHelper()()
  284. }
  285. l.sink.Error(err, msg, keysAndValues...)
  286. }
  287. // V returns a new Logger instance for a specific verbosity level, relative to
  288. // this Logger. In other words, V-levels are additive. A higher verbosity
  289. // level means a log message is less important. Negative V-levels are treated
  290. // as 0.
  291. func (l Logger) V(level int) Logger {
  292. if l.sink == nil {
  293. return l
  294. }
  295. if level < 0 {
  296. level = 0
  297. }
  298. l.level += level
  299. return l
  300. }
  301. // WithValues returns a new Logger instance with additional key/value pairs.
  302. // See Info for documentation on how key/value pairs work.
  303. func (l Logger) WithValues(keysAndValues ...interface{}) Logger {
  304. if l.sink == nil {
  305. return l
  306. }
  307. l.setSink(l.sink.WithValues(keysAndValues...))
  308. return l
  309. }
  310. // WithName returns a new Logger instance with the specified name element added
  311. // to the Logger's name. Successive calls with WithName append additional
  312. // suffixes to the Logger's name. It's strongly recommended that name segments
  313. // contain only letters, digits, and hyphens (see the package documentation for
  314. // more information).
  315. func (l Logger) WithName(name string) Logger {
  316. if l.sink == nil {
  317. return l
  318. }
  319. l.setSink(l.sink.WithName(name))
  320. return l
  321. }
  322. // WithCallDepth returns a Logger instance that offsets the call stack by the
  323. // specified number of frames when logging call site information, if possible.
  324. // This is useful for users who have helper functions between the "real" call
  325. // site and the actual calls to Logger methods. If depth is 0 the attribution
  326. // should be to the direct caller of this function. If depth is 1 the
  327. // attribution should skip 1 call frame, and so on. Successive calls to this
  328. // are additive.
  329. //
  330. // If the underlying log implementation supports a WithCallDepth(int) method,
  331. // it will be called and the result returned. If the implementation does not
  332. // support CallDepthLogSink, the original Logger will be returned.
  333. //
  334. // To skip one level, WithCallStackHelper() should be used instead of
  335. // WithCallDepth(1) because it works with implementions that support the
  336. // CallDepthLogSink and/or CallStackHelperLogSink interfaces.
  337. func (l Logger) WithCallDepth(depth int) Logger {
  338. if l.sink == nil {
  339. return l
  340. }
  341. if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
  342. l.setSink(withCallDepth.WithCallDepth(depth))
  343. }
  344. return l
  345. }
  346. // WithCallStackHelper returns a new Logger instance that skips the direct
  347. // caller when logging call site information, if possible. This is useful for
  348. // users who have helper functions between the "real" call site and the actual
  349. // calls to Logger methods and want to support loggers which depend on marking
  350. // each individual helper function, like loggers based on testing.T.
  351. //
  352. // In addition to using that new logger instance, callers also must call the
  353. // returned function.
  354. //
  355. // If the underlying log implementation supports a WithCallDepth(int) method,
  356. // WithCallDepth(1) will be called to produce a new logger. If it supports a
  357. // WithCallStackHelper() method, that will be also called. If the
  358. // implementation does not support either of these, the original Logger will be
  359. // returned.
  360. func (l Logger) WithCallStackHelper() (func(), Logger) {
  361. if l.sink == nil {
  362. return func() {}, l
  363. }
  364. var helper func()
  365. if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
  366. l.setSink(withCallDepth.WithCallDepth(1))
  367. }
  368. if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
  369. helper = withHelper.GetCallStackHelper()
  370. } else {
  371. helper = func() {}
  372. }
  373. return helper, l
  374. }
  375. // IsZero returns true if this logger is an uninitialized zero value
  376. func (l Logger) IsZero() bool {
  377. return l.sink == nil
  378. }
  379. // contextKey is how we find Loggers in a context.Context.
  380. type contextKey struct{}
  381. // FromContext returns a Logger from ctx or an error if no Logger is found.
  382. func FromContext(ctx context.Context) (Logger, error) {
  383. if v, ok := ctx.Value(contextKey{}).(Logger); ok {
  384. return v, nil
  385. }
  386. return Logger{}, notFoundError{}
  387. }
  388. // notFoundError exists to carry an IsNotFound method.
  389. type notFoundError struct{}
  390. func (notFoundError) Error() string {
  391. return "no logr.Logger was present"
  392. }
  393. func (notFoundError) IsNotFound() bool {
  394. return true
  395. }
  396. // FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this
  397. // returns a Logger that discards all log messages.
  398. func FromContextOrDiscard(ctx context.Context) Logger {
  399. if v, ok := ctx.Value(contextKey{}).(Logger); ok {
  400. return v
  401. }
  402. return Discard()
  403. }
  404. // NewContext returns a new Context, derived from ctx, which carries the
  405. // provided Logger.
  406. func NewContext(ctx context.Context, logger Logger) context.Context {
  407. return context.WithValue(ctx, contextKey{}, logger)
  408. }
  409. // RuntimeInfo holds information that the logr "core" library knows which
  410. // LogSinks might want to know.
  411. type RuntimeInfo struct {
  412. // CallDepth is the number of call frames the logr library adds between the
  413. // end-user and the LogSink. LogSink implementations which choose to print
  414. // the original logging site (e.g. file & line) should climb this many
  415. // additional frames to find it.
  416. CallDepth int
  417. }
  418. // runtimeInfo is a static global. It must not be changed at run time.
  419. var runtimeInfo = RuntimeInfo{
  420. CallDepth: 1,
  421. }
  422. // LogSink represents a logging implementation. End-users will generally not
  423. // interact with this type.
  424. type LogSink interface {
  425. // Init receives optional information about the logr library for LogSink
  426. // implementations that need it.
  427. Init(info RuntimeInfo)
  428. // Enabled tests whether this LogSink is enabled at the specified V-level.
  429. // For example, commandline flags might be used to set the logging
  430. // verbosity and disable some info logs.
  431. Enabled(level int) bool
  432. // Info logs a non-error message with the given key/value pairs as context.
  433. // The level argument is provided for optional logging. This method will
  434. // only be called when Enabled(level) is true. See Logger.Info for more
  435. // details.
  436. Info(level int, msg string, keysAndValues ...interface{})
  437. // Error logs an error, with the given message and key/value pairs as
  438. // context. See Logger.Error for more details.
  439. Error(err error, msg string, keysAndValues ...interface{})
  440. // WithValues returns a new LogSink with additional key/value pairs. See
  441. // Logger.WithValues for more details.
  442. WithValues(keysAndValues ...interface{}) LogSink
  443. // WithName returns a new LogSink with the specified name appended. See
  444. // Logger.WithName for more details.
  445. WithName(name string) LogSink
  446. }
  447. // CallDepthLogSink represents a LogSink that knows how to climb the call stack
  448. // to identify the original call site and can offset the depth by a specified
  449. // number of frames. This is useful for users who have helper functions
  450. // between the "real" call site and the actual calls to Logger methods.
  451. // Implementations that log information about the call site (such as file,
  452. // function, or line) would otherwise log information about the intermediate
  453. // helper functions.
  454. //
  455. // This is an optional interface and implementations are not required to
  456. // support it.
  457. type CallDepthLogSink interface {
  458. // WithCallDepth returns a LogSink that will offset the call
  459. // stack by the specified number of frames when logging call
  460. // site information.
  461. //
  462. // If depth is 0, the LogSink should skip exactly the number
  463. // of call frames defined in RuntimeInfo.CallDepth when Info
  464. // or Error are called, i.e. the attribution should be to the
  465. // direct caller of Logger.Info or Logger.Error.
  466. //
  467. // If depth is 1 the attribution should skip 1 call frame, and so on.
  468. // Successive calls to this are additive.
  469. WithCallDepth(depth int) LogSink
  470. }
  471. // CallStackHelperLogSink represents a LogSink that knows how to climb
  472. // the call stack to identify the original call site and can skip
  473. // intermediate helper functions if they mark themselves as
  474. // helper. Go's testing package uses that approach.
  475. //
  476. // This is useful for users who have helper functions between the
  477. // "real" call site and the actual calls to Logger methods.
  478. // Implementations that log information about the call site (such as
  479. // file, function, or line) would otherwise log information about the
  480. // intermediate helper functions.
  481. //
  482. // This is an optional interface and implementations are not required
  483. // to support it. Implementations that choose to support this must not
  484. // simply implement it as WithCallDepth(1), because
  485. // Logger.WithCallStackHelper will call both methods if they are
  486. // present. This should only be implemented for LogSinks that actually
  487. // need it, as with testing.T.
  488. type CallStackHelperLogSink interface {
  489. // GetCallStackHelper returns a function that must be called
  490. // to mark the direct caller as helper function when logging
  491. // call site information.
  492. GetCallStackHelper() func()
  493. }
  494. // Marshaler is an optional interface that logged values may choose to
  495. // implement. Loggers with structured output, such as JSON, should
  496. // log the object return by the MarshalLog method instead of the
  497. // original value.
  498. type Marshaler interface {
  499. // MarshalLog can be used to:
  500. // - ensure that structs are not logged as strings when the original
  501. // value has a String method: return a different type without a
  502. // String method
  503. // - select which fields of a complex type should get logged:
  504. // return a simpler struct with fewer fields
  505. // - log unexported fields: return a different struct
  506. // with exported fields
  507. //
  508. // It may return any value of any type.
  509. MarshalLog() interface{}
  510. }