lxzap.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package lxzap
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "net/http"
  7. "net/http/httputil"
  8. "os"
  9. "runtime/debug"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/gin-gonic/gin"
  14. "github.com/natefinch/lumberjack"
  15. "go.uber.org/zap"
  16. "go.uber.org/zap/zapcore"
  17. )
  18. var Lg *zap.Logger
  19. // Level = "info"
  20. // Filename = "./log/a.log"
  21. // MaxSize = 2
  22. // MaxAge = 1000
  23. // MaxBackups = 1000
  24. type ZapLogConfig struct {
  25. Level string `json:"level"`
  26. Filename string `json:"filename"`
  27. MaxSize int `json:"maxsize"`
  28. MaxAge int `json:"max_age"`
  29. MaxBackups int `json:"max_backups"`
  30. }
  31. // InitZapLogger 初始化Logger
  32. func InitZapLogger(cfg ZapLogConfig, env string) (err error) {
  33. writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
  34. encoder := getEncoder(env)
  35. var l = new(zapcore.Level)
  36. err = l.UnmarshalText([]byte(cfg.Level))
  37. if err != nil {
  38. return
  39. }
  40. // 创建 zap 核心
  41. core := zapcore.NewCore(
  42. encoder,
  43. zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), writeSyncer),
  44. l,
  45. )
  46. if env != "dev" {
  47. core = zapcore.NewCore(encoder, writeSyncer, l)
  48. }
  49. // 创建 zap 日志对象
  50. Lg = zap.New(core, zap.AddCaller())
  51. zap.ReplaceGlobals(Lg) // 替换zap包中全局的logger实例,后续在其他包中只需使用zap.L()调用即可
  52. return
  53. }
  54. func getEncoder(env string) zapcore.Encoder {
  55. encoderConfig := zap.NewProductionEncoderConfig()
  56. encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
  57. encoderConfig.TimeKey = "time"
  58. encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
  59. encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
  60. encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
  61. l := zapcore.NewConsoleEncoder(encoderConfig)
  62. if env != "dev" {
  63. l = zapcore.NewJSONEncoder(encoderConfig)
  64. }
  65. return l
  66. }
  67. func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {
  68. lumberJackLogger := &lumberjack.Logger{
  69. Filename: filename,
  70. MaxSize: maxSize,
  71. MaxBackups: maxBackup,
  72. MaxAge: maxAge,
  73. LocalTime: true,
  74. }
  75. return zapcore.AddSync(lumberJackLogger)
  76. }
  77. // GinLogger 接收gin框架默认的日志
  78. func GinLogger() gin.HandlerFunc {
  79. return func(c *gin.Context) {
  80. start := time.Now()
  81. path := c.Request.URL.Path
  82. query := c.Request.URL.RawQuery
  83. spanID := NewUUID()
  84. c.Set("X-Span-ID", spanID)
  85. cost := time.Since(start)
  86. Lg.Info(path,
  87. zap.Int("status", c.Writer.Status()),
  88. zap.String("spanId", strconv.Itoa(int(spanID))),
  89. zap.String("method", c.Request.Method),
  90. zap.String("path", path),
  91. zap.String("query", query),
  92. zap.String("ip", c.ClientIP()),
  93. zap.String("user-agent", c.Request.UserAgent()),
  94. zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
  95. zap.Duration("cost", cost),
  96. )
  97. c.Next()
  98. }
  99. }
  100. // GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志
  101. func GinRecovery(stack bool) gin.HandlerFunc {
  102. return func(c *gin.Context) {
  103. defer func() {
  104. if err := recover(); err != nil {
  105. // Check for a broken connection, as it is not really a
  106. // condition that warrants a panic stack trace.
  107. var brokenPipe bool
  108. if ne, ok := err.(*net.OpError); ok {
  109. if se, ok := ne.Err.(*os.SyscallError); ok {
  110. if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
  111. brokenPipe = true
  112. }
  113. }
  114. }
  115. httpRequest, _ := httputil.DumpRequest(c.Request, false)
  116. if brokenPipe {
  117. Lg.Error(c.Request.URL.Path,
  118. zap.Any("error", err),
  119. zap.String("request", string(httpRequest)),
  120. )
  121. // If the connection is dead, we can't write a status to it.
  122. c.Error(err.(error)) // nolint: errcheck
  123. c.Abort()
  124. return
  125. }
  126. if stack {
  127. Lg.Error("[Recovery from panic]",
  128. zap.Any("error", err),
  129. zap.String("request", string(httpRequest)),
  130. zap.String("stack", string(debug.Stack())),
  131. )
  132. } else {
  133. Lg.Error("[Recovery from panic]",
  134. zap.Any("error", err),
  135. zap.String("request", string(httpRequest)),
  136. )
  137. }
  138. c.AbortWithStatus(http.StatusInternalServerError)
  139. }
  140. }()
  141. c.Next()
  142. }
  143. }
  144. func Log(msg any, ctx context.Context) {
  145. v := ctx.Value("X-Span-ID")
  146. spanId := fmt.Sprintf("%v", v)
  147. //zap.L().Info(`spanId:"` + spanId + `"log:` + msg)
  148. zap.L().Info("fmt",
  149. zap.Any("content", msg),
  150. zap.String("spanId", spanId))
  151. }