logrus日志自定义格式操作

 更新时间:2020年11月10日 10:48:13   作者:chen09122763  
这篇文章主要介绍了logrus日志自定义格式操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

由于最近开始做一些go写的外围程序,因此开始关注go的日志,毕竟自带的logger模块功能较少。简单看了一些资料以后最开始使用seelog,性能感觉也不错,可以通过配置文件做很多额外处理。

但是由于协程的使用,需要日志标明协程号来方便日志查询请求应答。在一番尝试以后仍然没有解决,只能看看有没有其他日志库备选,因此选择了logrus(github上同类星星最多)

其实一开始看介绍时就看见过logrus这个库,但是之所以没有一开始考虑它, 是因为许多介绍都说它无法显示文件名和行号。不过时代是发展的,现在的logrus版本已经支持该设置。

logrus的基本使用这里就不再多说了,可以移步 https://www.jb51.net/article/168799.htm

1)设置行号

log.SetReportCaller(true)

log.WithFields(log.Fields{"animal": "walrus"}).Info("A walrus appears")

time="2020-03-29T15:58:09+08:00" level=info msg="A walrus appears" func=main.main file="F:/workspace/go/src/test/main.go:12" animal=walrus

设置行号通过SetRportCaller实现,但是发现一个问题默认的设置file是全路径的,这样实际在日志中输出的话,日志会有很多无用数据,针对于我自己的需求仅为需要提示go文件名以及行号和goid即可。

2)自定义格式

logrus提供了SetFormatter可以设置格式,logrus自带的格式为(JSONFormatter,TextFormatter)

func (s *LogFormatter) Format(entry *log.Entry) ([]byte, error) {
 timestamp := //日期格式实现
 entry.Caller //调用者信息
 entry.Data //withfiled方式传入的数据
 msg := 自己想要的格式输出内容
 return []byte(msg), nil
}
 
func main() {
 log.SetFormatter(new(LogFormatter)) //注册自定义格式
}

自定义格式可选取的素材是log.Entry中提供的,具体了解需要自行查看源码,这里仅简单介绍。其中Caller是调用信息,有文件名,函数名,行数等信息,但是需要注意的是如果没有设置SetRportCaller为true,这里无法获取改信息。

entry.Data信息为WithFields传入的map,如果需要一定的范式输出的话,可以好好利用。此外获取当前协程号也是在该函数中实现的,具体可以看文末的范例。定义完成后需要注册自定义格式。

3)日志写入文件

logrus默认是标准输出, SetOutput方法可以更换输出io,设置文件句柄就是写入文件了。网上能找到很多通过改写io.write或者hook的方式拆分文件,推送文件等等其他各种需要,总之非常强大,不过我因为没有试过,所以就不在本文中描述了。但是统一项目的日志都是在指定目录按日期目录存放的,因此需要实现io.write,针对日期来划分文件。

package gotools
 
import (
 "bytes"
 "errors"
 "fmt"
 "os"
 "path/filepath"
 "runtime"
 "strconv"
 "strings"
 "time"
 
 log "github.com/sirupsen/logrus"
)
 
//日志自定义格式
type LogFormatter struct{}
 
//格式详情
func (s *LogFormatter) Format(entry *log.Entry) ([]byte, error) {
 timestamp := time.Now().Local().Format("0102-150405.000")
 var file string
 var len int
 if entry.Caller != nil {
 file = filepath.Base(entry.Caller.File)
 len = entry.Caller.Line
 }
 //fmt.Println(entry.Data)
 msg := fmt.Sprintf("%s [%s:%d][GOID:%d][%s] %s\n", timestamp, file, len, getGID(), strings.ToUpper(entry.Level.String()), entry.Message)
 return []byte(msg), nil
}
 
func getGID() uint64 {
 b := make([]byte, 64)
 b = b[:runtime.Stack(b, false)]
 b = bytes.TrimPrefix(b, []byte("goroutine "))
 b = b[:bytes.IndexByte(b, ' ')]
 n, _ := strconv.ParseUint(string(b), 10, 64)
 return n
}
 
type logFileWriter struct {
 file *os.File
 logPath string
 fileDate string //判断日期切换目录
 appName string
 encoding string
}
 
func (p *logFileWriter) Write(data []byte) (n int, err error) {
 if p == nil {
 return 0, errors.New("logFileWriter is nil")
 }
 if p.file == nil {
 return 0, errors.New("file not opened")
 }
 
 //判断是否需要切换日期
 fileDate := time.Now().Format("20060102")
 if p.fileDate != fileDate {
 p.file.Close()
 err = os.MkdirAll(fmt.Sprintf("%s/%s", p.logPath, fileDate), os.ModePerm)
 if err != nil {
 return 0, err
 }
 filename := fmt.Sprintf("%s/%s/%s-%s.log", p.logPath, fileDate, p.appName, fileDate)
 
 p.file, err = os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC, 0600)
 if err != nil {
 return 0, err
 }
 
 }
 if p.encoding != "" {
 dataToEncode := ConvertStringToByte(string(data), p.encoding)
 n, e := p.file.Write(dataToEncode)
 return n, e
 }
 
 n, e := p.file.Write(data)
 return n, e
 
}
 
