乐闻世界logo
搜索文章和话题

Gin 框架中的日志记录和监控如何实现?

2月21日 15:15

Gin 框架的日志记录和监控实现方法如下:

1. 日志记录概述

Gin 框架内置了基本的日志功能,也支持集成第三方日志库如 logrus、zap、zerolog 等。

2. 使用内置日志

2.1 基本日志

go
import "github.com/gin-gonic/gin" func main() { // 设置 Gin 模式 gin.SetMode(gin.DebugMode) // 或 gin.ReleaseMode r := gin.Default() // 默认包含 Logger 和 Recovery 中间件 r.GET("/", func(c *gin.Context) { c.String(200, "Hello World") }) r.Run(":8080") }

2.2 自定义日志格式

go
func main() { r := gin.New() // 自定义日志中间件 r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { return fmt.Sprintf("[%s] %s %s %d %s\n", param.TimeStamp.Format("2006-01-02 15:04:05"), param.Method, param.Path, param.StatusCode, param.Latency, ) })) r.Use(gin.Recovery()) r.GET("/", func(c *gin.Context) { c.String(200, "Hello World") }) r.Run(":8080") }

3. 集成 logrus

3.1 配置 logrus

go
import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) var log = logrus.New() func init() { log.SetFormatter(&logrus.JSONFormatter{}) log.SetOutput(os.Stdout) log.SetLevel(logrus.InfoLevel) } func logrusMiddleware() gin.HandlerFunc { return func(c *gin.Context) { startTime := time.Now() c.Next() latency := time.Since(startTime) status := c.Writer.Status() entry := log.WithFields(logrus.Fields{ "method": c.Request.Method, "path": c.Request.URL.Path, "status": status, "latency": latency, "ip": c.ClientIP(), "user-agent": c.Request.UserAgent(), }) if status >= 500 { entry.Error("Server error") } else if status >= 400 { entry.Warn("Client error") } else { entry.Info("Request completed") } } }

3.2 使用 logrus

go
func main() { r := gin.New() r.Use(logrusMiddleware()) r.Use(gin.Recovery()) r.GET("/", func(c *gin.Context) { log.Info("Processing request") c.String(200, "Hello World") }) r.Run(":8080") }

4. 集成 zap

4.1 配置 zap

go
import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var logger *zap.Logger func initLogger() { config := zap.NewProductionConfig() config.EncoderConfig.TimeKey = "timestamp" config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder var err error logger, err = config.Build() if err != nil { panic(err) } } func zapMiddleware() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery c.Next() latency := time.Since(start) status := c.Writer.Status() logger.Info("Request", zap.String("method", c.Request.Method), zap.String("path", path), zap.String("query", query), zap.Int("status", status), zap.Duration("latency", latency), zap.String("ip", c.ClientIP()), ) } }

5. 请求追踪

5.1 添加请求 ID

go
import "github.com/google/uuid" func requestIDMiddleware() gin.HandlerFunc { return func(c *gin.Context) { requestID := c.GetHeader("X-Request-ID") if requestID == "" { requestID = uuid.New().String() } c.Set("request_id", requestID) c.Header("X-Request-ID", requestID) c.Next() } }

5.2 分布式追踪

go
import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" ) func tracingMiddleware() gin.HandlerFunc { return func(c *gin.Context) { tracer := otel.Tracer("gin-server") ctx, span := tracer.Start(c.Request.Context(), c.Request.URL.Path) defer span.End() c.Request = c.Request.WithContext(ctx) c.Next() } }

6. 性能监控

6.1 Prometheus 指标

go
import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ) var ( httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ Name: "http_request_duration_seconds", Help: "HTTP request duration in seconds", }, []string{"method", "path", "status"}) httpRequestsTotal = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "http_requests_total", Help: "Total number of HTTP requests", }, []string{"method", "path", "status"}) ) func prometheusMiddleware() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() c.Next() duration := time.Since(start).Seconds() status := strconv.Itoa(c.Writer.Status()) httpDuration.WithLabelValues(c.Request.Method, c.FullPath(), status).Observe(duration) httpRequestsTotal.WithLabelValues(c.Request.Method, c.FullPath(), status).Inc() } }

6.2 暴露指标端点

go
import "github.com/prometheus/client_golang/prometheus/promhttp" func setupMetrics(r *gin.Engine) { r.GET("/metrics", gin.WrapH(promhttp.Handler())) }

7. 错误监控

7.1 Sentry 集成

go
import "github.com/getsentry/sentry-go" func initSentry() { err := sentry.Init(sentry.ClientOptions{ Dsn: "your-sentry-dsn", }) if err != nil { panic(err) } } func sentryMiddleware() gin.HandlerFunc { return func(c *gin.Context) { hub := sentry.CurrentHub().Clone() hub.Scope().SetRequest(c.Request) c.Set("sentry", hub) defer func() { if err := recover(); err != nil { hub.CaptureException(err.(error)) c.JSON(500, gin.H{"error": "Internal server error"}) c.Abort() } }() c.Next() } }

8. 结构化日志

8.1 使用结构化字段

go
func logStructured(c *gin.Context) { log.WithFields(logrus.Fields{ "request_id": c.GetString("request_id"), "user_id": c.GetInt("user_id"), "action": "user_login", "ip": c.ClientIP(), }).Info("User logged in") }

8.2 日志级别控制

go
func logWithLevel(c *gin.Context, err error) { if err == nil { log.Info("Operation successful") return } switch { case errors.Is(err, ErrNotFound): log.WithField("error", err).Warn("Resource not found") case errors.Is(err, ErrUnauthorized): log.WithField("error", err).Warn("Unauthorized access") default: log.WithField("error", err).Error("Internal error") } }

9. 日志轮转

9.1 使用 lumberjack

go
import "gopkg.in/natefinch/lumberjack.v2" func setupRotatingLogger() { log.SetOutput(&lumberjack.Logger{ Filename: "app.log", MaxSize: 100, // MB MaxBackups: 3, MaxAge: 28, // days Compress: true, }) }

10. 最佳实践

  1. 日志内容

    • 记录请求 ID 便于追踪
    • 包含时间戳、方法、路径、状态码
    • 记录关键业务操作
    • 避免记录敏感信息
  2. 日志级别

    • Debug: 开发调试信息
    • Info: 正常业务流程
    • Warn: 潜在问题
    • Error: 错误但可恢复
    • Fatal: 致命错误
  3. 性能考虑

    • 使用异步日志
    • 避免在热路径中记录详细日志
    • 使用结构化日志便于解析
    • 合理设置日志级别
  4. 监控指标

    • 请求延迟分布
    • 请求成功率
    • 错误率
    • 并发连接数
    • 内存和 CPU 使用
  5. 告警配置

    • 设置错误率阈值
    • 配置延迟告警
    • 监控资源使用
    • 设置日志异常检测

通过以上方法,可以在 Gin 框架中实现完善的日志记录和监控体系。

标签:Gin