Gin实战(3):系统日志管理

📌Gin实战(3):系统日志管理
引言
- 日志在Web开发中很有用,它记录了请求的URL、请求方法、响应状态码、请求耗时、客户端IP地址等关键信息,用于分析系统运行情况,排查问题等。
- 前情回顾:
二、Gin日志系统核心解析
-
内置日志中间件剖析
gin.Default()
自带的Logger和Recovery中间件- 默认日志格式:
[GIN] 2023/10/01 - 15:04:05 | 200 | 1.002539ms | 127.0.0.1 | GET "/api/v1/ping"
-
定制你的专属日志中间件
func LoggerMiddleware(logger logger.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
c.Next()
latency := time.Now().Sub(t)
logger.Info(
"latency", latency,
"status", c.Writer.Status(),
"path", c.Request.URL.Path,
"method", c.Request.Method,
"ip", c.ClientIP(),
"user-agent", c.Request.UserAgent(),
)
}
}
- 实现 logger.Logger
- 按场景划分:Debug/Info/Warn/Error
- 接口定义:
type Logger interface {
Info(...interface{})
Error(...interface{})
Debug(...interface{})
Warn(...interface{})
Fatal(...interface{})
}
- zap+file-rotatelogs 实现按日期切割的文件日志;
- WithPrefix 用来设置日志的一些公共字段,如服务名、版本号等
- zapcore.NewCore 设置 同时输出到控制台和日志文件 主要代码
var _ Logger = (*FileLogger)(nil)
type FileLogger struct {
log *zap.Logger
prefix []interface{}
}
var DefaultLoggerSavePath = "./logs"
func NewFileLogger(level Level) *FileLogger {
logSavePath := os.Getenv("GIN_LOG_PATH")
if logSavePath == "" {
logSavePath = DefaultLoggerSavePath
}
rotateLogger, err := rotatelogs.New(logSavePath+"/%Y%m%d.log", rotatelogs.WithMaxAge(time.Hour*24*30))
if err != nil {
log.Fatalf("logger.Setup err: %v", err)
}
encoder := zapcore.EncoderConfig{
TimeKey: "t",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stack",
EncodeTime: zapcore.ISO8601TimeEncoder,
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder,
}
zipLevel := levelToZipLevel(level)
core := zapcore.NewCore(
zapcore.NewConsoleEncoder(encoder),
zapcore.NewMultiWriteSyncer(
zapcore.AddSync(os.Stdout), //输出到控制台上
zapcore.AddSync(rotateLogger), //输出到文本中
), zipLevel)
zapLogger := zap.New(core,
zap.AddStacktrace(zap.NewAtomicLevelAt(zapcore.ErrorLevel)),
zap.AddCaller(),
zap.AddCallerSkip(2),
zap.Development())
return &FileLogger{
log: zapLogger,
}
}
func WithPrefix(logger *FileLogger, prefix ...interface{}) Logger {
kvs := make([]interface{}, 0, len(logger.prefix)+len(prefix))
kvs = append(kvs, logger.prefix...)
kvs = append(kvs, prefix...)
return &FileLogger{
log: logger.log,
prefix: kvs,
}
}
func levelToZipLevel(level Level) zapcore.Level {
switch level {
case Info:
return zapcore.InfoLevel
case Error:
return zapcore.ErrorLevel
case Debug:
return zapcore.DebugLevel
case Warn:
return zapcore.WarnLevel
case Fatal:
return zapcore.FatalLevel
default:
return zapcore.InfoLevel
}
}
func (f FileLogger) Info(i ...interface{}) {
f.InfoW(Info, i...)
}
func (f FileLogger) Error(i ...interface{}) {
f.InfoW(Error, i...)
}
func (f FileLogger) Debug(i ...interface{}) {
f.InfoW(Debug, i...)
}
func (f FileLogger) Warn(i ...interface{}) {
f.InfoW(Warn, i...)
}
func (f FileLogger) Fatal(i ...interface{}) {
f.InfoW(Fatal, i...)
}
三、项目中使用logger
- 配置日志中间件 internal/routers/router.go
func InitRouters(
account *service.AccountServer,
logger logger.Logger,
) *gin.Engine {
mdls := []gin.HandlerFunc{middleware.LoggerMiddleware(logger)}
mdls = append(mdls, middleware.AppMiddleware...)
r := initGin(mdls...)
// add api ping
r.GET("ping", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "this is ping", "timestamp": time.Now().Unix()})
})
r.POST("/login", account.Login)
return r
}
- 项目中使用logger main.go
fileLogger := logger.WithPrefix(logger.NewFileLogger(logger.Info),
"service.id", Id,
"service.name", Name,
"service.version", Version,
)
s, err := wireApp(config.Server, fileLogger)
service/account.go
type AccountServer struct {
logger logger.Logger
}
func NewAccountServer(logger logger.Logger) *AccountServer {
return &AccountServer{
logger: logger,
}
}
func (a *AccountServer) Login(ctx *gin.Context) {
a.logger.Info("msg", "this is login api")
ctx.JSON(200, gin.H{
"msg": "this is login api",
})
}
- 增加依赖参数后需要重新构建:执行命令
make generate
四、避坑指南
- 高并发场景下的日志性能优化
- 避免日志导致的goroutine泄漏
- 日志安全三原则:
- 不记录敏感信息
- 控制日志级别避免磁盘爆满
- 设置合理的日志保留策略
公众号后台回复「Gin实战」获取
