logger.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package logger
  2. import (
  3. "io"
  4. "os"
  5. "path/filepath"
  6. "time"
  7. "go.uber.org/zap"
  8. "go.uber.org/zap/zapcore"
  9. "gopkg.in/natefinch/lumberjack.v2"
  10. )
  11. const (
  12. // DefaultLevel the default log level
  13. DefaultLevel = zapcore.InfoLevel
  14. // DefaultTimeLayout the default time layout;
  15. DefaultTimeLayout = time.RFC3339
  16. )
  17. // Option custom setup config
  18. type Option func(*option)
  19. type option struct {
  20. level zapcore.Level
  21. fields map[string]string
  22. file io.Writer
  23. timeLayout string
  24. disableConsole bool
  25. }
  26. // WithDebugLevel only greater than 'level' will output
  27. func WithDebugLevel() Option {
  28. return func(opt *option) {
  29. opt.level = zapcore.DebugLevel
  30. }
  31. }
  32. // WithInfoLevel only greater than 'level' will output
  33. func WithInfoLevel() Option {
  34. return func(opt *option) {
  35. opt.level = zapcore.InfoLevel
  36. }
  37. }
  38. // WithWarnLevel only greater than 'level' will output
  39. func WithWarnLevel() Option {
  40. return func(opt *option) {
  41. opt.level = zapcore.WarnLevel
  42. }
  43. }
  44. // WithErrorLevel only greater than 'level' will output
  45. func WithErrorLevel() Option {
  46. return func(opt *option) {
  47. opt.level = zapcore.ErrorLevel
  48. }
  49. }
  50. // WithField add some field(s) to log
  51. func WithField(key, value string) Option {
  52. return func(opt *option) {
  53. opt.fields[key] = value
  54. }
  55. }
  56. // WithFileP write log to some file
  57. func WithFileP(file string) Option {
  58. dir := filepath.Dir(file)
  59. if err := os.MkdirAll(dir, 0766); err != nil {
  60. panic(err)
  61. }
  62. f, err := os.OpenFile(file, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0766)
  63. if err != nil {
  64. panic(err)
  65. }
  66. return func(opt *option) {
  67. opt.file = zapcore.Lock(f)
  68. }
  69. }
  70. // WithFileRotationP write log to some file with rotation
  71. func WithFileRotationP(file string) Option {
  72. dir := filepath.Dir(file)
  73. if err := os.MkdirAll(dir, 0766); err != nil {
  74. panic(err)
  75. }
  76. return func(opt *option) {
  77. opt.file = &lumberjack.Logger{ // concurrent-safed
  78. Filename: file, // 文件路径
  79. MaxSize: 128, // 单个文件最大尺寸,默认单位 M
  80. MaxBackups: 300, // 最多保留 300 个备份
  81. MaxAge: 30, // 最大时间,默认单位 day
  82. LocalTime: true, // 使用本地时间
  83. Compress: true, // 是否压缩 disabled by default
  84. }
  85. }
  86. }
  87. // WithTimeLayout custom time format
  88. func WithTimeLayout(timeLayout string) Option {
  89. return func(opt *option) {
  90. opt.timeLayout = timeLayout
  91. }
  92. }
  93. // WithEnableConsole write log to os.Stdout or os.Stderr
  94. func WithDisableConsole() Option {
  95. return func(opt *option) {
  96. opt.disableConsole = true
  97. }
  98. }
  99. // NewJSONLogger return a json-encoder zap logger,
  100. func NewJSONLogger(opts ...Option) (*zap.Logger, error) {
  101. opt := &option{level: DefaultLevel, fields: make(map[string]string)}
  102. for _, f := range opts {
  103. f(opt)
  104. }
  105. timeLayout := DefaultTimeLayout
  106. if opt.timeLayout != "" {
  107. timeLayout = opt.timeLayout
  108. }
  109. // similar to zap.NewProductionEncoderConfig()
  110. encoderConfig := zapcore.EncoderConfig{
  111. TimeKey: "time",
  112. LevelKey: "level",
  113. NameKey: "logger", // used by logger.Named(key); optional; useless
  114. CallerKey: "caller",
  115. MessageKey: "msg",
  116. StacktraceKey: "stacktrace", // use by zap.AddStacktrace; optional; useless
  117. LineEnding: zapcore.DefaultLineEnding,
  118. EncodeLevel: zapcore.LowercaseLevelEncoder, // 小写编码器
  119. EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
  120. enc.AppendString(t.Format(timeLayout))
  121. },
  122. EncodeDuration: zapcore.MillisDurationEncoder,
  123. EncodeCaller: zapcore.ShortCallerEncoder, // 全路径编码器
  124. }
  125. jsonEncoder := zapcore.NewJSONEncoder(encoderConfig)
  126. // lowPriority usd by info\debug\warn
  127. lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
  128. return lvl >= opt.level && lvl < zapcore.ErrorLevel
  129. })
  130. // highPriority usd by error\panic\fatal
  131. highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
  132. return lvl >= opt.level && lvl >= zapcore.ErrorLevel
  133. })
  134. stdout := zapcore.Lock(os.Stdout) // lock for concurrent safe
  135. stderr := zapcore.Lock(os.Stderr) // lock for concurrent safe
  136. core := zapcore.NewTee()
  137. if !opt.disableConsole {
  138. core = zapcore.NewTee(
  139. zapcore.NewCore(jsonEncoder,
  140. zapcore.NewMultiWriteSyncer(stdout),
  141. lowPriority,
  142. ),
  143. zapcore.NewCore(jsonEncoder,
  144. zapcore.NewMultiWriteSyncer(stderr),
  145. highPriority,
  146. ),
  147. )
  148. }
  149. if opt.file != nil {
  150. core = zapcore.NewTee(core,
  151. zapcore.NewCore(jsonEncoder,
  152. zapcore.AddSync(opt.file),
  153. zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
  154. return lvl >= opt.level
  155. }),
  156. ),
  157. )
  158. }
  159. logger := zap.New(core,
  160. zap.AddCaller(),
  161. zap.ErrorOutput(stderr),
  162. )
  163. for key, value := range opt.fields {
  164. logger = logger.WithOptions(zap.Fields(zapcore.Field{Key: key, Type: zapcore.StringType, String: value}))
  165. }
  166. return logger, nil
  167. }
  168. var _ Meta = (*meta)(nil)
  169. // Meta key-value
  170. type Meta interface {
  171. Key() string
  172. Value() any
  173. meta()
  174. }
  175. type meta struct {
  176. key string
  177. value any
  178. }
  179. func (m *meta) Key() string {
  180. return m.key
  181. }
  182. func (m *meta) Value() any {
  183. return m.value
  184. }
  185. func (m *meta) meta() {}
  186. // NewMeta create meat
  187. func NewMeta(key string, value any) Meta {
  188. return &meta{key: key, value: value}
  189. }
  190. // WrapMeta wrap meta to zap fields
  191. func WrapMeta(err error, metas ...Meta) (fields []zap.Field) {
  192. capacity := len(metas) + 1 // namespace meta
  193. if err != nil {
  194. capacity++
  195. }
  196. fields = make([]zap.Field, 0, capacity)
  197. if err != nil {
  198. fields = append(fields, zap.Error(err))
  199. }
  200. fields = append(fields, zap.Namespace("meta"))
  201. for _, meta := range metas {
  202. fields = append(fields, zap.Any(meta.Key(), meta.Value()))
  203. }
  204. return
  205. }