GoFrame提供了非常强大和灵活的日志组件,可以方便地实现日志的记录、分割与查询。在glog模块中,主要有以下几个常用的方法:
- glog.Print*:打印日志到终端,支持Printf/PrintStack/Print三个方法
- glog.Debug*:打印Debug级别日志信息
- glog.Info*:打印Info级别日志信息
- glog.Warn*:打印Warn级别日志信息
- glog.Error*:打印Error级别日志信息
- 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.yaml 或 config.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都能提供很好的支持。
掌握了这些技能,开发者就可以轻松地在项目中实施结构化的日志输出,满足调试、跟踪、监控、审计等各种日志需求,提升系统的可维护性。同时合理地控制日志输出,也有助于优化程序性能,减少磁盘空间占用。