第二十七:使用 Logrus + Lumberjack 创建日志中间件

https://download.csdn.net/blog/column/12814461/143185986

Go 复制代码
package main

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"gopkg.in/natefinch/lumberjack.v2"
)

// 设置 Logger
func SetupLogger() *logrus.Logger {
	logger := logrus.New()
	logger.SetOutput(&lumberjack.Logger{
		//Filename:   "log/gin.log", //  这里自动生成的 文件和文本
		Filename:   generateLogFileName(),
		MaxSize:    10, // MB
		MaxBackups: 3,
		MaxAge:     28, // days
		Compress:   true,
	})
	logger.SetFormatter(&logrus.TextFormatter{
		FullTimestamp: true,
	})
	return logger
}

// 日志中间件
func LoggerMiddleware(logger *logrus.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		logger.WithFields(logrus.Fields{
			"method": c.Request.Method,
			"path":   c.Request.URL.Path,
		}).Info("Request received")

		c.Next() // 继续处理请求

		logger.WithFields(logrus.Fields{
			"status": c.Writer.Status(),
			"method": c.Request.Method,
			"path":   c.Request.URL.Path,
		}).Info("Response sent")

		logger.Warning(logrus.Fields{
			"status": c.Writer.Status(),
			"method": c.Request.Method,
		})
	}
}

// generateLogFileName 生成日志文件名,格式为:app-YYYY-MM-DD.log
func generateLogFileName() string {
	// 获取当前执行文件的路径
	exePath, err := os.Executable()
	if err != nil {
		panic(err)
	}
	// 解析符号链接,如果当前程序是符号链接,则返回符号链接的目标路径
	realPath, err := filepath.EvalSymlinks(exePath)
	if err != nil {
		panic(err)
	}
	//获取当前执行文件的不带后缀的文件名
	appName := filepath.Base(realPath)
	appName = appName[:len(appName)-len(filepath.Ext(appName))]
	return fmt.Sprintf("logs/%s-%s.log", appName, time.Now().Format("2006-01-02"))
}

func main() {
	r := gin.Default()
	logger := SetupLogger()

	r.Use(LoggerMiddleware(logger))

	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "pong"})
	})

	r.Run(":8080")
}

新一代标准日志库slog

https://www.cnblogs.com/cheyunhua/p/18612100

https://blog.csdn.net/wick_light/article/details/140031724 // 多个 日志文件处理

Go 复制代码
package main

import (
	"gopkg.in/natefinch/lumberjack.v2"
	"log/slog"
	"os"
)

