Go语言结构化日志实战:高性能日志系统
1. 日志重要性
结构化日志将日志信息组织为键值对格式,便于搜索、分析和监控。Go语言中zap是最流行的高性能日志库。
2. Zap日志库
package logger import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" ) type Logger struct { zap *zap.Logger } func NewLogger(level zapcore.Level, pretty bool) (*Logger, error) { var config zap.Config if pretty { config = zap.NewDevelopmentConfig() } else { config = zap.NewProductionConfig() } config.Level = zap.NewAtomicLevelAt(level) config.EncoderConfig.TimeKey = "timestamp" config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder zapLogger, err := config.Build() if err != nil { return nil, err } return &Logger{zap: zapLogger}, nil } func (l *Logger) Info(msg string, fields ...zap.Field) { l.zap.Info(msg, fields...) } func (l *Logger) Error(msg string, fields ...zap.Field) { l.zap.Error(msg, fields...) } func (l *Logger) Warn(msg string, fields ...zap.Field) { l.zap.Warn(msg, fields...) } func (l *Logger) Debug(msg string, fields ...zap.Field) { l.zap.Debug(msg, fields...) } func (l *Logger) With(fields ...zap.Field) *Logger { return &Logger{zap: l.zap.With(fields...)} }3. 字段定义
func String(key, val string) zap.Field { return zap.String(key, val) } func Int(key string, val int) zap.Field { return zap.Int(key, val) } func Int64(key string, val int64) zap.Field { return zap.Int64(key, val) } func Float64(key string, val float64) zap.Field { return zap.Float64(key, val) } func Bool(key string, val bool) zap.Field { return zap.Bool(key, val) } func Error(err error) zap.Field { return zap.Error(err) } func Duration(key string, d time.Duration) zap.Field { return zap.Duration(key, d) } func Object(key string, obj zapcore.ObjectMarshaler) zap.Field { return zap.Object(key, obj) } type User struct { ID int64 `json:"id"` Name string `json:"name"` } func (u User) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt64("id", u.ID) enc.AddString("name", u.Name) return nil } func UserField(key string, user *User) zap.Field { return zap.Object(key, user) }4. 日志中间件
func LoggingMiddleware(logger *Logger) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery c.Next() logger.Info("http request", zap.String("method", c.Request.Method), zap.String("path", path), zap.String("query", query), zap.Int("status", c.Writer.Status()), zap.Duration("latency", time.Since(start)), zap.String("client_ip", c.ClientIP()), zap.String("user_agent", c.Request.UserAgent()), ) } }5. 异步日志
func NewAsyncLogger(logger *Logger) *AsyncLogger { return &AsyncLogger{ logger: logger, buffer: make(chan zap.Field, 1000), done: make(chan struct{}), } } type AsyncLogger struct { logger *Logger buffer chan zap.Field done chan struct{} } func (a *AsyncLogger) Info(msg string, fields ...zap.Field) { select { case a.buffer <- zap.String("msg", msg): default: } go a.flush() } func (a *AsyncLogger) flush() { select { case field := <-a.buffer: a.logger.Info(field.String) case <-a.done: return case <-time.After(time.Second): return } } func (a *AsyncLogger) Shutdown() { close(a.done) }6. 总结
结构化日志是现代应用监控和排查的基础,zap库提供了高性能的结构化日志能力,通过合理的日志设计可以大大提高问题排查效率。