GoFrame日志模块:从基础到进阶的实用指南

348 阅读7分钟

GoFrame提供了非常强大和灵活的日志组件,可以方便地实现日志的记录、分割与查询。在glog模块中,主要有以下几个常用的方法:

  1. glog.Print*:打印日志到终端,支持Printf/PrintStack/Print三个方法
  2. glog.Debug*:打印Debug级别日志信息
  3. glog.Info*:打印Info级别日志信息
  4. glog.Warn*:打印Warn级别日志信息
  5. glog.Error*:打印Error级别日志信息
  6. glog.Fatal*:打印Critical级别日志信息,并退出程序

通过这些方法,我们可以非常方便地在代码中插入日志语句。glog支持对日志进行分级,方便我们针对性地输出调试信息。

日志分割

另外,glog还支持日志文件的自动分割。通过SetPath方法指定日志的存储目录,通过SetFile方法设置日志文件名格式,glog就可以自动进行日志文件按天/小时分割:

import "github.com/gogf/gf/v2/os/glog"

glog.SetPath("/home/logs")
glog.SetFile("access-{Ymd}.log")

以上设置日志存储在/home/logs目录,日志文件名为access-{Ymd}.log,其中{Ymd}会被自动替换为当前的日期。这样日志就会按天进行分割了。

日志配置

如果项目较大,日志量非常大,还可以使用glog的链式操作方法,将日志写入到多个writer中,实现日志的分流。例如:

import "github.com/gogf/gf/v2/os/glog"

glog.SetPath("/home/logs")
l := glog.New()
l.SetFlags(glog.F_TIME_STD)
l.SetPath("/home/john/logs")
l.SetStdoutPrint(false) // 关闭标准输出
l.SetFile("access-{Ymd}.log")
l.Stdout(true)

以上通过glog.New创建一个日志对象,将日志写到access-xxx.log这个文件中,方便后续查看。

日志级别

glog支持以下几种日志级别:

LEVEL_ALL  
LEVEL_DEV  
LEVEL_PROD  
LEVEL_DEBUG 
LEVEL_INFO  
LEVEL_NOTI
LEVEL_WARN  
LEVEL_ERRO
LEVEL_CRIT
LEVEL_PANI
LEVEL_FATA

其中LEVEL_ALL最低,会打印所有级别的日志;LEVEL_FATA最高,不打印任何日志。如果想只记录错误和警告信息,可以将日志级别设置为LEVEL_WARN,示例代码如下:

import "github.com/gogf/gf/v2/os/glog"

func main() {
    ctx := gctx.New()
    l := glog.New()
    l.SetLevel(glog.LEVEL_WARN)
    l.SetStdoutPrint(false) // 关闭标准输出
    l.SetFile("log-{Ymd}.log")

    l.Debug(ctx, "This is a debug log message")
    l.Info(ctx, "This is an informational log message")
    l.Warning(ctx, "This is a warning log message")
    l.Error(ctx, "This is an error log message")
    l.Fatal(ctx, "This is a fatal log message")
}

执行后,由于设置了LEVEL_WARN,低于该级别的Debug和Info日志都不会打印,只有Warn、Error和Fatal级别的日志会输出到日志文件中。

除了用SetLevel方法外,还可以通过日志配置文件来配置日志级别。在启动时添加-gf.glog.level参数,或者在配置文件中添加:

gflog:
  logLevel: warn

这样对代码的侵入性更小一些。

日志格式

使用 SetFlags 自定义日志前缀

SetFlags 方法允许你配置日志的输出格式,例如是否打印时间、文件名、行号等。常见的 Flags 包括:

  • glog.F_TIME_STD :标准时间格式(2006-01-02 15:04:05)。
  • glog.F_TIME_TIME:短时间格式(15:04:05)。
  • glog.F_FILE_LONG:日志中附加完整的文件路径。
  • glog.F_FILE_SHORT:日志中附加文件名。

代码示例:

import "github.com/gogf/gf/v2/os/glog"

