logger.go 5.7 KB

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