【Logrus】以go代码实现的结构化日志记录为例,进行讲解,日志级别,不同日志输出位置,hook

file.go

package logs

import "os"

/*
实现了标准文件日志写入器。--- 将日志写到文件
*/

const LOGPATH = "runtime/logs/myLogs.log"

type fileWriter struct {
	*os.File
}

func (s *fileWriter) Flush() {
	s.Sync()
}

// 文件日志写入器,并在包初始化时注册为 "file" 类型。
func newFileWriter() LogWriter {
	file, err := os.OpenFile(LOGPATH, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		file = os.Stderr
	}
	return &fileWriter{
		file,
	}
}

func init() {
	RegisterInitWriterFunc("file", newFileWriter)
}


file_rotate.go

package logs

import (
	rotatelogs "github.com/lestrrat-go/file-rotatelogs" // 第三方库 github.com/lestrrat-go/file-rotatelogs 实现日志轮转
	"log"
	"time"
)

/*
带有轮转功能的文件日志写入器
*/

// 结构体封装了 rotatelogs.RotateLogs,实现了 LogWriter 接口。
type fileRotateWriter struct {
	*rotatelogs.RotateLogs
}

func (frw *fileRotateWriter) Flush() {
	frw.Close()
}

func newFileRotateWriter() LogWriter {
	writer, err := getRotateLogs()
	if err != nil {
		log.Fatal(err)
	}
	return &fileRotateWriter{
		writer,
	}
}

// 配置了日志轮转的具体参数,如保存时间和切分间隔。
// 在包初始化时注册为 "file-rotate" 类型。
func getRotateLogs() (*rotatelogs.RotateLogs, error) {
	path := LOGPATH
	logf, err := rotatelogs.New(
		path+".%Y%m%d%H%M",
		//rotatelogs.WithLinkName(path),               // 最新文件软链接到path, linux支持
		rotatelogs.WithMaxAge(time.Second*1800),     // 30分钟日志保存时间
		rotatelogs.WithRotationTime(time.Second*10), // 日志切分时间间隔
	)
	return logf, err
}

func init() {
	RegisterInitWriterFunc("file-rotate", newFileRotateWriter)
}


log.go

package logs

import (
	"github.com/sirupsen/logrus"
)

/*
实现了日志系统的初始化逻辑
*/

// Log 封装了 logrus.Entry 和 LogWriter,提供了统一的日志操作接口。
type Log struct {
	*logrus.Entry
	LogWriter
}

// Flush 调用底层 LogWriter 的 Flush 方法,确保日志数据被正确写入
func (l *Log) Flush() {
	l.LogWriter.Flush()
}

type LogConf struct {
	Level       logrus.Level
	AdapterName string
	Hook        logrus.Hook
}

// InitLog 配置初始化日志系统,设置日志级别、输出格式、钩子等。
func InitLog(conf LogConf) *Log {
	adapterName := "std"
	if conf.AdapterName != "" {
		adapterName = conf.AdapterName
	}
	writer, ok := writerAdapter[adapterName]
	if !ok {
		writer, _ = writerAdapter[adapterName]
	}

	log := &Log{
		logrus.NewEntry(logrus.New()),
		writer(),
	}
	log.Logger.SetFormatter(&logrus.JSONFormatter{})
	if conf.Hook != nil {
		log.Logger.AddHook(conf.Hook)
	}
	log.Logger.SetOutput(log.LogWriter)
	if conf.Level != 0 {
		log.Logger.SetLevel(conf.Level)
	}
	log.Logger.SetReportCaller(true)
	return log
}


std.go

package logs

import "os"

/*
实现了标准输出(通常是终端)日志写入器--- 将日志写到os.Stdout
*/

// 封装了 os.File,实现了 LogWriter 接口。
type stdWriter struct {
	*os.File
}

func (s *stdWriter) Flush() {
	s.Sync()
}

// 创建一个新的标准输出日志写入器,并在包初始化时注册为 "std" 类型。
func newStdWriter() LogWriter {
	return &stdWriter{
		os.Stderr,
	}
}

func init() {
	RegisterInitWriterFunc("std", newStdWriter)
}


writer.go

package logs

import "io"

/*
定义了日志写入器的接口和注册机制
*/

// 是一个全局的 map,用来存储不同类型的日志写入器初始化函数
var writerAdapter = make(map[string]InitLogWriterFunc)

type InitLogWriterFunc func() LogWriter

type LogWriter interface {
	io.Writer
	Flush()
}

