logrus介绍
golang标准库的日志框架非常简单,仅仅提供了print,panic和fatal三个函数。对于更精细的日志级别、日志文件分割,以及日志分发等方面,并没有提供支持。在golang的世界,流行的日志框架包括logrus、zap、zerolog、seelog等。
logrus配置
1. 日志级别: logrus有7个日志级别,依次是Trace << Debug << Info << Warning << Error << Fatal << Panic
// 只输出不低于当前级别是日志数据 logrus.SetLevel(logrus.DebugLevel)
2. 日志格式: logrus内置了JSONFormatter
和TextFormatter
两种格式,也可以通过Formatter
接口定义日志格式
// TextFormatter格式 logrus.SetFormatter(&logrus.TextFormatter{ ForceColors: true, EnvironmentOverrideColors: true, TimestampFormat: "2006-01-02 15:04:05", //时间格式 // FullTimestamp:true, // DisableLevelTruncation:true, })
// JSONFormatter格式 logrus.SetFormatter(&logrus.JSONFormatter{ PrettyPrint: false, //格式化 TimestampFormat: "2006-01-02 15:04:05", //时间格式 })
3. 输出文件:
logfile, _ := os.OpenFile("./app.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) logrus.SetOutput(logfile) //默认为os.stderr
4. 日志定位: 定位行号(如:func=main.main file="./xxx.go:38"
)
logrus.SetReportCaller(true)
示例:
func init() { logrus.SetLevel(logrus.DebugLevel) logrus.SetFormatter(&logrus.JSONFormatter{ TimestampFormat: "2006-01-02 15:04:05", }) logfile, _ := os.OpenFile("./app.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) logrus.SetOutput(logfile) //默认为os.stderr } //方式一:logrus函数(最终调用的是logrus.StandardLogger默认实例方法) func main() { logrus.Infoln("测试数据") }
日志打印
FieldLogger接口: FieldLogger
定义了所有日志打印的方法
type FieldLogger interface { WithField(key string, value interface{}) *Entry WithFields(fields Fields) *Entry WithError(err error) *Entry Debugf(format string, args ...interface{}) Infof(format string, args ...interface{}) Printf(format string, args ...interface{}) Warnf(format string, args ...interface{}) Warningf(format string, args ...interface{}) Errorf(format string, args ...interface{}) Fatalf(format string, args ...interface{}) Panicf(format string, args ...interface{}) Debug(args ...interface{}) Info(args ...interface{}) Print(args ...interface{}) Warn(args ...interface{}) Warning(args ...interface{}) Error(args ...interface{}) Fatal(args ...interface{}) Panic(args ...interface{}) Debugln(args ...interface{}) Infoln(args ...interface{}) Println(args ...interface{}) Warnln(args ...interface{}) Warningln(args ...interface{}) Errorln(args ...interface{}) Fatalln(args ...interface{}) Panicln(args ...interface{}) }
日志打印1: 默认实例 (函数)
,即通过logrus包提供的函数(覆盖了FieldLogger
接口的所有方法),直接打印日志。但其实logrus包函数是调用了logrus.Loger
默认实例。
// 直接调用包函数 func main() { logrus.Infoln("...") logrus.Errorln("...") // ... }
日志打印2:Logger实例(对象)
,它实现了FieldLogger
接口。
func main() { //var loger = logrus.StandardLogger() var loger = logrus.New() loger.Formatter = &logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"} loger.Infoln("可以创建任意数量的logrus实例") }
日志打印3:Entry示例(对象)
,它也实现了FieldLogger
接口,是最终是日志打印入口。
- 这里用到了
Field
机制,logrus鼓励通过Field
机制进行精细化的、结构化的日志记录,而不是通过冗长的消息来记录日志。
func main() { logrus.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"}) // Entry实例 entry := logrus.WithFields(logrus.Fields{ "global": "全局字段", }) entry.WithFields(logrus.Fields{"module": "用户模块"}). Info("测试ok") }
HOOK机制
- hook即钩子,拦截器。它为logrus提供了强大的功能扩展,如将日志分发到任意地方,如本地文件系统、
logstash
、es
等,或者切割日志、定义日志内容和格式等。hook接口原型如下:
type Hook interface { Levels() []Level //日志级别 Fire(*Entry) error //打印入口(Entry对象) }
Hook - 日志切割:
import ( "github.com/lestrrat-go/file-rotatelogs" "github.com/rifflock/lfshook" "github.com/sirupsen/logrus" "time" ) // 说明:按时间切割日志文件(2秒创建一个日志文件) func main() { hook := NewLfsHook("app_hook", time.Second*2, 5) logrus.AddHook(hook) logrus.Infoln("测试开始") log := logrus.WithFields(logrus.Fields{"module": "用户模块"}) for i := 0; i < 10; i++ { log.Infoln("成功", i) time.Sleep(time.Second) } } // 日志钩子(日志拦截,并重定向) func NewLfsHook(logName string, rotationTime time.Duration, leastDay uint) logrus.Hook { writer, err := rotatelogs.New( // 日志文件 logName+".%Y%m%d%H%M%S", // 日志周期(默认每86400秒/一天旋转一次) rotatelogs.WithRotationTime(rotationTime), // 清除历史 (WithMaxAge和WithRotationCount只能选其一) //rotatelogs.WithMaxAge(time.Hour*24*7), //默认每7天清除下日志文件 rotatelogs.WithRotationCount(leastDay), //只保留最近的N个日志文件 ) if err != nil { panic(err) } // 可设置按不同level创建不同的文件名 lfsHook := lfshook.NewHook(lfshook.WriterMap{ logrus.DebugLevel: writer, logrus.InfoLevel: writer, logrus.WarnLevel: writer, logrus.ErrorLevel: writer, logrus.FatalLevel: writer, logrus.PanicLevel: writer, }, &logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"}) return lfsHook }
Hook - Redis重定向: 即将日志输出到redis
import ( logredis "github.com/rogierlommers/logrus-redis-hook" "io/ioutil" "github.com/sirupsen/logrus" ) func init() { hookConfig := logredis.HookConfig{ Host: "localhost", Key: "test", Format: "v1", App: "my_app_name", Port: 6379, Hostname: "my_app_hostname", DB: 0, // optional TTL: 3600, } hook, err := logredis.NewHook(hookConfig) if err == nil { logrus.AddHook(hook) } else { logrus.Errorf("logredis error: %q", err) } } func main() { logrus.WithFields(logrus.Fields{ "module": "user"}). Info("user login") // If you want to disable writing to stdout, use setOutput logrus.SetOutput(ioutil.Discard) logrus.Info("log to Redis") } // 测试: // 1.启动redis服务: redis-server // 2.监控redis数据: redis-cli monitor
其他Hook:
MongoDb
:https://github.com/weekface/mgorusRedis
:https://github.com/rogierlommers/logrus-redis-hookInfluxDb
:https://github.com/abramovic/logrus_influxdbLogstash
:https://github.com/bshuster-repo/logrus-logstash-hook
Gin日志
- 将gin框架的日志定向到logrus日志文件
func init() { // 输出格式 logrus.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"}) // 输出路径 logfile, _ := os.OpenFile("./app.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) logrus.SetOutput(logfile) // Gin日志重定向 gin.DisableConsoleColor() //不需要颜色 gin.DefaultWriter = io.MultiWriter(os.Stdout, logfile) //os.Stdout } //测试:curl 0.0.0.0:8080/index func main() { log := logrus.WithFields(logrus.Fields{ "module": "用户模块", }) r := gin.Default() r.GET("/index", func(c *gin.Context) { log.Warnln("gin日志数据") c.String(200, "ok") }) _ = r.Run() }
Fatal处理
- logrus的
Fatal
输出,会执行os.Exit(1)
。logrus提供RegisterExitHandler
方法,可以在系统异常时调用一些资源释放api等,让应用正确地关闭。
func main() { logrus.RegisterExitHandler(func() { fmt.Println("发生了fatal异常,执行关闭文件等工作") }) logrus.Warnln("warn测试") logrus.Fatalln("fatal测试") logrus.Infoln("info测试") //不会执行 }
线程安全
- 默认情况下,logrus的api都是线程安全的,其内部通过互斥锁来保护并发写。互斥锁在调用hooks或者写日志的时候执行。如果不需要锁,可以调用
logger.SetNoLock()
来关闭之。
可以关闭logrus互斥锁的情形包括: - 没有设置hook,或者所有的hook都是线程安全的实现。
- 写日志到logger.Out已经是线程安全的了。例如,logger.Out已经被锁保护,或者写文件时,文件是以O_APPEND方式打开的,并且每次写操作都小于4k。
参考
The Times 03/Jan/2009 Chancellor on brink of second bailout for banks
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性