官方方法

官方文档

zap三方库

可以更高效的记录

    config := zap.Config{
        Level:            zap.NewAtomicLevelAt(zap.InfoLevel), // 日志级别
        Development:      false,                               // 是否是开发模式
        Encoding:         "console",                           // 日志格式:json 或 console
        OutputPaths:      []string{"./gin.log"},               // 日志输出路径
        ErrorOutputPaths: []string{"./gin.log"},               // 错误日志输出路径
        EncoderConfig: zapcore.EncoderConfig{
            TimeKey:  "time",
            LevelKey: "level",
            NameKey:  "logger",
            // CallerKey:      "caller",
            MessageKey:     "msg",
            StacktraceKey:  "stacktrace",
            LineEnding:     zapcore.DefaultLineEnding,
            EncodeLevel:    zapcore.CapitalLevelEncoder,   // 将日志级别转换为大写 (INFO, WARN, ERROR)
            EncodeTime:     zapcore.ISO8601TimeEncoder,    // 时间格式:ISO8601
            EncodeDuration: zapcore.StringDurationEncoder, // 持续时间格式化为字符串
            // EncodeCaller:   zapcore.ShortCallerEncoder,    // 简短文件名 + 行号
            EncodeName: zapcore.FullNameEncoder,
        },
    }

    logger, _ := config.Build()
    defer logger.Sync() 


       r.Use(GinZap(logger))
func GinZap(logger *zap.Logger) gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()

        // 处理请求
        c.Next()

        // 计算处理时间
        latency := time.Since(start)

        // 记录日志
        logger.Info("request",
            zap.String("method", c.Request.Method),
            zap.String("path", c.Request.URL.Path),
            zap.Int("status", c.Writer.Status()),
            zap.Duration("latency", latency),
            zap.String("clientIP", c.ClientIP()),
            zap.String("userAgent", c.Request.UserAgent()),
            zap.String("error", c.Errors.ByType(gin.ErrorTypePrivate).String()),
        )
    }
}

GPT给出的高并发方案

使用缓冲通道或队列将日志写入操作从主请求处理流程中剥离出来,日志先写入内存队列,再由单独的goroutine异步处理并写入文件。

package main

import (
    "github.com/gin-gonic/gin"
    "log"
    "os"
    "time"
    "fmt"
)

// 日志通道
var logChannel = make(chan string, 1000)

// 异步日志写入
func asyncLogWriter() {
    // 打开日志文件
    f, err := os.OpenFile("gin_requests.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Fatalf("无法打开日志文件: %v", err)
    }
    defer f.Close()

    // 监听日志通道
    for logMsg := range logChannel {
        _, err := f.WriteString(logMsg + "\n")
        if err != nil {
            log.Printf("写入日志文件失败: %v", err)
        }
    }
}

// 日志记录到通道的中间件
func LoggerToFile() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 开始时间
        startTime := time.Now()

        // 处理请求
        c.Next()

        // 结束时间
        endTime := time.Now()

        // 计算处理时间
        latencyTime := endTime.Sub(startTime)

        // 请求方式
        reqMethod := c.Request.Method

        // 请求路由
        reqUri := c.Request.RequestURI

        // 状态码
        statusCode := c.Writer.Status()

        // 请求IP
        clientIP := c.ClientIP()

        // 日志格式
        logMsg := fmt.Sprintf("%s - %s | %3d | %13v | %15s | %-7s %s",
            startTime.Format("2006-01-02 15:04:05"),
            endTime.Format("2006-01-02 15:04:05"),
            statusCode,
            latencyTime,
            clientIP,
            reqMethod,
            reqUri,
        )

        // 发送日志到通道
        logChannel <- logMsg
    }
}

func main() {
    // 启动异步日志写入goroutine
    go asyncLogWriter()

    r := gin.Default()

    // 使用日志中间件
    r.Use(LoggerToFile())

    // 一个简单的路由
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    // 启动Gin服务
    r.Run(":8080")
}