logger.go 6.3 KB

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