func main() {
    ctx := gctx.New()
    l := glog.New()
    l.SetPath("./logs")                             // 设置日志存储路径
    l.SetFile("custom-{Ymd}.log")                   // 设置日志文件名称
    l.SetFlags(glog.F_TIME_STD | glog.F_FILE_SHORT) // 自定义日志格式
    l.Info(ctx, "This is an info log")
}

输出日志格式示例:

2024-11-24 14:30:00 [INFO] main.go:12: This is an info log

添加自定义字段到日志内容

通过日志的 Print 系列方法,可以直接在日志内容中插入自定义字段。也可以使用字符串模板构建统一的日志格式:

import "github.com/gogf/gf/v2/os/glog"

func main() {
    ctx := gctx.New()
    l := glog.New()
    l.SetPath("./logs")
    l.SetFile("custom-{Ymd}.log")

    // 自定义字段
    userID := 12345
    module := "auth"

    // 打印日志时插入自定义字段
    l.Infof(ctx,"UserID: %d | Module: %s | Message: %s", userID, module, "Login successful")
}

输出日志格式示例:

2024-11-24 14:32:00 [INFO] UserID: 12345 | Module: auth | Message: Login successful

使用自定义 Writer

通过实现自定义的 Writer,可以完全控制日志的输出内容和格式。以下是一个示例,通过自定义 io.Writer 添加额外字段到日志中:

import ( 
    "fmt"
    "github.com/gogf/gf/v2/os/glog"
    "io" 
)

// 自定义 Writer
type CustomWriter struct{}

func (w *CustomWriter) Write(p []byte) (n int, err error) {
    // 在日志前添加自定义字段
    customLog := fmt.Sprintf("[CustomField: %s] %s", "CustomValue", string(p))
    fmt.Print(customLog) // 输出到控制台
    return len(customLog), nil
}

func main() {
    ctx := gctx.New()
    l := glog.New()
    l.SetWriter(&CustomWriter{}) // 设置自定义 Writer
    l.Info(ctx, "This is a custom log message")
}

输出日志格式示例:

[CustomField: CustomValue] 2024-11-24 14:35:00 [INFO] This is a custom log message

使用 SetConfig 方法统一配置日志格式

如果需要在配置文件中统一配置日志的格式,可以使用 SetConfig 方法加载配置文件(如 config.yamlconfig.json)。例如:

配置文件:config.toml

gflog:
  path: "./logs"
  file: "app-{Ymd}.log"
  stdoutPrint: false
  flags: "F_TIME_STD | F_LEVEL | F_FILE_SHORT"

代码:

import "github.com/gogf/gf/v2/frame/g"

func main() {
    ctx := gctx.New()
    g.Log().SetConfigWithMap(g.Cfg().MustGet(ctx, "gflog").Map())
    g.Log().Info(ctx, "This is a log message with custom format")
}

使用自定义 Logger 实现复杂格式

如果需要更加灵活的日志格式,可以通过创建一个新的 Logger,并自定义其输出逻辑。例如:

import ( 
    "fmt" 
    "github.com/gogf/gf/v2/os/glog"
)

func main() {
    ctx := gctx.New()
    l := glog.New()
    l.SetPath("./logs")
    l.SetFile("custom-{Ymd}.log")

    // 自定义日志格式函数
    logFormat := func(level, msg string) string {
       return fmt.Sprintf("[CustomField: %s] [%s] %s", "CustomValue", level, msg)
    }

    l.Print(ctx, logFormat("INFO", "This is a custom log message"))
}

日志分流

使用多个日志实例写入不同文件

借助 glog.New() 创建多个日志对象,每个对象可以设置不同的日志文件路径和名称。这样可以实现日志分流,例如普通日志和错误日志分开记录。

代码示例

import "github.com/gogf/gf/v2/os/glog"