// RegisterInitWriterFunc 注册不同类型的日志写入器初始化函数
func RegisterInitWriterFunc(adapterName string, writerFunc InitLogWriterFunc) {
	writerAdapter[adapterName] = writerFunc
}


1. 日志模块总览

  • 目标:提供一个灵活的日志系统,支持多种日志存储方式(文件、标准输出、带日志轮转的文件等)和丰富的日志功能(结构化日志、钩子等)。
  • 实现方式
    • 定义统一的日志接口 LogWriter,便于扩展。
    • 使用 Logrus 作为日志核心库。
    • 支持动态配置(日志格式、输出目标、日志级别等)。
    • 提供多种日志适配器(标准输出、文件、文件轮转)。
    • 通过钩子扩展日志功能(例如:特定级别日志的额外处理)。

2. 日志写入器基础接口

type LogWriter interface {
	io.Writer
	Flush()
}
  • 功能:定义日志写入器必须实现的接口。
    • Write:写入日志数据。
    • Flush:确保日志缓冲区中的数据被持久化(文件或其他目标)。

3. 日志适配器注册机制

var writerAdapter = make(map[string]InitLogWriterFunc)
type InitLogWriterFunc func() LogWriter

func RegisterInitWriterFunc(adapterName string, writerFunc InitLogWriterFunc) {
	writerAdapter[adapterName] = writerFunc
}
  • 功能
    • 使用全局 map 存储不同类型的日志写入器初始化函数。
    • 通过适配器名称(如 "file""file-rotate"),动态选择日志写入目标。
  • 优势:新增加日志类型时,只需实现对应的 InitLogWriterFunc 并注册即可,具有良好的扩展性。

4. 标准日志写入器

type stdWriter struct {
	*os.File
}

func (s *stdWriter) Flush() {
	s.Sync()
}

func newStdWriter() LogWriter {
	return &stdWriter{
		os.Stderr,
	}
}
  • 功能:将日志输出到标准错误输出(os.Stderr)。
  • 用途:适合开发和调试阶段的简单日志记录。

5. 文件日志写入器

type fileWriter struct {
	*os.File
}

func (s *fileWriter) Flush() {
	s.Sync()
}

func newFileWriter() LogWriter {
	file, err := os.OpenFile(LOGPATH, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		file = os.Stderr
	}
	return &fileWriter{
		file,
	}
}
  • 功能:将日志写入文件。
    • 默认路径:runtime/logs/myLogs.log
    • 支持追加写入(os.O_APPEND)。
  • 适配器名称"file"

6. 带轮转功能的文件日志写入器

type fileRotateWriter struct {
	*rotatelogs.RotateLogs
}

func (frw *fileRotateWriter) Flush() {
	frw.Close()
}

func newFileRotateWriter() LogWriter {
	writer, err := getRotateLogs()
	if err != nil {
		log.Fatal(err)
	}
	return &fileRotateWriter{
		writer,
	}
}
  • 功能:支持日志文件定期切分(轮转)并自动清理过期日志。
  • 配置参数
    • WithMaxAge:日志保留时间。
    • WithRotationTime:日志切分时间间隔。
  • 适配器名称"file-rotate"
  • 用途:适合生产环境下长时间运行的服务。

7. 日志系统初始化

type Log struct {
	*logrus.Entry
	LogWriter
}

type LogConf struct {
	Level       logrus.Level
	AdapterName string
	Hook        logrus.Hook
}

func InitLog(conf LogConf) *Log {
	// 动态选择日志写入器
	adapterName := "std"
	if conf.AdapterName != "" {
		adapterName = conf.AdapterName
	}
	writer, ok := writerAdapter[adapterName]
	if !ok {
		writer, _ = writerAdapter[adapterName]
	}

	log := &Log{
		logrus.NewEntry(logrus.New()),
		writer(),
	}
	log.Logger.SetFormatter(&logrus.JSONFormatter{}) // 设置日志格式为 JSON
	if conf.Hook != nil {
		log.Logger.AddHook(conf.Hook) // 添加钩子
	}
	log.Logger.SetOutput(log.LogWriter)             // 设置日志输出目标
	if conf.Level != 0 {
		log.Logger.SetLevel(conf.Level)             // 设置日志级别
	}
	log.Logger.SetReportCaller(true)                // 输出调用日志的位置
	return log
}
  • 功能:初始化日志系统,配置日志级别、输出目标、钩子等。
  • 关键点
    • 动态选择日志写入器(通过适配器名称)。
    • 支持结构化日志格式(JSON)。
    • 日志级别控制,默认 InfoLevel

8. 钩子扩展

