default.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package logger
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "runtime"
  7. "sort"
  8. "strings"
  9. "sync"
  10. "time"
  11. dlog "device-manage/debug/log"
  12. )
  13. func init() {
  14. lvl, err := GetLevel(os.Getenv("GO_ADMIN_LOG_LEVEL"))
  15. if err != nil {
  16. lvl = InfoLevel
  17. }
  18. DefaultLogger = NewHelper(NewLogger(WithLevel(lvl)))
  19. }
  20. type defaultLogger struct {
  21. sync.RWMutex
  22. opts Options
  23. }
  24. // Init(opts...) should only overwrite provided options
  25. func (l *defaultLogger) Init(opts ...Option) error {
  26. for _, o := range opts {
  27. o(&l.opts)
  28. }
  29. return nil
  30. }
  31. func (l *defaultLogger) String() string {
  32. return "default"
  33. }
  34. func (l *defaultLogger) Fields(fields map[string]interface{}) Logger {
  35. l.Lock()
  36. l.opts.Fields = copyFields(fields)
  37. l.Unlock()
  38. return l
  39. }
  40. func copyFields(src map[string]interface{}) map[string]interface{} {
  41. dst := make(map[string]interface{}, len(src))
  42. for k, v := range src {
  43. dst[k] = v
  44. }
  45. return dst
  46. }
  47. // logCallerfilePath returns a package/file:line description of the caller,
  48. // preserving only the leaf directory name and file name.
  49. func logCallerfilePath(loggingFilePath string) string {
  50. // To make sure we trim the path correctly on Windows too, we
  51. // counter-intuitively need to use '/' and *not* os.PathSeparator here,
  52. // because the path given originates from Go stdlib, specifically
  53. // runtime.Caller() which (as of Mar/17) returns forward slashes even on
  54. // Windows.
  55. //
  56. // See https://github.com/golang/go/issues/3335
  57. // and https://github.com/golang/go/issues/18151
  58. //
  59. // for discussion on the issue on Go side.
  60. idx := strings.LastIndexByte(loggingFilePath, '/')
  61. if idx == -1 {
  62. return loggingFilePath
  63. }
  64. idx = strings.LastIndexByte(loggingFilePath[:idx], '/')
  65. if idx == -1 {
  66. return loggingFilePath
  67. }
  68. return loggingFilePath[idx+1:]
  69. }
  70. func (l *defaultLogger) Log(level Level, v ...interface{}) {
  71. // TODO decide does we need to write message if log level not used?
  72. if !l.opts.Level.Enabled(level) {
  73. return
  74. }
  75. l.RLock()
  76. fields := copyFields(l.opts.Fields)
  77. l.RUnlock()
  78. fields["level"] = level.String()
  79. if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
  80. fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line)
  81. }
  82. rec := dlog.Record{
  83. Timestamp: time.Now(),
  84. Message: fmt.Sprint(v...),
  85. Metadata: make(map[string]string, len(fields)),
  86. }
  87. keys := make([]string, 0, len(fields))
  88. for k, v := range fields {
  89. keys = append(keys, k)
  90. rec.Metadata[k] = fmt.Sprintf("%v", v)
  91. }
  92. sort.Strings(keys)
  93. metadata := ""
  94. for _, k := range keys {
  95. metadata += fmt.Sprintf(" %s=%v", k, fields[k])
  96. }
  97. t := rec.Timestamp.Format("2006-01-02 15:04:05")
  98. fmt.Printf("%s %s %v\n", t, metadata, rec.Message)
  99. }
  100. func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) {
  101. // TODO decide does we need to write message if log level not used?
  102. if level < l.opts.Level {
  103. return
  104. }
  105. l.RLock()
  106. fields := copyFields(l.opts.Fields)
  107. l.RUnlock()
  108. fields["level"] = level.String()
  109. if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
  110. fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line)
  111. }
  112. rec := dlog.Record{
  113. Timestamp: time.Now(),
  114. Message: fmt.Sprintf(format, v...),
  115. Metadata: make(map[string]string, len(fields)),
  116. }
  117. keys := make([]string, 0, len(fields))
  118. for k, v := range fields {
  119. keys = append(keys, k)
  120. rec.Metadata[k] = fmt.Sprintf("%v", v)
  121. }
  122. sort.Strings(keys)
  123. metadata := ""
  124. for _, k := range keys {
  125. metadata += fmt.Sprintf(" %s=%v", k, fields[k])
  126. }
  127. t := rec.Timestamp.Format("2006-01-02 15:04:05")
  128. fmt.Printf("%s %s %v\n", t, metadata, rec.Message)
  129. }
  130. func (l *defaultLogger) Options() Options {
  131. // not guard against options Context values
  132. l.RLock()
  133. opts := l.opts
  134. opts.Fields = copyFields(l.opts.Fields)
  135. l.RUnlock()
  136. return opts
  137. }
  138. // NewLogger builds a new logger based on options
  139. func NewLogger(opts ...Option) Logger {
  140. // Default options
  141. options := Options{
  142. Level: InfoLevel,
  143. Fields: make(map[string]interface{}),
  144. Out: os.Stderr,
  145. CallerSkipCount: 2,
  146. Context: context.Background(),
  147. }
  148. l := &defaultLogger{opts: options}
  149. if err := l.Init(opts...); err != nil {
  150. l.Log(FatalLevel, err)
  151. }
  152. return l
  153. }