|
@@ -10,37 +10,39 @@ import (
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+// Logger 是全局的 logrus.Logger 实例,供其他包使用
|
|
var Logger *logrus.Logger
|
|
var Logger *logrus.Logger
|
|
|
|
|
|
// Hook 结构体定义了一个日志钩子,用于将日志条目输出到指定的 Writer,并使用指定的 Formatter 格式化日志条目。
|
|
// Hook 结构体定义了一个日志钩子,用于将日志条目输出到指定的 Writer,并使用指定的 Formatter 格式化日志条目。
|
|
// Level 字段是一个包含日志级别的切片,用于指定该钩子应处理的日志级别。
|
|
// Level 字段是一个包含日志级别的切片,用于指定该钩子应处理的日志级别。
|
|
type Hook struct {
|
|
type Hook struct {
|
|
- Writer io.Writer
|
|
|
|
- Formatter logrus.Formatter
|
|
|
|
- Level []logrus.Level
|
|
|
|
|
|
+ Writer io.Writer // 日志输出的目标 Writer
|
|
|
|
+ Formatter logrus.Formatter // 日志的格式化器
|
|
|
|
+ Level []logrus.Level // 此钩子应处理的日志级别
|
|
}
|
|
}
|
|
|
|
|
|
// Fire 方法是 logrus.Hook 接口的实现。当日志条目符合钩子的日志级别时,
|
|
// Fire 方法是 logrus.Hook 接口的实现。当日志条目符合钩子的日志级别时,
|
|
// 这个方法会被调用。它首先使用 Formatter 将日志条目格式化为字节切片,
|
|
// 这个方法会被调用。它首先使用 Formatter 将日志条目格式化为字节切片,
|
|
// 然后将其写入 Writer。此版本增强了日志条目,添加了错误发生的文件名和行号。
|
|
// 然后将其写入 Writer。此版本增强了日志条目,添加了错误发生的文件名和行号。
|
|
func (h *Hook) Fire(entry *logrus.Entry) error {
|
|
func (h *Hook) Fire(entry *logrus.Entry) error {
|
|
- // 获取错误发生的文件名和行号
|
|
|
|
|
|
+ // 如果日志条目有调用者信息,则记录文件名和行号
|
|
if entry.HasCaller() {
|
|
if entry.HasCaller() {
|
|
entry.Data["file"] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
|
entry.Data["file"] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // 使用 Formatter 格式化日志条目为字节切片
|
|
line, err := h.Formatter.Format(entry)
|
|
line, err := h.Formatter.Format(entry)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return err
|
|
|
|
|
|
+ return err // 如果格式化出错,返回错误
|
|
}
|
|
}
|
|
- h.Writer.Write(line)
|
|
|
|
|
|
+ h.Writer.Write(line) // 将格式化的日志条目写入 Writer
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
// Levels 方法返回该钩子支持的日志级别切片。
|
|
// Levels 方法返回该钩子支持的日志级别切片。
|
|
// logrus 在写入日志时会调用这个方法来决定是否使用这个钩子。
|
|
// logrus 在写入日志时会调用这个方法来决定是否使用这个钩子。
|
|
func (h *Hook) Levels() []logrus.Level {
|
|
func (h *Hook) Levels() []logrus.Level {
|
|
- return h.Level
|
|
|
|
|
|
+ return h.Level // 返回支持的日志级别
|
|
}
|
|
}
|
|
|
|
|
|
// newHook 函数用于创建一个新的 Hook 实例。它接收一个 Writer,
|
|
// newHook 函数用于创建一个新的 Hook 实例。它接收一个 Writer,
|
|
@@ -49,11 +51,13 @@ func (h *Hook) Levels() []logrus.Level {
|
|
// 并返回该 Hook 实例。
|
|
// 并返回该 Hook 实例。
|
|
func newHook(writer io.Writer, formatter logrus.Formatter, level logrus.Level) *Hook {
|
|
func newHook(writer io.Writer, formatter logrus.Formatter, level logrus.Level) *Hook {
|
|
var levels []logrus.Level
|
|
var levels []logrus.Level
|
|
|
|
+ // 遍历 logrus 所有的日志级别,如果当前级别低于或等于指定的 level,添加到 levels 切片中
|
|
for _, l := range logrus.AllLevels {
|
|
for _, l := range logrus.AllLevels {
|
|
if l <= level {
|
|
if l <= level {
|
|
levels = append(levels, l)
|
|
levels = append(levels, l)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ // 返回新建的 Hook 实例
|
|
return &Hook{
|
|
return &Hook{
|
|
Writer: writer,
|
|
Writer: writer,
|
|
Formatter: formatter,
|
|
Formatter: formatter,
|
|
@@ -61,63 +65,111 @@ func newHook(writer io.Writer, formatter logrus.Formatter, level logrus.Level) *
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-// InitLogs 函数负责初始化 logrus.Logger 对象。它首先检查是否存在指定的日志目录(默认为 ./logs/)。
|
|
|
|
-// 如果目录不存在,它会创建该目录。
|
|
|
|
|
|
+// createLogFile 函数用于根据当前日期创建一个新的日志文件。
|
|
|
|
+// 它返回日志文件的文件叠指针、文件名和可能的错误信息。
|
|
|
|
+func createLogFile(logFilePath string) (*os.File, string, error) {
|
|
|
|
+ // 使用当前日期生成日志文件名,格式为 `log-YYYY-MM-DD-HH-MM-SS.log`
|
|
|
|
+ currentDate := time.Now().Format("2006-01-02-15-04-05")
|
|
|
|
+ logFileName := fmt.Sprintf("%s/log-%s.log", logFilePath, currentDate)
|
|
|
|
+
|
|
|
|
+ // 打开或创建日志文件,文件路径由 logFilePath 指定
|
|
|
|
+ logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
|
|
|
+ return logFile, logFileName, err // 返回日志文件叠指针、文件名和可能的错误
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// InitLogs 函数负责初始化 logrus.Logger 对象。它首先检查是否存在指定的日志目录。
|
|
|
|
+// 如果目录不存在,会创建该目录。
|
|
func InitLogs(logFilePath string, logLevelStr string) *logrus.Logger {
|
|
func InitLogs(logFilePath string, logLevelStr string) *logrus.Logger {
|
|
- //dir := "./logs/"
|
|
|
|
- // 检查目录是否存在
|
|
|
|
|
|
+ // 检查日志目录是否存在
|
|
if _, err := os.Stat(logFilePath); os.IsNotExist(err) {
|
|
if _, err := os.Stat(logFilePath); os.IsNotExist(err) {
|
|
// 目录不存在,创建目录
|
|
// 目录不存在,创建目录
|
|
err := os.MkdirAll(logFilePath, os.ModePerm)
|
|
err := os.MkdirAll(logFilePath, os.ModePerm)
|
|
if err != nil {
|
|
if err != nil {
|
|
- fmt.Println("Error creating directory:", err)
|
|
|
|
|
|
+ fmt.Println("Error creating directory:", err) // 打印创建目录失败的错误信息
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
- fmt.Println("Directory created:", logFilePath)
|
|
|
|
|
|
+ fmt.Println("Directory created:", logFilePath) // 打印成功创建目录的信息
|
|
}
|
|
}
|
|
|
|
|
|
- // 尝试将 logLevelStr 转换为 logrus.Level。如果转换失败,则默认使用 InfoLevel 级别。
|
|
|
|
|
|
+ // 将 logLevelStr 转换为 logrus.Level,失败时默认使用 InfoLevel
|
|
logLevel, err := logrus.ParseLevel(logLevelStr)
|
|
logLevel, err := logrus.ParseLevel(logLevelStr)
|
|
if err != nil {
|
|
if err != nil {
|
|
logLevel = logrus.InfoLevel
|
|
logLevel = logrus.InfoLevel
|
|
- log.Printf("Invalid log level: %s. Defaulting to info", logLevelStr)
|
|
|
|
|
|
+ log.Printf("Invalid log level: %s. Defaulting to info", logLevelStr) // 输出无效日志级别信息
|
|
}
|
|
}
|
|
|
|
|
|
- // 初始化 Logger 实例,并将其输出设置为 io.Discard,即默认情况下不输出任何日志。
|
|
|
|
- // 这是为了强制所有日志输出都通过钩子来处理。
|
|
|
|
|
|
+ // 初始化 Logger 实例,将输出设置为 io.Discard,以强制通过钩子处理所有日志输出
|
|
Logger = logrus.New()
|
|
Logger = logrus.New()
|
|
- Logger.SetOutput(io.Discard)
|
|
|
|
|
|
+ Logger.SetOutput(io.Discard) // 默认不输出任何日志
|
|
|
|
|
|
- // 启用记录调用者信息,这样可以记录错误发生的位置(文件名和行号)。
|
|
|
|
|
|
+ // 启用记录调用者信息(文件名和行号)
|
|
Logger.SetReportCaller(true)
|
|
Logger.SetReportCaller(true)
|
|
|
|
|
|
- // 使用当前日期生成日志文件名,格式为 `log-YYYY-MM-DD.log`
|
|
|
|
- currentDate := time.Now().Format("2006-01-02")
|
|
|
|
- logFileName := fmt.Sprintf("%s/log-%s.log", logFilePath, currentDate)
|
|
|
|
-
|
|
|
|
- // 打开或创建日志文件。日志文件的路径由 logFilePath 参数指定。
|
|
|
|
- logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
|
|
|
|
|
+ // 使用 createLogFile 创建一个新的日志文件
|
|
|
|
+ logFile, logFileName, err := createLogFile(logFilePath)
|
|
if err != nil {
|
|
if err != nil {
|
|
- log.Printf("Failed to open log file: %s", logFileName)
|
|
|
|
- panic(err)
|
|
|
|
|
|
+ log.Printf("Failed to open log file: %s", logFileName) // 打印文件打开失败的信息
|
|
|
|
+ panic(err) // 触发 panic
|
|
}
|
|
}
|
|
|
|
|
|
- // 添加一个新的 Hook,将日志输出到日志文件中,并使用 JSONFormatter 进行格式化。
|
|
|
|
|
|
+ // 添加一个新的 Hook,将日志输出到日志文件中,并使用 JSONFormatter 进行格式化
|
|
Logger.AddHook(newHook(
|
|
Logger.AddHook(newHook(
|
|
logFile,
|
|
logFile,
|
|
&logrus.JSONFormatter{},
|
|
&logrus.JSONFormatter{},
|
|
logLevel,
|
|
logLevel,
|
|
))
|
|
))
|
|
|
|
|
|
- // 添加另一个 Hook,将日志输出到标准错误输出(通常是控制台),
|
|
|
|
- // 并使用 TextFormatter 格式化日志,显示完整时间戳并强制使用颜色。
|
|
|
|
|
|
+ // 添加一个 Hook,将日志输出到标准错误输出(控制台),使用 TextFormatter 格式化
|
|
Logger.AddHook(newHook(
|
|
Logger.AddHook(newHook(
|
|
os.Stderr,
|
|
os.Stderr,
|
|
&logrus.TextFormatter{
|
|
&logrus.TextFormatter{
|
|
- FullTimestamp: true,
|
|
|
|
- ForceColors: true,
|
|
|
|
|
|
+ FullTimestamp: true, // 显示完整时间戳
|
|
|
|
+ ForceColors: true, // 强制使用颜色输出
|
|
},
|
|
},
|
|
logLevel,
|
|
logLevel,
|
|
))
|
|
))
|
|
- return Logger
|
|
|
|
|
|
+
|
|
|
|
+ // 启动一个 goroutine,用于每天创建新的日志文件
|
|
|
|
+ go func() {
|
|
|
|
+ // 每 24 小时触发一次
|
|
|
|
+ ticker := time.NewTicker(24 * time.Hour)
|
|
|
|
+ defer ticker.Stop() // 停止 ticker
|
|
|
|
+
|
|
|
|
+ for {
|
|
|
|
+ <-ticker.C // 等待 ticker 触发
|
|
|
|
+
|
|
|
|
+ // 关闭当前日志文件
|
|
|
|
+ logFile.Close()
|
|
|
|
+
|
|
|
|
+ // 创建新的日志文件
|
|
|
|
+ newLogFile, newLogFileName, err := createLogFile(logFilePath)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("Failed to create new log file: %s", newLogFileName) // 打印创建失败信息
|
|
|
|
+ continue // 如果失败,跳过此次循环,等待下一次检查
|
|
|
|
+ }
|
|
|
|
+ logFile = newLogFile // 更新 logFile
|
|
|
|
+
|
|
|
|
+ // 清除所有旧的日志钩子
|
|
|
|
+ Logger.ReplaceHooks(make(logrus.LevelHooks))
|
|
|
|
+
|
|
|
|
+ // 添加新的文件输出 Hook
|
|
|
|
+ Logger.AddHook(newHook(
|
|
|
|
+ logFile,
|
|
|
|
+ &logrus.JSONFormatter{},
|
|
|
|
+ logLevel,
|
|
|
|
+ ))
|
|
|
|
+
|
|
|
|
+ // 添加控制台输出 Hook
|
|
|
|
+ Logger.AddHook(newHook(
|
|
|
|
+ os.Stderr,
|
|
|
|
+ &logrus.TextFormatter{
|
|
|
|
+ FullTimestamp: true,
|
|
|
|
+ ForceColors: true,
|
|
|
|
+ },
|
|
|
|
+ logLevel,
|
|
|
|
+ ))
|
|
|
|
+ }
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ return Logger // 返回初始化后的 Logger 实例
|
|
}
|
|
}
|