//初始化日志
func InitLog(logPath string, appName string, encoding string) {
 fileDate := time.Now().Format("20060102")
 //创建目录
 err := os.MkdirAll(fmt.Sprintf("%s/%s", logPath, fileDate), os.ModePerm)
 if err != nil {
 log.Error(err)
 return
 }
 
 filename := fmt.Sprintf("%s/%s/%s-%s.log", logPath, fileDate, appName, fileDate)
 file, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC, 0600)
 if err != nil {
 log.Error(err)
 return
 }
 
 fileWriter := logFileWriter{file, logPath, fileDate, appName, encoding}
 log.SetOutput(&fileWriter)
 
 log.SetReportCaller(true)
 log.SetFormatter(new(LogFormatter)) 
}
package main
 
import (
 log "github.com/sirupsen/logrus"
 "sand.com/gotools"
)
 
func main() {
 
 gotools.InitLog("./log/", "test", "gb18030")
 log.WithFields(log.Fields{"animal": "walrus"}).Info("A walrus appears")
 log.Info("测试中文")
}

因为其他系统的日志都是gbk编码的,因此在io.write时做了编码转换,如果没有这样需要的话最好不要做这种转换影响性能

0329-163148.199 [main.go:11][GOID:1][INFO] A walrus appears

0329-163148.199 [main.go:12][GOID:1][INFO] 测试中文

补充知识: go使用logrus同时输出屏幕和文件日志

我就废话不多说了,大家还是直接看代码吧~

func InitLog() {
 //设置输出样式,自带的只有两种样式logrus.JSONFormatter{}和logrus.TextFormatter{}
 log.SetFormatter(&log.TextFormatter{})
 log.SetOutput(os.Stdout)
 //设置output,默认为stderr,可以为任何io.Writer,比如文件*os.File
 file, err := os.OpenFile("checkemstools.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
 writers := []io.Writer{
 file,
 os.Stdout}
 //同时写文件和屏幕
 fileAndStdoutWriter := io.MultiWriter(writers...)
 if err == nil {
 log.SetOutput(fileAndStdoutWriter)
 } else {
 log.Info("failed to log to file.")
 }
 //设置最低loglevel
 log.SetLevel(log.InfoLevel)
}

以上这篇logrus日志自定义格式操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Go 中 time.After 可能导致的内存泄露问题解析

    Go 中 time.After 可能导致的内存泄露问题解析

    这篇文章主要介绍了Go 中 time.After 可能导致的内存泄露,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • Golang协程池的实现与应用

    Golang协程池的实现与应用

    这篇文章主要介绍了Golang协程池的实现与应用,使用协程池的好处是减少在创建和销毁协程上所花的时间以及资源的开销,解决资源不足的问题,需要详细了解可以参考下文
    2023-05-05
  • 解决go mod私有仓库拉取的问题

    解决go mod私有仓库拉取的问题

    这篇文章主要介绍了解决go mod私有仓库拉取的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • 解决Golang 中使用WaitGroup的那点坑

    解决Golang 中使用WaitGroup的那点坑

    这篇文章主要介绍了解决Golang 中使用WaitGroup的那点坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • 详解Go如何实现协程并发执行

    详解Go如何实现协程并发执行

    线程是通过本地队列,全局队列或者偷其它线程的方式来获取协程的,目前看来,线程运行完一个协程后再从队列中获取下一个协程执行,还只是顺序执行协程的,而多个线程一起这么运行也能达到并发的效果,接下来就给给大家详细介绍一下Go如何实现协程并发执行
    2023-08-08
  • Go语言开发发送Get和Post请求的示例

    Go语言开发发送Get和Post请求的示例

    这篇文章主要介绍了Go语言开发发送Get和Post请求的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • Golang中interface的基本用法详解

    Golang中interface的基本用法详解

    Go 中接口也是一个使用得非常频繁的特性,好的软件设计往往离不开接口的使用,比如依赖倒置原则(通过抽象出接口,分离了具体实现与实际使用的耦合)。 今天,就让我们来了解一下 Go 中接口的一些基本用法
    2023-01-01
  • 使用Go语言实现发送微信群消息

    使用Go语言实现发送微信群消息

    这篇文章主要为大家详细介绍了如何使用Go语言实现发送微信群消息,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-01-01
  • Golang官方限流器库实现限流示例详解

    Golang官方限流器库实现限流示例详解

    这篇文章主要为大家介绍了Golang官方限流器库使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 详解golang的切片扩容机制

    详解golang的切片扩容机制

    golang的切片扩容机制是golang面试者绕不开的一扇大门,无论在面试提问,或者面试情景上都绕不开它,今天就说说我理解下的切片扩容机制,感兴趣的小伙伴跟着小编一起来看看吧
    2023-07-07

最新评论