zerolog 封装:打造更强大、更灵活的日志系统

zerolog 封装:打造更强大、更灵活的日志系统

在现代软件开发中,一个强大而灵活的日志系统是不可或缺的。本文将详细介绍如何封装 github.com/rs/zerolog 库,以创建一个更加易用、可配置且功能丰富的日志系统。

为什么要封装 zerolog?

虽然 zerolog 是一个高性能的日志库,但直接使用它可能会面临以下挑战:

  1. 配置复杂,需要编写大量样板代码
  2. 缺乏开箱即用的全局配置
  3. 不易实现额外功能,如 HTTP 日志转发

通过封装,我们可以解决这些问题,同时保留 zerolog 的高性能特性。

封装的主要特性

我们的封装库 zlog 提供了以下主要特性:

  1. 全局单例 logger,易于在整个应用中使用
  2. 支持通过结构体进行灵活配置
  3. 支持日志轮转
  4. 内置 HTTP 日志转发功能
  5. 支持动态修改日志级别
  6. 提供了便捷的日志级别检查方法

核心代码结构

让我们深入了解 zlog 的核心代码结构:

package zlog

import (
    "io"
    "os"
    "strings"
    "sync"
    "time"

    "github.com/rs/zerolog"
    "gopkg.in/natefinch/lumberjack.v2"
)

// LogConfig 定义了日志的配置结构
type LogConfig struct {
    Level     string    `yaml:"Level"`
    Pattern   string    `yaml:"Pattern"`
    Output    string    `yaml:"Output"`
    LogRotate LogRotate `yaml:"LogRotate"`
    HTTP      HTTPConfig `yaml:"HTTP"`
}

// LogRotate 定义了日志轮转的配置
type LogRotate struct {
    Filename   string `yaml:"Filename"`
    MaxSize    int    `yaml:"MaxSize"`
    MaxBackups int    `yaml:"MaxBackups"`
    MaxAge     int    `yaml:"MaxAge"`
    Compress   bool   `yaml:"Compress"`
}

// HTTPConfig 定义了 HTTP 日志的配置
type HTTPConfig struct {
    Enabled       bool   `yaml:"Enabled"`
    URL           string `yaml:"URL"`
    WarnOnHttpErr bool   `yaml:"WarnOnHttpErr"`
}

// Logger 接口定义了日志的方法
type Logger interface {
    Trace(message string, args ...interface{})
    Debug(message string, args ...interface{})
    Info(message string, args ...interface{})
    Warn(message string, args ...interface{})
    Error(message string, args ...interface{})
    Fatal(message string, args ...interface{})
    Panic(message string, args ...interface{})
    SetConfig(config LogConfig)
    GetConfig() LogConfig
    EnableHTTPLogging(url string, warnOnHttpErr bool)
    IsDebugEnabled() bool
    // ... 其他方法
}

// loggerImpl 是 Logger 接口的具体实现
type loggerImpl struct {
    zl              zerolog.Logger
    config          LogConfig
    isTraceEnabled  bool
    isDebugEnabled  bool
    isInfoEnabled   bool
    isWarnEnabled   bool
    isErrorEnabled  bool
    isFatalEnabled  bool
    isPanicEnabled  bool
    output          io.Writer
    httpWriter      *LogHTTPWriter
}

var (
    globalLogger *loggerImpl
    once         sync.Once
)

// GetLogger 返回全局单例 logger
func GetLogger() Logger {
    once.Do(func() {
        globalLogger = &loggerImpl{
            config: DefaultConfig,
        }
        globalLogger.init()
    })
    return globalLogger
}

// init 初始化 logger
func (l *loggerImpl) init() {
    // 设置日志级别
    level, _ := zerolog.ParseLevel(strings.ToLower(l.config.Level))
    zerolog.SetGlobalLevel(level)

    // 更新启用的日志级别标志
    l.updateEnabledFlags(level)

    // 配置输出
    if l.config.Output == "stdout" {
        l.output = os.Stdout
    } else {
        l.output = &lumberjack.Logger{
            Filename:   l.config.LogRotate.Filename,
            MaxSize:    l.config.LogRotate.MaxSize,
            MaxBackups: l.config.LogRotate.MaxBackups,
            MaxAge:     l.config.LogRotate.MaxAge,
            Compress:   l.config.LogRotate.Compress,
        }
    }

    // 配置 HTTP 日志
    if l.config.HTTP.Enabled {
        l.httpWriter = NewLogHTTPWriter(l.config.HTTP.URL, l.config.HTTP.WarnOnHttpErr)
        l.output = io.MultiWriter(l.output, l.httpWriter)
    }

    // 创建 zerolog.Logger
    logContext := zerolog.New(l.output).With().Timestamp()
    if l.config.Pattern == "development" {
        l.output = zerolog.ConsoleWriter{Out: l.output, TimeFormat: time.RFC3339}
        logContext = zerolog.New(l.output).With().Timestamp().Caller()
    }
    l.zl = logContext.Logger()
}