func main() {
	// 配置 lumberjack 日志归档
	lumberjackLogger := &lumberjack.Logger{
		Filename:   "./tmp/app.log", // 日志文件路径
		MaxSize:    10,              // 单个日志文件最大大小(单位:MB)
		MaxBackups: 5,               // 保留的旧日志文件个数
		MaxAge:     30,              // 保留的旧日志文件最大天数(单位:天)
		Compress:   true,            // 是否压缩旧日志文件
	}

	handler := slog.NewJSONHandler(lumberjackLogger, &slog.HandlerOptions{
		AddSource: true, // 如果设置为 true,日志输出中将包含源代码的位置(文件名和行号),默认值:false(不记录源位置)
		Level:     slog.LevelDebug,
	})

	// 第一种情况
	// 使用默认日志记录器,此模式日志输出格式为text文本型,且不会输出Debug级别日志
	//logger := slog.Default()
	//第二种情况 日志级别控制:
	//2. 日志级别控制
	//slog 支持以下日志级别
	//
	//• slog.LevelDebug
	//• slog.LevelInfo
	//• slog.LevelWarn
	//• slog.LevelError
	//HandlerOptions 是 log/slog 提供的一种配置结构,用于自定义日志处理器的行为。通过配置 HandlerOptions,可以实现日志的源位置跟踪、日志级别动态调整,以及对日志属性的定制化处理,参数包括如下:
	//
	//• AddSource:启用源代码位置记录,便于定位日志位置
	//• Level:控制日志级别,可静态或动态调整
	//• ReplaceAttr:修改、移除或过滤属性,支持灵活的日志定制
	//按照日志级别控制,并输出JSON格式
	/*handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		Level: slog.LevelDebug,
	})

	logger := slog.New(handler)

	// 输出结构化日志
	logger.Debug("This is a debug message")
	logger.Info("Starting qkp platform log", "version", "v3.2.2")
	logger.Warn("Low disk space", "remaining", "500MB")
	logger.Error("Failed to connect to ETCD cluster", "error", "connection timeout")*/

	// 第三种情况:
	//3. 自定义日志处理器(Handler)
	//slog.Handler 是日志输出的核心接口,允许自定义日志处理行为 内置处理器
	//
	//• slog.NewTextHandler: 以文本形式输出日志(默认格式)
	//• slog.NewJSONHandler: 以 JSON 格式输出日志

	// 第四种情况 4. 使用上下文记录器
	//slog 支持通过上下文管理附加信息
	/*handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		Level: slog.LevelDebug,
	})

	logger := slog.New(handler).With("platform", "qkp", "version", "v3.2.2")

	// 输出结构化日志
	logger.Debug("This is a debug message")
	logger.Info("Starting qkp platform log", "version", "v3.2.2")
	logger.Warn("Low disk space", "remaining", "500MB")
	logger.Error("Failed to connect to ETCD cluster", "error", "connection timeout")
	*/

	// 第五种: 5. 集成lumberjack进行日志切片和归档

	// 配置 lumberjack 日志归档
	/*lumberjackLogger :=&lumberjack.Logger{
		Filename:"/tmp/app.log",// 日志文件路径
		MaxSize:10,// 单个日志文件最大大小(单位:MB)
		MaxBackups:5,// 保留的旧日志文件个数
		MaxAge:30,// 保留的旧日志文件最大天数(单位:天)
		Compress:true,// 是否压缩旧日志文件
	}

	handler := slog.NewJSONHandler(lumberjackLogger,&slog.HandlerOptions{
		AddSource:true,// 如果设置为 true,日志输出中将包含源代码的位置(文件名和行号),默认值:false(不记录源位置)
		Level: slog.LevelDebug,
	})

	logger := slog.New(handler).With("platform","qkp","version","v3.2.2")

	// 输出结构化日志
	logger.Debug("This is a debug message")
	logger.Info("Starting qkp platform log","version","v3.2.2")
	logger.Warn("Low disk space","remaining","500MB")
	logger.Error("Failed to connect to ETCD cluster","error","connection timeout")
	在/tmp目录下查询app.log日志
	*/

	// 第七种 :6. 结合归档日志级别以及替换关键字格式化日志

	// 配置 lumberjack 日志归档
	/*lumberjackLogger :=&lumberjack.Logger{
			Filename:"/tmp/app.log",// 日志文件路径
			MaxSize:10,// 单个日志文件最大大小(单位:MB)
			MaxBackups:5,// 保留的旧日志文件个数
			MaxAge:30,// 保留的旧日志文件最大天数(单位:天)
			Compress:true,// 是否压缩旧日志文件
		}

		handler := slog.NewJSONHandler(lumberjackLogger,&slog.HandlerOptions{
			AddSource:true,// 如果设置为 true,日志输出中将包含源代码的位置(文件名和行号),默认值:false(不记录源位置)
			Level: slog.LevelDebug,
			ReplaceAttr:func(groups []string, a slog.Attr) slog.Attr{
				// 替换关键字platform 的值从qkp变成kubernetes
				if a.Key=="platform"{
					return slog.Attr{Key: a.Key,Value: slog.StringValue("kubernetes")}
				}
				return a
			},
		})

		logger := slog.New(handler).With("platform","qkp","version","v3.2.2")

		// 输出结构化日志
		logger.Debug("This is a debug message")
		logger.Info("Starting qkp platform log","version","v3.2.2")
		logger.Warn("Low disk space","remaining","500MB")
		logger.Error("Failed to connect to ETCD cluster","error","connection timeout")
	}
	*/

	logger := slog.New(handler).With("platform", "qkp", "version", "v3.2.2")

	// 输出结构化日志
	logger.Debug("This is a debug message")
	logger.Info("Starting qkp platform log", "version", "v3.2.2")
	logger.Warn("Low disk space", "remaining", "500MB")
	logger.Error("Failed to connect to ETCD cluster", "error", "connection timeout")
}

