stdr.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. // Package stdr implements github.com/go-logr/logr.Logger in terms of
  14. // Go's standard log package.
  15. package stdr
  16. import (
  17. "log"
  18. "os"
  19. "github.com/go-logr/logr"
  20. "github.com/go-logr/logr/funcr"
  21. )
  22. // The global verbosity level. See SetVerbosity().
  23. var globalVerbosity int
  24. // SetVerbosity sets the global level against which all info logs will be
  25. // compared. If this is greater than or equal to the "V" of the logger, the
  26. // message will be logged. A higher value here means more logs will be written.
  27. // The previous verbosity value is returned. This is not concurrent-safe -
  28. // callers must be sure to call it from only one goroutine.
  29. func SetVerbosity(v int) int {
  30. old := globalVerbosity
  31. globalVerbosity = v
  32. return old
  33. }
  34. // New returns a logr.Logger which is implemented by Go's standard log package,
  35. // or something like it. If std is nil, this will use a default logger
  36. // instead.
  37. //
  38. // Example: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)))
  39. func New(std StdLogger) logr.Logger {
  40. return NewWithOptions(std, Options{})
  41. }
  42. // NewWithOptions returns a logr.Logger which is implemented by Go's standard
  43. // log package, or something like it. See New for details.
  44. func NewWithOptions(std StdLogger, opts Options) logr.Logger {
  45. if std == nil {
  46. // Go's log.Default() is only available in 1.16 and higher.
  47. std = log.New(os.Stderr, "", log.LstdFlags)
  48. }
  49. if opts.Depth < 0 {
  50. opts.Depth = 0
  51. }
  52. fopts := funcr.Options{
  53. LogCaller: funcr.MessageClass(opts.LogCaller),
  54. }
  55. sl := &logger{
  56. Formatter: funcr.NewFormatter(fopts),
  57. std: std,
  58. }
  59. // For skipping our own logger.Info/Error.
  60. sl.Formatter.AddCallDepth(1 + opts.Depth)
  61. return logr.New(sl)
  62. }
  63. // Options carries parameters which influence the way logs are generated.
  64. type Options struct {
  65. // Depth biases the assumed number of call frames to the "true" caller.
  66. // This is useful when the calling code calls a function which then calls
  67. // stdr (e.g. a logging shim to another API). Values less than zero will
  68. // be treated as zero.
  69. Depth int
  70. // LogCaller tells stdr to add a "caller" key to some or all log lines.
  71. // Go's log package has options to log this natively, too.
  72. LogCaller MessageClass
  73. // TODO: add an option to log the date/time
  74. }
  75. // MessageClass indicates which category or categories of messages to consider.
  76. type MessageClass int
  77. const (
  78. // None ignores all message classes.
  79. None MessageClass = iota
  80. // All considers all message classes.
  81. All
  82. // Info only considers info messages.
  83. Info
  84. // Error only considers error messages.
  85. Error
  86. )
  87. // StdLogger is the subset of the Go stdlib log.Logger API that is needed for
  88. // this adapter.
  89. type StdLogger interface {
  90. // Output is the same as log.Output and log.Logger.Output.
  91. Output(calldepth int, logline string) error
  92. }
  93. type logger struct {
  94. funcr.Formatter
  95. std StdLogger
  96. }
  97. var _ logr.LogSink = &logger{}
  98. var _ logr.CallDepthLogSink = &logger{}
  99. func (l logger) Enabled(level int) bool {
  100. return globalVerbosity >= level
  101. }
  102. func (l logger) Info(level int, msg string, kvList ...interface{}) {
  103. prefix, args := l.FormatInfo(level, msg, kvList)
  104. if prefix != "" {
  105. args = prefix + ": " + args
  106. }
  107. _ = l.std.Output(l.Formatter.GetDepth()+1, args)
  108. }
  109. func (l logger) Error(err error, msg string, kvList ...interface{}) {
  110. prefix, args := l.FormatError(err, msg, kvList)
  111. if prefix != "" {
  112. args = prefix + ": " + args
  113. }
  114. _ = l.std.Output(l.Formatter.GetDepth()+1, args)
  115. }
  116. func (l logger) WithName(name string) logr.LogSink {
  117. l.Formatter.AddName(name)
  118. return &l
  119. }
  120. func (l logger) WithValues(kvList ...interface{}) logr.LogSink {
  121. l.Formatter.AddValues(kvList)
  122. return &l
  123. }
  124. func (l logger) WithCallDepth(depth int) logr.LogSink {
  125. l.Formatter.AddCallDepth(depth)
  126. return &l
  127. }
  128. // Underlier exposes access to the underlying logging implementation. Since
  129. // callers only have a logr.Logger, they have to know which implementation is
  130. // in use, so this interface is less of an abstraction and more of way to test
  131. // type conversion.
  132. type Underlier interface {
  133. GetUnderlying() StdLogger
  134. }
  135. // GetUnderlying returns the StdLogger underneath this logger. Since StdLogger
  136. // is itself an interface, the result may or may not be a Go log.Logger.
  137. func (l logger) GetUnderlying() StdLogger {
  138. return l.std
  139. }