You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
3.8 KiB

package logger
import (
"net"
"net/http"
"net/http/httputil"
"os"
"runtime/debug"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/natefinch/lumberjack"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func Init() (err error) {
writerSyncer := getLogWriter(
viper.GetString("logger.name"),
viper.GetInt("logger.max_sizes"),
viper.GetInt("logger.max_age"),
viper.GetInt("logger.max_backups"),
)
encoder := getEncoder()
var l = new(zapcore.Level)
err = l.UnmarshalText([]byte(viper.GetString("logger.level")))
if err != nil {
return
}
core := zapcore.NewCore(encoder, writerSyncer, zapcore.DebugLevel)
lg := zap.New(core, zap.AddCaller())
//用 lg 来代替全局 logger
zap.ReplaceGlobals(lg)
return
}
func getEncoder() zapcore.Encoder {
// return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
encoder := zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
// encoder.EncodeTime = zapcore.ISO8601TimeEncoder
// encoder.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoder)
}
func getLogWriter(filename string, maxSize int, maxBackups int, maxAge int) zapcore.WriteSyncer {
lumberjackLogger := &lumberjack.Logger{
Filename: filename,
MaxSize: maxSize, // 最大大小单位是M
MaxBackups: maxBackups, //最大备份数量,在上个文件达到最大大小时,开始备份文件
MaxAge: maxAge, //最大保持备份天数
Compress: false, //是否压缩
}
return zapcore.AddSync(lumberjackLogger)
}
func GinLogger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
query := c.Request.URL.RawQuery
ip := c.ClientIP()
user_agent := c.Request.UserAgent()
err := c.Errors.ByType(gin.ErrorTypePrivate).String()
c.Next()
cost := time.Since(start)
zap.L().Info(
path,
zap.Int("status", c.Writer.Status()),
zap.String("method", c.Request.Method),
zap.String("path", path),
zap.String("query", query),
zap.String("ip", ip),
zap.String("user-agent", user_agent),
zap.String("errors", err),
zap.Duration("cost", cost),
)
}
}
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 {
zap.L().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 {
zap.L().Error("[Recovery from panic]",
zap.Any("error", err),
zap.String("request", string(httpRequest)),
zap.String("stack", string(debug.Stack())),
)
} else {
zap.L().Error("[Recovery from panic]",
zap.Any("error", err),
zap.String("request", string(httpRequest)),
)
}
c.AbortWithStatus(http.StatusInternalServerError)
}
}()
c.Next()
}
}