lxzap.go 4.2 KB

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