当前位置 博文首页 > logrus日志自定义格式操作

    logrus日志自定义格式操作

    作者:chen09122763 时间:2021-02-01 06:13

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

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

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

    logrus的基本使用这里就不再多说了,可以移步 http://blog.iis7.com/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日志自定义格式操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持站长博客。

    js
    下一篇:没有了