logger.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package logger
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "os"
  9. "time"
  10. "gorm.io/gorm/utils"
  11. )
  12. var ErrRecordNotFound = errors.New("record not found")
  13. // Colors
  14. const (
  15. Reset = "\033[0m"
  16. Red = "\033[31m"
  17. Green = "\033[32m"
  18. Yellow = "\033[33m"
  19. Blue = "\033[34m"
  20. Magenta = "\033[35m"
  21. Cyan = "\033[36m"
  22. White = "\033[37m"
  23. BlueBold = "\033[34;1m"
  24. MagentaBold = "\033[35;1m"
  25. RedBold = "\033[31;1m"
  26. YellowBold = "\033[33;1m"
  27. )
  28. // LogLevel
  29. type LogLevel int
  30. const (
  31. Silent LogLevel = iota + 1
  32. Error
  33. Warn
  34. Info
  35. )
  36. // Writer log writer interface
  37. type Writer interface {
  38. Printf(string, ...interface{})
  39. }
  40. type Config struct {
  41. SlowThreshold time.Duration
  42. Colorful bool
  43. IgnoreRecordNotFoundError bool
  44. LogLevel LogLevel
  45. }
  46. // Interface logger interface
  47. type Interface interface {
  48. LogMode(LogLevel) Interface
  49. Info(context.Context, string, ...interface{})
  50. Warn(context.Context, string, ...interface{})
  51. Error(context.Context, string, ...interface{})
  52. Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error)
  53. }
  54. var (
  55. Discard = New(log.New(ioutil.Discard, "", log.LstdFlags), Config{})
  56. Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), Config{
  57. SlowThreshold: 200 * time.Millisecond,
  58. LogLevel: Warn,
  59. IgnoreRecordNotFoundError: false,
  60. Colorful: true,
  61. })
  62. Recorder = traceRecorder{Interface: Default, BeginAt: time.Now()}
  63. )
  64. func New(writer Writer, config Config) Interface {
  65. var (
  66. infoStr = "%s\n[info] "
  67. warnStr = "%s\n[warn] "
  68. errStr = "%s\n[error] "
  69. traceStr = "%s\n[%.3fms] [rows:%v] %s"
  70. traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s"
  71. traceErrStr = "%s %s\n[%.3fms] [rows:%v] %s"
  72. )
  73. if config.Colorful {
  74. infoStr = Green + "%s\n" + Reset + Green + "[info] " + Reset
  75. warnStr = BlueBold + "%s\n" + Reset + Magenta + "[warn] " + Reset
  76. errStr = Magenta + "%s\n" + Reset + Red + "[error] " + Reset
  77. traceStr = Green + "%s\n" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s"
  78. traceWarnStr = Green + "%s " + Yellow + "%s\n" + Reset + RedBold + "[%.3fms] " + Yellow + "[rows:%v]" + Magenta + " %s" + Reset
  79. traceErrStr = RedBold + "%s " + MagentaBold + "%s\n" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s"
  80. }
  81. return &logger{
  82. Writer: writer,
  83. Config: config,
  84. infoStr: infoStr,
  85. warnStr: warnStr,
  86. errStr: errStr,
  87. traceStr: traceStr,
  88. traceWarnStr: traceWarnStr,
  89. traceErrStr: traceErrStr,
  90. }
  91. }
  92. type logger struct {
  93. Writer
  94. Config
  95. infoStr, warnStr, errStr string
  96. traceStr, traceErrStr, traceWarnStr string
  97. }
  98. // LogMode log mode
  99. func (l *logger) LogMode(level LogLevel) Interface {
  100. newlogger := *l
  101. newlogger.LogLevel = level
  102. return &newlogger
  103. }
  104. // Info print info
  105. func (l logger) Info(ctx context.Context, msg string, data ...interface{}) {
  106. if l.LogLevel >= Info {
  107. l.Printf(l.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
  108. }
  109. }
  110. // Warn print warn messages
  111. func (l logger) Warn(ctx context.Context, msg string, data ...interface{}) {
  112. if l.LogLevel >= Warn {
  113. l.Printf(l.warnStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
  114. }
  115. }
  116. // Error print error messages
  117. func (l logger) Error(ctx context.Context, msg string, data ...interface{}) {
  118. if l.LogLevel >= Error {
  119. l.Printf(l.errStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
  120. }
  121. }
  122. // Trace print sql message
  123. func (l logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
  124. if l.LogLevel <= Silent {
  125. return
  126. }
  127. elapsed := time.Since(begin)
  128. switch {
  129. case err != nil && l.LogLevel >= Error && (!errors.Is(err, ErrRecordNotFound) || !l.IgnoreRecordNotFoundError):
  130. sql, rows := fc()
  131. if rows == -1 {
  132. l.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
  133. } else {
  134. l.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
  135. }
  136. case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= Warn:
  137. sql, rows := fc()
  138. slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold)
  139. if rows == -1 {
  140. l.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
  141. } else {
  142. l.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
  143. }
  144. case l.LogLevel == Info:
  145. sql, rows := fc()
  146. if rows == -1 {
  147. l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
  148. } else {
  149. l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
  150. }
  151. }
  152. }
  153. type traceRecorder struct {
  154. Interface
  155. BeginAt time.Time
  156. SQL string
  157. RowsAffected int64
  158. Err error
  159. }
  160. func (l traceRecorder) New() *traceRecorder {
  161. return &traceRecorder{Interface: l.Interface, BeginAt: time.Now()}
  162. }
  163. func (l *traceRecorder) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
  164. l.BeginAt = begin
  165. l.SQL, l.RowsAffected = fc()
  166. l.Err = err
  167. }