contextual.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. Copyright 2021 The Kubernetes 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. package klog
  14. import (
  15. "context"
  16. "github.com/go-logr/logr"
  17. )
  18. // This file provides the implementation of
  19. // https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/1602-structured-logging
  20. //
  21. // SetLogger and ClearLogger were originally added to klog.go and got moved
  22. // here. Contextual logging adds a way to retrieve a Logger for direct logging
  23. // without the logging calls in klog.go.
  24. //
  25. // The global variables are expected to be modified only during sequential
  26. // parts of a program (init, serial tests) and therefore are not protected by
  27. // mutex locking.
  28. var (
  29. // klogLogger is used as fallback for logging through the normal klog code
  30. // when no Logger is set.
  31. klogLogger logr.Logger = logr.New(&klogger{})
  32. )
  33. // SetLogger sets a Logger implementation that will be used as backing
  34. // implementation of the traditional klog log calls. klog will do its own
  35. // verbosity checks before calling logger.V().Info. logger.Error is always
  36. // called, regardless of the klog verbosity settings.
  37. //
  38. // If set, all log lines will be suppressed from the regular output, and
  39. // redirected to the logr implementation.
  40. // Use as:
  41. //
  42. // ...
  43. // klog.SetLogger(zapr.NewLogger(zapLog))
  44. //
  45. // To remove a backing logr implemention, use ClearLogger. Setting an
  46. // empty logger with SetLogger(logr.Logger{}) does not work.
  47. //
  48. // Modifying the logger is not thread-safe and should be done while no other
  49. // goroutines invoke log calls, usually during program initialization.
  50. func SetLogger(logger logr.Logger) {
  51. SetLoggerWithOptions(logger)
  52. }
  53. // SetLoggerWithOptions is a more flexible version of SetLogger. Without
  54. // additional options, it behaves exactly like SetLogger. By passing
  55. // ContextualLogger(true) as option, it can be used to set a logger that then
  56. // will also get called directly by applications which retrieve it via
  57. // FromContext, Background, or TODO.
  58. //
  59. // Supporting direct calls is recommended because it avoids the overhead of
  60. // routing log entries through klogr into klog and then into the actual Logger
  61. // backend.
  62. func SetLoggerWithOptions(logger logr.Logger, opts ...LoggerOption) {
  63. logging.loggerOptions = loggerOptions{}
  64. for _, opt := range opts {
  65. opt(&logging.loggerOptions)
  66. }
  67. logging.logger = &logWriter{
  68. Logger: logger,
  69. writeKlogBuffer: logging.loggerOptions.writeKlogBuffer,
  70. }
  71. }
  72. // ContextualLogger determines whether the logger passed to
  73. // SetLoggerWithOptions may also get called directly. Such a logger cannot rely
  74. // on verbosity checking in klog.
  75. func ContextualLogger(enabled bool) LoggerOption {
  76. return func(o *loggerOptions) {
  77. o.contextualLogger = enabled
  78. }
  79. }
  80. // FlushLogger provides a callback for flushing data buffered by the logger.
  81. func FlushLogger(flush func()) LoggerOption {
  82. return func(o *loggerOptions) {
  83. o.flush = flush
  84. }
  85. }
  86. // WriteKlogBuffer sets a callback that will be invoked by klog to write output
  87. // produced by non-structured log calls like Infof.
  88. //
  89. // The buffer will contain exactly the same data that klog normally would write
  90. // into its own output stream(s). In particular this includes the header, if
  91. // klog is configured to write one. The callback then can divert that data into
  92. // its own output streams. The buffer may or may not end in a line break.
  93. //
  94. // Without such a callback, klog will call the logger's Info or Error method
  95. // with just the message string (i.e. no header).
  96. func WriteKlogBuffer(write func([]byte)) LoggerOption {
  97. return func(o *loggerOptions) {
  98. o.writeKlogBuffer = write
  99. }
  100. }
  101. // LoggerOption implements the functional parameter paradigm for
  102. // SetLoggerWithOptions.
  103. type LoggerOption func(o *loggerOptions)
  104. type loggerOptions struct {
  105. contextualLogger bool
  106. flush func()
  107. writeKlogBuffer func([]byte)
  108. }
  109. // logWriter combines a logger (always set) with a write callback (optional).
  110. type logWriter struct {
  111. Logger
  112. writeKlogBuffer func([]byte)
  113. }
  114. // ClearLogger removes a backing Logger implementation if one was set earlier
  115. // with SetLogger.
  116. //
  117. // Modifying the logger is not thread-safe and should be done while no other
  118. // goroutines invoke log calls, usually during program initialization.
  119. func ClearLogger() {
  120. logging.logger = nil
  121. logging.loggerOptions = loggerOptions{}
  122. }
  123. // EnableContextualLogging controls whether contextual logging is enabled.
  124. // By default it is enabled. When disabled, FromContext avoids looking up
  125. // the logger in the context and always returns the global logger.
  126. // LoggerWithValues, LoggerWithName, and NewContext become no-ops
  127. // and return their input logger respectively context. This may be useful
  128. // to avoid the additional overhead for contextual logging.
  129. //
  130. // This must be called during initialization before goroutines are started.
  131. func EnableContextualLogging(enabled bool) {
  132. logging.contextualLoggingEnabled = enabled
  133. }
  134. // FromContext retrieves a logger set by the caller or, if not set,
  135. // falls back to the program's global logger (a Logger instance or klog
  136. // itself).
  137. func FromContext(ctx context.Context) Logger {
  138. if logging.contextualLoggingEnabled {
  139. if logger, err := logr.FromContext(ctx); err == nil {
  140. return logger
  141. }
  142. }
  143. return Background()
  144. }
  145. // TODO can be used as a last resort by code that has no means of
  146. // receiving a logger from its caller. FromContext or an explicit logger
  147. // parameter should be used instead.
  148. func TODO() Logger {
  149. return Background()
  150. }
  151. // Background retrieves the fallback logger. It should not be called before
  152. // that logger was initialized by the program and not by code that should
  153. // better receive a logger via its parameters. TODO can be used as a temporary
  154. // solution for such code.
  155. func Background() Logger {
  156. if logging.loggerOptions.contextualLogger {
  157. // Is non-nil because logging.loggerOptions.contextualLogger is
  158. // only true if a logger was set.
  159. return logging.logger.Logger
  160. }
  161. return klogLogger
  162. }
  163. // LoggerWithValues returns logger.WithValues(...kv) when
  164. // contextual logging is enabled, otherwise the logger.
  165. func LoggerWithValues(logger Logger, kv ...interface{}) Logger {
  166. if logging.contextualLoggingEnabled {
  167. return logger.WithValues(kv...)
  168. }
  169. return logger
  170. }
  171. // LoggerWithName returns logger.WithName(name) when contextual logging is
  172. // enabled, otherwise the logger.
  173. func LoggerWithName(logger Logger, name string) Logger {
  174. if logging.contextualLoggingEnabled {
  175. return logger.WithName(name)
  176. }
  177. return logger
  178. }
  179. // NewContext returns logr.NewContext(ctx, logger) when
  180. // contextual logging is enabled, otherwise ctx.
  181. func NewContext(ctx context.Context, logger Logger) context.Context {
  182. if logging.contextualLoggingEnabled {
  183. return logr.NewContext(ctx, logger)
  184. }
  185. return ctx
  186. }