全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-18 8 分钟 ✍️ juanwangdev

Gin日志记录与性能监控中间件

日志和监控是运维的基础,完善的中间件实现可快速定位问题和优化性能。

Gin内置Logger中间件

默认Logger实现

Go
func Logger() HandlerFunc {
    return LoggerWithConfig(LoggerConfig{})
}

type LoggerConfig struct {
    SkipPaths    []string
    Output       io.Writer
    Formatter    LogFormatter
}

func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
    formatter := conf.Formatter
    if formatter == nil {
        formatter = defaultFormatter
    }

    return func(c *Context) {
        path := c.Request.URL.Path
        start := time.Now()

        c.Next()

        // 跳过指定路径
        if inSliceString(path, conf.SkipPaths) {
            return
        }

        param := LogFormatterParams{
            Request: c.Request,
            TimeStamp: time.Now(),
            StatusCode: c.Writer.Status(),
            Latency: time.Since(start),
            ClientIP: c.ClientIP(),
            Method: c.Request.Method,
            Path: path,
        }

        formatter(param)
    }
}

自定义日志中间件

结构化日志

Go
func StructuredLogger() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        path := c.Request.URL.Path
        method := c.Request.Method

        // 请求信息
        log.Printf("[Request] method=%s path=%s ip=%s",
            method, path, c.ClientIP())

        c.Next()

        // 响应信息
        duration := time.Since(start)
        status := c.Writer.Status()

        log.Printf("[Response] method=%s path=%s status=%d duration=%v size=%d",
            method, path, status, duration, c.Writer.Size())
    }
}

JSON格式日志

Go
func JSONLogger() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()

        c.Next()

        entry := map[string]any{
            "timestamp":  time.Now().Format(time.RFC3339),
            "method":     c.Request.Method,
            "path":       c.Request.URL.Path,
            "status":     c.Writer.Status(),
            "duration":   time.Since(start).Milliseconds(),
            "client_ip":  c.ClientIP(),
            "size":       c.Writer.Size(),
            "trace_id":   c.GetString("traceID"),
        }

        jsonEntry, _ := json.Marshal(entry)
        fmt.Println(string(jsonEntry))
    }
}

性能监控中间件

响应时间监控

Go
func PerformanceMonitor() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        path := c.Request.URL.Path

        c.Next()

        duration := time.Since(start)

        // 超时告警
        if duration > 1*time.Second {
            log.Printf("[WARN] Slow request: path=%s duration=%v", path, duration)
        }

        // 记录指标
        metrics.RecordLatency(path, duration)
    }
}

Prometheus指标集成

Go
var (
    httpRequestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total number of HTTP requests",
        },
        []string{"method", "path", "status"},
    )

    httpRequestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "HTTP request duration in seconds",
            Buckets: []float64{.01, .05, .1, .5, 1, 5},
        },
        []string{"method", "path"},
    )
)

func PrometheusMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        path := c.Request.URL.Path
        method := c.Request.Method

        c.Next()

        status := c.Writer.Status()
        duration := time.Since(start).Seconds()

        httpRequestsTotal.WithLabelValues(method, path, fmt.Sprintf("%d", status)).Inc()
        httpRequestDuration.WithLabelValues(method, path).Observe(duration)
    }
}

请求追踪中间件

TraceID生成与传递

Go
func TraceMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 从请求头获取或生成TraceID
        traceID := c.GetHeader("X-Trace-ID")
        if traceID == "" {
            traceID = uuid.New().String()
        }

        // 存储到Context
        c.Set("traceID", traceID)

        // 设置响应头
        c.Header("X-Trace-ID", traceID)

        c.Next()
    }
}

增强日志记录

Go
func TracedLogger() gin.HandlerFunc {
    return func(c *gin.Context) {
        traceID := c.GetString("traceID")
        start := time.Now()

        log.Printf("[%s] >>> %s %s", traceID, c.Request.Method, c.Request.URL.Path)

        c.Next()

        log.Printf("[%s] <<< %d %v", traceID, c.Writer.Status(), time.Since(start))
    }
}

日志中间件配置

跳过健康检查路径

Go
r := gin.New()
r.Use(gin.LoggerWithConfig(gin.LoggerConfig{
    SkipPaths: []string{
        "/health",
        "/metrics",
        "/favicon.ico",
    },
}))

自定义日志格式

Go
func customFormatter(param gin.LogFormatterParams) string {
    return fmt.Sprintf("[GIN] %v | %3d | %13v | %15s | %-7s %#v\n",
        param.TimeStamp.Format("2006/01/02 - 15:04:05"),
        param.StatusCode,
        param.Latency,
        param.ClientIP,
        param.Method,
        param.Path,
    )
}

r.Use(gin.LoggerWithFormatter(customFormatter))

监控指标汇总

指标类型Prometheus类型说明
请求数量Counter各路径请求总数
响应时间Histogram请求耗时分布
并发数Gauge当前并发请求
错误率Counter各状态码数量

注意:健康检查路径应跳过日志记录,避免日志量过大。

要点总结

  • Logger默认记录请求时间、状态码、耗时、路径
  • 使用LoggerConfig跳过特定路径日志
  • 结构化日志便于日志系统解析
  • Prometheus中间件记录请求指标
  • TraceID串联请求链路,便于追踪分析

📝 发现内容有误?点击此处直接编辑

← 上一篇 Gin中间件嵌套与分层设计
下一篇 → Gin错误处理中间件
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库