logger.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package fslog
  2. import (
  3. "fmt"
  4. "go.uber.org/zap"
  5. "go.uber.org/zap/zapcore"
  6. "gopkg.in/natefinch/lumberjack.v2"
  7. "io"
  8. "sync"
  9. )
  10. // 默认配置,准备了控制台、json文件两种输出方式
  11. var consoleEncoder zapcore.Encoder
  12. var jsonEncoder zapcore.Encoder
  13. var consoleSync zapcore.WriteSyncer
  14. var outputSync zapcore.WriteSyncer
  15. type outFileConfig struct {
  16. filename string
  17. maxSize int
  18. maxAge int
  19. maxBackups int
  20. localTime bool
  21. Compress bool
  22. }
  23. type Logger struct {
  24. // 互斥量
  25. // 用于内部不可并发逻辑使用
  26. lock sync.Mutex
  27. // 日志打印用
  28. logger zap.SugaredLogger
  29. // 当前日志打印级别
  30. // 借助zap内部级别设置机制,该机制
  31. // 内部使用乐观锁,协程安全
  32. lv zap.AtomicLevel
  33. // zapcore.Core 映射,存储不同来源/用途的core创建
  34. cores map[coreType][]CoreContext
  35. }
  36. func NewLogger() *Logger {
  37. logger := new(Logger)
  38. logger.lv = zap.NewAtomicLevelAt(zap.DebugLevel)
  39. logger.setCore(console, CoreContext{
  40. Core: zapcore.NewCore(consoleEncoder, consoleSync, logger.lv),
  41. Writer: consoleSync,
  42. })
  43. logger.setCore(output, CoreContext{
  44. Core: zapcore.NewCore(jsonEncoder, outputSync, logger.lv),
  45. Writer: outputSync,
  46. })
  47. logger.flush()
  48. return logger
  49. }
  50. // With 拼接自定义信息
  51. // 主要用于打印当前环境快照信息(变量或其他自定义信息)
  52. // 打印后,该信息会跟随日志一起打印
  53. func (l *Logger) With(k string, v any) *Logger {
  54. newL := l.clone()
  55. newL.logger = *newL.logger.With(zap.Any(k, v))
  56. return newL
  57. }
  58. // Debug 格式化打印调试级别日志
  59. // 不同于zap内部可变参数逻辑,该可变参数是用于,字符串格式化的
  60. func (l *Logger) Debug(msg string, vs ...any) {
  61. if len(vs) == 0 {
  62. l.logger.Debug(msg)
  63. return
  64. }
  65. l.logger.Debugf(fmt.Sprintf(msg, vs...))
  66. }
  67. // Info 格式化打印信息级别日志
  68. // 不同于zap内部可变参数逻辑,该可变参数是用于,字符串格式化的
  69. func (l *Logger) Info(msg string, vs ...any) {
  70. if len(vs) == 0 {
  71. l.logger.Info(msg)
  72. return
  73. }
  74. l.logger.Infof(msg, vs...)
  75. }
  76. // Warn 格式化打印警告级别日志
  77. // 不同于zap内部可变参数逻辑,该可变参数是用于,字符串格式化的
  78. func (l *Logger) Warn(msg string, vs ...any) {
  79. if len(vs) == 0 {
  80. l.logger.Warn(msg)
  81. return
  82. }
  83. l.logger.Warnf(msg, vs...)
  84. }
  85. // Error 打印错误级别日志
  86. // 该方法具有两种传参形式:
  87. // 1. error类型:会直接格式化打印%+v日志
  88. // 2. 信息(格式化)
  89. // 3. error+格式化信息:error会作为 With 格式存在,且依旧以%+v格式输出
  90. func (l *Logger) Error(vs ...any) {
  91. if len(vs) == 0 {
  92. return
  93. }
  94. err, ok := vs[0].(error)
  95. if ok {
  96. if len(vs) == 1 {
  97. l.logger.Errorf("%+v", err)
  98. return
  99. }
  100. withed := l.logger.With(zap.String("err", fmt.Sprintf("%+v", err)))
  101. msg, ok := vs[1].(string)
  102. if ok {
  103. withed.Errorf(msg, vs[2:])
  104. return
  105. }
  106. withed.Error(vs[1:])
  107. return
  108. }
  109. msg, ok := vs[0].(string)
  110. if ok {
  111. if len(vs) > 1 {
  112. l.logger.Errorf(msg, vs[1:]...)
  113. return
  114. }
  115. if len(vs) == 1 {
  116. l.logger.Error(vs[0])
  117. return
  118. }
  119. }
  120. }
  121. // Lv 获取当前日志打印级别
  122. func (l *Logger) Lv() zap.AtomicLevel {
  123. return l.lv
  124. }
  125. // SetLv 设置当前日志打印级别
  126. func (l *Logger) SetLv(lv Level) {
  127. l.lv.SetLevel(lv.zap())
  128. }
  129. // NewFileOutput 新增日志输出文件配置
  130. func (l *Logger) NewFileOutput(opts ...FileOutputOpt) {
  131. cfg := new(outFileConfig)
  132. for _, opt := range opts {
  133. opt(cfg)
  134. }
  135. l.newOut(&lumberjack.Logger{
  136. Filename: cfg.filename,
  137. MaxSize: cfg.maxSize,
  138. MaxAge: cfg.maxAge,
  139. MaxBackups: cfg.maxBackups,
  140. LocalTime: cfg.localTime,
  141. Compress: cfg.Compress,
  142. })
  143. }
  144. // NewOutput 新增日志输出位置
  145. func (l *Logger) NewOutput(writer io.Writer) {
  146. l.newOut(writer)
  147. }
  148. // AddCore 添加Core
  149. func (l *Logger) AddCore(core ...CoreContext) {
  150. l.setCore(third, core...)
  151. l.flush()
  152. }
  153. // Flush 将缓冲区日志刷新至目标
  154. func (l *Logger) Flush() {
  155. err := l.logger.Sync()
  156. if err != nil {
  157. Warn("flush log error: %s", err.Error())
  158. }
  159. }
  160. // newOut 设置日志写出位置
  161. func (l *Logger) newOut(writer io.Writer) {
  162. l.setCore(output, CoreContext{
  163. Core: zapcore.NewCore(jsonEncoder, zapcore.AddSync(writer), l.lv),
  164. Writer: writer,
  165. })
  166. l.flush()
  167. }
  168. // 保存内部core(不负责刷新 logger)
  169. func (l *Logger) setCore(ct coreType, core ...CoreContext) {
  170. l.lock.Lock()
  171. defer l.lock.Unlock()
  172. if l.cores == nil {
  173. l.cores = make(map[coreType][]CoreContext)
  174. }
  175. l.cores[ct] = append(l.cores[ct], core...)
  176. }
  177. // 刷新内部 logger
  178. // 刷新互斥,触发刷新后,刷新完成前依旧按照旧的配置执行
  179. func (l *Logger) flush() {
  180. l.lock.Lock()
  181. defer l.lock.Unlock()
  182. cores := make(coreContexts, 0, len(l.cores))
  183. for _, core := range l.cores {
  184. cores = append(cores, core...)
  185. }
  186. l.logger = *zap.New(
  187. zapcore.NewTee(cores.Cores()...),
  188. zap.AddCaller(),
  189. ).Sugar()
  190. }
  191. func (l *Logger) clone() *Logger {
  192. newL := &Logger{cores: l.cores, lv: l.lv}
  193. newL.flush()
  194. return newL
  195. }