func main() {
    ctx := gctx.New()

    // 普通日志写入 access.log
    accessLogger := glog.New()
    accessLogger.SetPath("./logs")
    accessLogger.SetFile("access-{Ymd}.log")

    // 错误日志写入 error.log
    errorLogger := glog.New()
    errorLogger.SetPath("./logs")
    errorLogger.SetFile("error-{Ymd}.log")

    // 写入日志
    accessLogger.Info(ctx,"This is an access log")
    errorLogger.Error(ctx,"This is an error log")
}

日志文件输出

  • ./logs/access-20241124.log 中记录普通日志:
2024-11-24 14:35:00 [INFO] This is an access log
  • ./logs/error-20241124.log 中记录错误日志:
2024-11-24 14:35:00 [ERROR] This is an error log

使用条件逻辑动态切换日志文件

如果日志的写入逻辑需要根据具体内容动态切换文件,可以使用 SetFile 方法动态设置日志文件。 代码示例

import ( 
    "fmt" 
    "github.com/gogf/gf/v2/os/glog" 
)

func main() {
    ctx := gctx.New()
    logger := glog.New()
    logger.SetPath("./logs")

    // 动态选择日志文件
    writeLog := func(logType, message string) {
       if logType == "access" {
          logger.SetFile("access-{Ymd}.log") // 设置为访问日志文件
       } else if logType == "error" {
          logger.SetFile("error-{Ymd}.log") // 设置为错误日志文件
       }
       logger.Print(ctx, message) // 写入日志
    }

    // 写入不同类型的日志
    writeLog("access", "This is an access log")
    writeLog("error", "This is an error log")
}

日志文件输出

-./logs/access-20241124.log 中记录:

2024-11-24 14:35:00 This is an access log

-./logs/error-20241124.log 中记录:

2024-11-24 14:35:00 This is an error log

自定义 Writer 实现日志分流

通过实现自定义的 io.Writer,可以根据日志内容或级别,将日志写入到不同的文件中。 代码示例

import ( 
    "fmt" 
    "os" 
    "github.com/gogf/gf/os/glog" 
)

// 自定义 Writer
type MultiFileWriter struct {
    accessFile *os.File
    errorFile  *os.File
}

// 初始化自定义 Writer
func NewMultiFileWriter() (*MultiFileWriter, error) {
    accessFile, err := os.OpenFile("./logs/access.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
       return nil, err
    }
    errorFile, err := os.OpenFile("./logs/error.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
       return nil, err
    }
    return &MultiFileWriter{
       accessFile: accessFile,
       errorFile:  errorFile,
    }, nil
}

// 实现 Write 方法
func (w *MultiFileWriter) Write(p []byte) (n int, err error) {
    log := string(p)
    if len(log) > 0 && log[0:5] == "[INFO" { // 判断日志级别
       return w.accessFile.Write(p)
    } else if len(log) > 0 && log[0:6] == "[ERROR" {
       return w.errorFile.Write(p)
    }
    return len(p), nil
}

func main() {
    ctx := gctx.New()

    // 创建自定义 Writer
    writer, err := NewMultiFileWriter()
    if err != nil {
       panic(err)
    }
    defer writer.accessFile.Close()
    defer writer.errorFile.Close()

    // 设置自定义 Writer
    logger := glog.New()
    logger.SetWriter(writer)

    // 写入日志
    logger.Info(ctx, "This is an access log")
    logger.Error(ctx, "This is an error log")
}

日志文件输出

-./logs/access.log 中记录:

2024-11-24 14:35:00 [INFO] This is an access log

-./logs/error.log 中记录:

2024-11-24 14:35:00 [ERROR] This is an error log

总结

通过对GoFrame日志组件的深入探讨,开发者可以了解到如何灵活运用它的各项特性,打造一套适合自己项目的日志方案。无论是基础的日志打印和文件存储,还是个性化的日志格式定制和复杂的分流策略,GoFrame都能提供很好的支持。

掌握了这些技能,开发者就可以轻松地在项目中实施结构化的日志输出,满足调试、跟踪、监控、审计等各种日志需求,提升系统的可维护性。同时合理地控制日志输出,也有助于优化程序性能,减少磁盘空间占用。