type myHook struct{}

func (h *myHook) Levels() []logrus.Level {
	return []logrus.Level{logrus.ErrorLevel}
}

func (h *myHook) Fire(entry *logrus.Entry) error {
	log.Printf("%+v", entry)
	return nil
}
  • 功能:扩展日志功能(例如:特定级别的日志额外处理)。
  • 示例钩子:捕获 ErrorLevel 日志,并打印到控制台。

9. 示例程序

func main() {
	conf := logs.LogConf{
		Level:       logrus.InfoLevel,
		AdapterName: "file-rotate",
		Hook:        &myHook{},
	}
	log := logs.InitLog(conf)
	defer log.Flush()

	log.WithFields(logrus.Fields{
		"animal": "walrus",
		"size":   10,
	}).Info("A group of walrus emerges from the ocean")

	log.WithFields(logrus.Fields{
		"omg":    true,
		"number": 100,
	}).Fatal("The ice breaks!")
}
  • 功能
    1. 使用日志轮转功能记录日志。
    2. 添加上下文信息(WithFields)记录结构化日志。
    3. 使用钩子处理 ErrorLevel 日志。

总结

代码结构分析

1. 项目目录结构

目录结构大致如下:

golang18-logrus/
├── logs/
│   ├── file.go
│   ├── file_rotate.go
│   ├── log.go
│   ├── std.go
│   └── writer.go
└── main.go
2. 核心模块
2.1 logs

logs 包是整个日志系统的实现核心,它负责管理和初始化不同的日志写入器(Writer),并提供一个统一的日志接口。

2.1.1 writer.go
  • 功能:定义了日志写入器的接口和注册机制。
  • 关键点
    • LogWriter 接口继承了 io.Writer 并添加了一个 Flush() 方法。
    • RegisterInitWriterFunc 函数用于注册不同类型的日志写入器初始化函数。
    • writerAdapter 是一个全局的 map,用来存储不同类型的日志写入器初始化函数。
2.1.2 log.go
  • 功能:实现了日志系统的初始化逻辑。
  • 关键点
    • Log 结构体封装了 logrus.EntryLogWriter,提供了统一的日志操作接口。
    • InitLog 函数根据配置初始化日志系统,设置日志级别、输出格式、钩子等。
    • Flush 方法调用底层 LogWriterFlush 方法,确保日志数据被正确写入。
2.1.3 file.go
  • 功能:实现了标准文件日志写入器。
  • 关键点
    • fileWriter 结构体封装了 os.File,实现了 LogWriter 接口。
    • newFileWriter 函数创建一个新的文件日志写入器,并在包初始化时注册为 “file” 类型。
2.1.4 std.go
  • 功能:实现了标准输出(通常是终端)日志写入器。
  • 关键点
    • stdWriter 结构体封装了 os.File,实现了 LogWriter 接口。
    • newStdWriter 函数创建一个新的标准输出日志写入器,并在包初始化时注册为 “std” 类型。
2.1.5 file_rotate.go
  • 功能:实现了带有轮转功能的文件日志写入器。
  • 关键点
    • 使用了第三方库 github.com/lestrrat-go/file-rotatelogs 实现日志轮转。
    • fileRotateWriter 结构体封装了 rotatelogs.RotateLogs,实现了 LogWriter 接口。
    • getRotateLogs 函数配置了日志轮转的具体参数,如保存时间和切分间隔。
    • 在包初始化时注册为 “file-rotate” 类型。
2.2 main.go
  • 功能:项目的入口文件,演示如何使用自定义的日志系统。
  • 关键点
    • 创建了一个 LogConf 配置对象,指定了日志级别、适配器类型和钩子。
    • 调用 logs.InitLog 初始化日志系统,并使用 defer 确保程序退出时调用 Flush
    • 通过 log.WithFields 添加上下文信息,记录不同类型的日志。
    • 定义了一个简单的钩子 myHook,用于处理特定级别的日志事件。
3. 依赖库
  • github.com/sirupsen/logrus:一个流行的 Go 日志库,提供了丰富的日志功能,如日志级别、格式化、钩子等。
  • github.com/lestrrat-go/file-rotatelogs:一个用于实现日志轮转的第三方库,支持按时间或大小切分日志文件。
4. 总结

核心思想是通过 logs 包抽象出一个灵活的日志系统,支持多种日志写入方式(如标准输出、普通文件、带轮转的文件等),并通过 LogConf 配置来选择具体的日志适配器。


https://github.com/0voice

你可能感兴趣的:(golang,驱动开发,开发语言)