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)
}
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)
}
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
}
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)
}
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
}
LogWriter
,便于扩展。Logrus
作为日志核心库。type LogWriter interface {
io.Writer
Flush()
}
Write
:写入日志数据。Flush
:确保日志缓冲区中的数据被持久化(文件或其他目标)。var writerAdapter = make(map[string]InitLogWriterFunc)
type InitLogWriterFunc func() LogWriter
func RegisterInitWriterFunc(adapterName string, writerFunc InitLogWriterFunc) {
writerAdapter[adapterName] = writerFunc
}
map
存储不同类型的日志写入器初始化函数。"file"
或 "file-rotate"
),动态选择日志写入目标。InitLogWriterFunc
并注册即可,具有良好的扩展性。type stdWriter struct {
*os.File
}
func (s *stdWriter) Flush() {
s.Sync()
}
func newStdWriter() LogWriter {
return &stdWriter{
os.Stderr,
}
}
os.Stderr
)。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"
。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"
。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
}
InfoLevel
。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
日志,并打印到控制台。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!")
}
WithFields
)记录结构化日志。ErrorLevel
日志。目录结构大致如下:
golang18-logrus/
├── logs/
│ ├── file.go
│ ├── file_rotate.go
│ ├── log.go
│ ├── std.go
│ └── writer.go
└── main.go
logs
包logs
包是整个日志系统的实现核心,它负责管理和初始化不同的日志写入器(Writer),并提供一个统一的日志接口。
writer.go
LogWriter
接口继承了 io.Writer
并添加了一个 Flush()
方法。RegisterInitWriterFunc
函数用于注册不同类型的日志写入器初始化函数。writerAdapter
是一个全局的 map,用来存储不同类型的日志写入器初始化函数。log.go
Log
结构体封装了 logrus.Entry
和 LogWriter
,提供了统一的日志操作接口。InitLog
函数根据配置初始化日志系统,设置日志级别、输出格式、钩子等。Flush
方法调用底层 LogWriter
的 Flush
方法,确保日志数据被正确写入。file.go
fileWriter
结构体封装了 os.File
,实现了 LogWriter
接口。newFileWriter
函数创建一个新的文件日志写入器,并在包初始化时注册为 “file” 类型。std.go
stdWriter
结构体封装了 os.File
,实现了 LogWriter
接口。newStdWriter
函数创建一个新的标准输出日志写入器,并在包初始化时注册为 “std” 类型。file_rotate.go
github.com/lestrrat-go/file-rotatelogs
实现日志轮转。fileRotateWriter
结构体封装了 rotatelogs.RotateLogs
,实现了 LogWriter
接口。getRotateLogs
函数配置了日志轮转的具体参数,如保存时间和切分间隔。main.go
LogConf
配置对象,指定了日志级别、适配器类型和钩子。logs.InitLog
初始化日志系统,并使用 defer
确保程序退出时调用 Flush
。log.WithFields
添加上下文信息,记录不同类型的日志。myHook
,用于处理特定级别的日志事件。github.com/sirupsen/logrus
:一个流行的 Go 日志库,提供了丰富的日志功能,如日志级别、格式化、钩子等。github.com/lestrrat-go/file-rotatelogs
:一个用于实现日志轮转的第三方库,支持按时间或大小切分日志文件。核心思想是通过 logs
包抽象出一个灵活的日志系统,支持多种日志写入方式(如标准输出、普通文件、带轮转的文件等),并通过 LogConf
配置来选择具体的日志适配器。
https://github.com/0voice