164 lines
4.3 KiB
Go
164 lines
4.3 KiB
Go
package lxzap
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"net"
|
||
"net/http"
|
||
"net/http/httputil"
|
||
"os"
|
||
"runtime/debug"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/natefinch/lumberjack"
|
||
"go.uber.org/zap"
|
||
"go.uber.org/zap/zapcore"
|
||
)
|
||
|
||
var Lg *zap.Logger
|
||
|
||
// Level = "info"
|
||
// Filename = "./log/a.log"
|
||
// MaxSize = 2
|
||
// MaxAge = 1000
|
||
// MaxBackups = 1000
|
||
type ZapLogConfig struct {
|
||
Level string `json:"level"`
|
||
Filename string `json:"filename"`
|
||
MaxSize int `json:"maxsize"`
|
||
MaxAge int `json:"max_age"`
|
||
MaxBackups int `json:"max_backups"`
|
||
}
|
||
|
||
// InitZapLogger 初始化Logger
|
||
func InitZapLogger(cfg ZapLogConfig, env string) (err error) {
|
||
writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
|
||
encoder := getEncoder(env)
|
||
var l = new(zapcore.Level)
|
||
err = l.UnmarshalText([]byte(cfg.Level))
|
||
if err != nil {
|
||
return
|
||
}
|
||
// 创建 zap 核心
|
||
core := zapcore.NewCore(
|
||
encoder,
|
||
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), writeSyncer),
|
||
l,
|
||
)
|
||
if env != "dev" {
|
||
core = zapcore.NewCore(encoder, writeSyncer, l)
|
||
}
|
||
// 创建 zap 日志对象
|
||
Lg = zap.New(core, zap.AddCaller())
|
||
zap.ReplaceGlobals(Lg) // 替换zap包中全局的logger实例,后续在其他包中只需使用zap.L()调用即可
|
||
return
|
||
}
|
||
|
||
func getEncoder(env string) zapcore.Encoder {
|
||
encoderConfig := zap.NewProductionEncoderConfig()
|
||
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||
encoderConfig.TimeKey = "time"
|
||
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
|
||
encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
|
||
encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
|
||
l := zapcore.NewConsoleEncoder(encoderConfig)
|
||
if env != "dev" {
|
||
l = zapcore.NewJSONEncoder(encoderConfig)
|
||
}
|
||
return l
|
||
}
|
||
|
||
func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {
|
||
lumberJackLogger := &lumberjack.Logger{
|
||
Filename: filename,
|
||
MaxSize: maxSize,
|
||
MaxBackups: maxBackup,
|
||
MaxAge: maxAge,
|
||
LocalTime: true,
|
||
}
|
||
return zapcore.AddSync(lumberJackLogger)
|
||
}
|
||
|
||
// GinLogger 接收gin框架默认的日志
|
||
func GinLogger() gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
start := time.Now()
|
||
path := c.Request.URL.Path
|
||
query := c.Request.URL.RawQuery
|
||
spanID := NewUUID()
|
||
c.Set("X-Span-ID", spanID)
|
||
c.Next()
|
||
cost := time.Since(start)
|
||
Lg.Info(path,
|
||
zap.Int("status", c.Writer.Status()),
|
||
zap.String("spanId", strconv.Itoa(int(spanID))),
|
||
zap.String("method", c.Request.Method),
|
||
zap.String("path", path),
|
||
zap.String("query", query),
|
||
zap.String("ip", c.ClientIP()),
|
||
zap.String("user-agent", c.Request.UserAgent()),
|
||
zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
|
||
zap.Duration("cost", cost),
|
||
)
|
||
}
|
||
}
|
||
|
||
// GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志
|
||
func GinRecovery(stack bool) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
defer func() {
|
||
if err := recover(); err != nil {
|
||
// Check for a broken connection, as it is not really a
|
||
// condition that warrants a panic stack trace.
|
||
var brokenPipe bool
|
||
if ne, ok := err.(*net.OpError); ok {
|
||
if se, ok := ne.Err.(*os.SyscallError); ok {
|
||
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
|
||
brokenPipe = true
|
||
}
|
||
}
|
||
}
|
||
|
||
httpRequest, _ := httputil.DumpRequest(c.Request, false)
|
||
if brokenPipe {
|
||
Lg.Error(c.Request.URL.Path,
|
||
zap.Any("error", err),
|
||
zap.String("request", string(httpRequest)),
|
||
)
|
||
// If the connection is dead, we can't write a status to it.
|
||
c.Error(err.(error)) // nolint: errcheck
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
if stack {
|
||
Lg.Error("[Recovery from panic]",
|
||
zap.Any("error", err),
|
||
zap.String("request", string(httpRequest)),
|
||
zap.String("stack", string(debug.Stack())),
|
||
)
|
||
} else {
|
||
Lg.Error("[Recovery from panic]",
|
||
zap.Any("error", err),
|
||
zap.String("request", string(httpRequest)),
|
||
)
|
||
}
|
||
c.AbortWithStatus(http.StatusInternalServerError)
|
||
}
|
||
}()
|
||
c.Next()
|
||
}
|
||
}
|
||
|
||
func Log(msg any, ctx context.Context) {
|
||
v := ctx.Value("X-Span-ID")
|
||
spanId := fmt.Sprintf("%v", v)
|
||
//zap.L().Info(`spanId:"` + spanId + `"log:` + msg)
|
||
zap.L().Info("fmt",
|
||
zap.Any("content", msg),
|
||
zap.String("spanId", spanId))
|
||
}
|