日志分隔:

Go 复制代码
package main
 
import (
    "os"
    "github.com/sirupsen/logrus"
    "gopkg.in/natefinch/lumberjack.v2"
)
 
func main() {
    // 配置 lumberjack 用于 info 级别的日志
    infoLogger := &lumberjack.Logger{
        Filename:   "info.log",
        MaxSize:    500,
        MaxBackups: 3,
        MaxAge:     28,
        Compress:   true,
    }
 
    // 配置 lumberjack 用于 error 级别的日志
    errorLogger := &lumberjack.Logger{
        Filename:   "error.log",
        MaxSize:    500,
        MaxBackups: 3,
        MaxAge:     28,
        Compress:   true,
    }
 
    // 创建 logrus 的实例
    logger := logrus.New()
 
    // 设置 info 级别的日志输出目标
    logger.SetLevel(logrus.InfoLevel)
    logger.SetOutput(infoLogger)
 
    // 设置 error 级别的日志输出目标
    logger.AddHook(&errorHook{writer: errorLogger})
 
    // 使用 logrus 记录日志
    logger.Info("This is an info message")
    logger.Error("This is an error message")
}
 
// errorHook 是一个自定义的 logrus 钩子,用于将 error 级别的日志输出到指定的 writer
type errorHook struct {
    writer *lumberjack.Logger
}
 
// Fire 方法会在每次记录日志时被调用
func (h *errorHook) Fire(entry *logrus.Entry) error {
    if entry.Level <= logrus.ErrorLevel {
        line, err := entry.String()
        if err != nil {
            return err
        }
        h.writer.Write([]byte(line))
    }
    return nil
}
 
// Levels 方法返回需要处理的日志级别
func (h *errorHook) Levels() []logrus.Level {
    return []logrus.Level{
        logrus.ErrorLevel,
        logrus.FatalLevel,
        logrus.PanicLevel,
    }
}
相关推荐
Yeats_Liao19 小时前
Go Web 编程快速入门 05 - 表单处理:urlencoded 与 multipart
前端·golang·iphone
Tony Bai20 小时前
【Go 网络编程全解】12 本地高速公路:Unix 域套接字与网络设备信息
开发语言·网络·后端·golang·unix
Yeats_Liao21 小时前
Go Web 编程快速入门 06 - 响应 ResponseWriter:状态码与头部
开发语言·后端·golang
mit6.82421 小时前
[Agent可视化] 编排工作流(Go) | Temporal引擎 | DAG调度器 | ReAct模式实现
开发语言·后端·golang
猪哥-嵌入式1 天前
Go语言实战教学:从一个混合定时任务调度器(Crontab)深入理解Go的并发、接口与工程哲学
开发语言·后端·golang
简单点了2 天前
go前后端项目的启动 、打包和部署
开发语言·后端·golang
九江Mgx2 天前
用 Go 手搓一个 NTP 服务:从“时间混乱“到“精准同步“的奇幻之旅
golang·ntp
脚踏实地的大梦想家2 天前
【Go】P11 掌握 Go 语言函数(二):进阶玩转高阶函数、闭包与 Defer/Panic/Recover
开发语言·后端·golang
CoLiuRs2 天前
在 go-zero 中优雅使用 Google Wire 实现依赖注入
后端·微服务·golang