// SetConfig 设置新的日志配置
func (l *loggerImpl) SetConfig(config LogConfig) {
    l.config = config
    l.init()
}

// EnableHTTPLogging 启用 HTTP 日志
func (l *loggerImpl) EnableHTTPLogging(url string, warnOnHttpErr bool) {
    l.config.HTTP.Enabled = true
    l.config.HTTP.URL = url
    l.config.HTTP.WarnOnHttpErr = warnOnHttpErr
    l.init()
}

// Debug 输出 Debug 级别的日志
func (l *loggerImpl) Debug(message string, args ...interface{}) {
    if l.isDebugEnabled {
        l.log(zerolog.DebugLevel, message, args...)
    }
}

// log 是内部日志方法
func (l *loggerImpl) log(level zerolog.Level, message string, args ...interface{}) {
    if len(args) > 0 {
        l.zl.WithLevel(level).Msgf(message, args...)
    } else {
        l.zl.WithLevel(level).Msg(message)
    }
}

// IsDebugEnabled 检查 Debug 级别是否启用
func (l *loggerImpl) IsDebugEnabled() bool {
    return l.isDebugEnabled
}

// LogHTTPWriter 实现了将日志写入 HTTP 服务器的功能
type LogHTTPWriter struct {
    serverURL     string
    warnOnHttpErr bool
    errorLogger   zerolog.Logger
}

func NewLogHTTPWriter(serverURL string, warnOnHttpErr bool) *LogHTTPWriter {
    return &LogHTTPWriter{
        serverURL:     serverURL,
        warnOnHttpErr: warnOnHttpErr,
        errorLogger:   zerolog.New(os.Stderr).With().Caller().Timestamp().Logger(),
    }
}

func (w *LogHTTPWriter) Write(p []byte) (n int, err error) {
    // 实现 HTTP 写入逻辑
    // ...
    return len(p), nil
}

如何使用

使用 zlog 非常简单:

package main

import (
    "your_package_path/zlog"
)

func main() {
    logger := zlog.GetLogger()

    // 使用默认配置
    logger.Info("这是一条信息日志")

    // 启用 HTTP 日志
    logger.EnableHTTPLogging("http://log-server.com/logs", true)

    // 修改配置
    newConfig := zlog.LogConfig{
        Level:   "debug",
        Pattern: "development",
        Output:  "file",
        LogRotate: zlog.LogRotate{
            Filename: "app.log",
            MaxSize:  50, // MB
        },
    }
    logger.SetConfig(newConfig)

    // 使用新配置
    logger.Debug("这是一条调试日志")

    // 使用日志级别检查
    if logger.IsDebugEnabled() {
        logger.Debug("这条日志只在 Debug 级别启用时输出")
    }
}

高级特性:HTTP 日志转发

我们的封装库支持将日志实时转发到 HTTP 服务器,这对于分布式系统的日志收集非常有用。LogHTTPWriter 结构体实现了这一功能:

type LogHTTPWriter struct {
    serverURL     string
    warnOnHttpErr bool
    errorLogger   zerolog.Logger
}

func (w *LogHTTPWriter) Write(p []byte) (n int, err error) {
    // 将日志发送到 HTTP 服务器
    // 实现省略...
    return len(p), nil
}

性能考虑

尽管我们添加了额外的功能,但我们仍然保持了 zerolog 的高性能特性:

  1. 使用 sync.Once 确保全局 logger 只被初始化一次
  2. 提供了日志级别检查方法,避免不必要的字符串格式化
  3. 使用 io.MultiWriter 实现多目标日志输出,无需额外的 goroutine

通过封装 zerolog,我们创建了一个既易用又灵活的日志系统。它保留了 zerolog 的高性能特性,同时提供了更多的功能和更好的可配置性。无论是在小型项目还是大型分布式系统中,这个日志系统都能满足各种需求。高性能,灵活强大的zlog日志处理库在我们的SagooIoT V3 版企业级物联网平台系统中全面使用。

希望这个封装库能够帮助你更轻松地处理日志,让你专注于业务逻辑的开发。如果你有任何问题或建议,欢迎在评论区留言!

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值