default.go 4.1 KB

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