log.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package logrus
  2. import (
  3. "io"
  4. "runtime"
  5. "strings"
  6. "sync"
  7. "github.com/sirupsen/logrus"
  8. "github.com/ydb-platform/ydb/library/go/core/log"
  9. )
  10. /* Call frame calculations are copied from logrus package */
  11. var (
  12. // qualified package name, cached at first use
  13. logrusPackage string
  14. // Positions in the call stack when tracing to report the calling method
  15. minimumCallerDepth int
  16. // Used for caller information initialisation
  17. callerInitOnce sync.Once
  18. )
  19. const (
  20. maximumCallerDepth int = 25
  21. knownLogrusFrames int = 4
  22. )
  23. func init() {
  24. // start at the bottom of the stack before the package-name cache is primed
  25. minimumCallerDepth = 1
  26. }
  27. // getPackageName reduces a fully qualified function name to the package name
  28. // There really ought to be to be a better way...
  29. func getPackageName(f string) string {
  30. for {
  31. lastPeriod := strings.LastIndex(f, ".")
  32. lastSlash := strings.LastIndex(f, "/")
  33. if lastPeriod > lastSlash {
  34. f = f[:lastPeriod]
  35. } else {
  36. break
  37. }
  38. }
  39. return f
  40. }
  41. func getCallerDepth() int {
  42. // cache this package's fully-qualified name
  43. callerInitOnce.Do(func() {
  44. pcs := make([]uintptr, maximumCallerDepth)
  45. _ = runtime.Callers(0, pcs)
  46. // dynamic get the package name and the minimum caller depth
  47. logrusIsNext := false
  48. for i := 0; i < maximumCallerDepth; i++ {
  49. funcName := runtime.FuncForPC(pcs[i]).Name()
  50. if logrusIsNext {
  51. logrusPackage = getPackageName(funcName)
  52. break
  53. }
  54. if strings.Contains(funcName, "LogrusAdapter") {
  55. logrusIsNext = true
  56. continue
  57. }
  58. }
  59. minimumCallerDepth = knownLogrusFrames
  60. })
  61. // Restrict the lookback frames to avoid runaway lookups
  62. pcs := make([]uintptr, maximumCallerDepth)
  63. depth := runtime.Callers(minimumCallerDepth, pcs)
  64. frames := runtime.CallersFrames(pcs[:depth])
  65. callerDepth := minimumCallerDepth
  66. for f, again := frames.Next(); again; f, again = frames.Next() {
  67. pkg := getPackageName(f.Function)
  68. // If the caller isn't part of this package, we're done
  69. if pkg != logrusPackage {
  70. return callerDepth - 2
  71. }
  72. callerDepth++
  73. }
  74. // if we got here, we failed to find the caller's context
  75. return 0
  76. }
  77. func convertLevel(level log.Level) logrus.Level {
  78. switch level {
  79. case log.TraceLevel:
  80. return logrus.TraceLevel
  81. case log.DebugLevel:
  82. return logrus.DebugLevel
  83. case log.InfoLevel:
  84. return logrus.InfoLevel
  85. case log.WarnLevel:
  86. return logrus.WarnLevel
  87. case log.ErrorLevel:
  88. return logrus.ErrorLevel
  89. case log.FatalLevel:
  90. return logrus.FatalLevel
  91. }
  92. return logrus.PanicLevel
  93. }
  94. func SetLevel(level log.Level) {
  95. logrus.SetLevel(convertLevel(level))
  96. }
  97. type LogrusAdapter struct {
  98. logger log.Logger
  99. adaptCallstack bool
  100. convertPrefix bool
  101. }
  102. func (a *LogrusAdapter) Format(entry *logrus.Entry) ([]byte, error) {
  103. var name *string
  104. fields := make([]log.Field, 0, len(entry.Data))
  105. for key, val := range entry.Data {
  106. skip := false
  107. if a.convertPrefix && key == "prefix" {
  108. if w, ok := val.(string); ok {
  109. name = &w
  110. skip = true
  111. }
  112. }
  113. if !skip {
  114. fields = append(fields, log.Any(key, val))
  115. }
  116. }
  117. var logger log.Logger
  118. if a.adaptCallstack {
  119. logger = log.AddCallerSkip(a.logger, getCallerDepth())
  120. } else {
  121. logger = a.logger
  122. }
  123. if a.convertPrefix && name != nil {
  124. logger = logger.WithName(*name)
  125. }
  126. switch entry.Level {
  127. case logrus.TraceLevel:
  128. logger.Trace(entry.Message, fields...)
  129. case logrus.DebugLevel:
  130. logger.Debug(entry.Message, fields...)
  131. case logrus.InfoLevel:
  132. logger.Info(entry.Message, fields...)
  133. case logrus.WarnLevel:
  134. logger.Warn(entry.Message, fields...)
  135. case logrus.ErrorLevel:
  136. logger.Error(entry.Message, fields...)
  137. case logrus.FatalLevel:
  138. logger.Fatal(entry.Message, fields...)
  139. case logrus.PanicLevel:
  140. logger.Fatal(entry.Message, fields...)
  141. }
  142. return nil, nil
  143. }
  144. type Option func(*LogrusAdapter)
  145. func DontAdaptCallstack() Option {
  146. return func(adapter *LogrusAdapter) {
  147. adapter.adaptCallstack = false
  148. }
  149. }
  150. func ConvertPrefix() Option {
  151. return func(adapter *LogrusAdapter) {
  152. adapter.convertPrefix = true
  153. }
  154. }
  155. // AdaptLogrus replaces logr formatter by wrapped logger
  156. func AdaptLogrus(logr *logrus.Logger, logger log.Logger, level log.Level, opts ...Option) {
  157. logr.SetLevel(convertLevel(level))
  158. adapter := &LogrusAdapter{logger, true, false}
  159. for _, opt := range opts {
  160. opt(adapter)
  161. }
  162. logr.SetFormatter(adapter)
  163. logr.SetOutput(io.Discard)
  164. }
  165. // AdaptStandardLogger replaces logrus.StandardLogger() formatter by wrapped logger
  166. func AdaptStandardLogger(logger log.Logger, level log.Level, opts ...Option) {
  167. AdaptLogrus(logrus.StandardLogger(), logger, level, opts...)
  168. }