美文网首页
Logrus源码阅读(1)--基本用法

Logrus源码阅读(1)--基本用法

作者: HHFCodeRv | 来源:发表于2019-07-18 17:03 被阅读0次

    选择golang日志库时, 使用logrus的主要原因就是因为star比较多, 而且社区活跃度非常高. 在项目使用过程中, 发现logrus的调用入口, 性能, 插件, 自定义插件, 输出格式等都非常优秀, 值得学习一下

    整体结构图

    logrus

    整体来看总共提供两种方式调用:

    1. logrus.Info("hello logrus")
    2. logrus.WithField(logruns.Fields{"key1":"v1"}).Info("hello logrus")

    这些函数都在exported.go文件中. 当然为了提供不同级别输出日志的功能, 里面实现了各种各样的print函数, 如: Infof, Error, Errorf, Panic等等

    在直接使用logrus等情况下exported.go是唯一入口, 但是我们可以简单封装一下, 跟项目框架更加贴合, 这个留在后面用具体例子来解释这么做的原因和好处

    简单介绍使用方法

    普通用法

    package main
    
    import (
      log "github.com/sirupsen/logrus"
    )
    
    func main() {
      log.Info("A walrus appears")
    }
    
    time="2019-07-16T22:51:31+08:00" level=info msg="hello logrus"
    

    注意log "github.com/sirupsen/logrus"这里将logrus的别名设置为log, 然后就直接调用了log.Info. 假如你的项目现在使用的标准库log, 则可以无缝迁移到logrus上, 因为标准库实现的print函数较少, logrus全部已经实现, 只需要简单引入这个别名即可

    WithFields

    由于logrus不建议下面的用法:

    log.Fatalf("Failed to send event %s to topic %s with key %d")
    

    因为logrus鼓励结构化的日志输出, 上面的用法就非常的不人性化, 不美观. 应该改成下面的方式:

    log.WithFields(log.Fields{
      "event": event,
      "topic": topic,
      "key": key,
    }).Fatal("Failed to send event")
    

    但是根据实际使用过程中发现, 一般要使用WithFields输出日志字段时, 那些字段一般都是公共字段, 比如: request_id, token等等, 程序里到处打印WithFields也不是个优美的办法(后续会解释怎么做)

    所以尽管logrus不建议我们使用Printf, 但是程序该需要用到Printf的地方还是需要的

    设置打印格式

    logrus自带两种方式的输出格式: 纯文本和JSON格式的.

    JSONFormatter

    func main() {
        log.SetFormatter(&log.JSONFormatter{})
        log.Info("hello logrus")
    }
    
    {"level":"info","msg":"hello logrus","time":"2019-07-17T22:47:14+08:00"}
    

    TextFormatter

    默认情况下就是TextFormatter, 默认情况下是带颜色输出的. 当然也不是任何时候都输出带颜色的结果, 取决于在终端输出并且不是运行在windows系统, 或者是否设置过ForceColors=true, 如果设置了就会按照有颜色的方式输出.

    程序会在启动的时候检测是否是终端运行, 具体的实现就是terminal_check_(OS).go, 具体实现后续关于TextFormatter的具体实现再看

    func main() {
        log.SetFormatter(&log.TextFormatter{})
        log.Info("hello logrus")
    }
    
    INFO[0000] hello logrus
    

    也可以禁用

    func main() {
        log.SetFormatter(&log.TextFormatter{
            DisableColors: true,
        })
        log.Info("hello logrus")
    }
    
    
    time="2019-07-17T23:44:42+08:00" level=info msg="hello logrus"
    

    同时, 你可以根据自己的实际需求, 去定制自己的Formater, 只需要实现Format方法即可

    设置调用log的位置

    func main() {
        log.SetFormatter(&log.TextFormatter{
            DisableColors: true,
        })
        log.SetReportCaller(true)
        log.Info("hello logrus")
    }
    
    time="2019-07-18T10:40:21+08:00" level=info msg="hello logrus" func=main.main file="/Users/haohongfan/goproject/test/logrus_test/main.go:33"
    

    但是请注意:

    Note that this does add measurable overhead - the cost will depend on the version of Go,
    but is between 20 and 40% in recent tests with 1.6 and 1.7.You can validate this in your environment
    via benchmarks: go test -bench=.*CallerTracing
    

    也就设置这个是有性能问题的, 生产环境是一定不能启动用, 其实也没有必要, 我们并不关心是哪一行打印的(如果你的日志确实需要靠这个来确定的话, 那你的日志是需要优化一下的)

    设置日志级别

    logrus日志一共7级别, 从高到低: panic, fatal, error, warn, info, debug, trace.

    在生产环境时选择打印Info以上级别的日志, 就可以log.SetLevel(log.InfoLevel), 那么Debug, Trace就不会打印出来. 源码实现这个功能很简单, 就是判断Print函数的级别是否大于SetLevel的值

    log.SetLevel(log.ErrorLevel)这个函数要求传入的参数是Level类型的值(其实也就是uint32, type Level uint32), 我们在封装我们代码时, 肯定要定义panic等这些级别. logrus本身提供将panic转换成PanicLevel的函数和获取xxLevel对应的字符串. 这些都封装在logrus.go里面

    func main() {
        // log.SetLevel(log.ErrorLevel)
        level, _ := log.ParseLevel("info")
        log.SetLevel(level)
        log.Info("hello logrus")
        fmt.Println(log.ErrorLevel)
    }
    
    error
    time="2019-07-18T11:41:02+08:00" level=info msg="hello logrus"
    

    Hook

    Hook是一大特色, 也给logrus留下各种各样的扩展机会. 比如: lfshook, dingrus

    你可以根据自己的特殊需求扩展自己的Hook, 只需要简单实现Levels() []Level, Fire(*Entry) error即可. logrus提供一个syslog, test的插件, 同时github上可以找到很多

    后面说源码的时候, 我会选择lfshook作为例子进行分析其实现细节, 同时我们也会选择一个功能实现一个

    日志的文件输出, 切分, 删过期文件

    logrus本身不提供这样的功能, 需要借助第三方插件lfshook进行

    相对高级的用法

    前面说到程序里到处log.WithFields{log.Field{xxx}}是一种比较不好的用法, 故我们开发的框架在集成logrus的时候要简单封装一下. logrus README也有提到

    requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
    requestLogger.Info("something happened on that request") # will log request_id and user_ip
    requestLogger.Warn("something not great happened")
    

    下面说具体如何操作, 可以参考bilibili sniper

    比如跟gin的结合使用, 这是我的项目的一段实际的代码. 在log目录下创建log.go

    // Entry ling-nest log Entry
    func Entry(ctx *gin.Context) *logrus.Entry {
        return logrus.WithFields(logrus.Fields{
            "device_type": ctx.Value("device_type"),
            "channel":     ctx.Value("channel"),
            "license":     ctx.Value("license"),
            "v4":          ctx.Value("V4"),
        })
    }
    

    实际使用时: log.Entry(context).Info("xxxxxx")

    总结

    第一篇关于logrus源码阅读主要是为了介绍相关的用法. 从下面开始将正式进入源码阶段. 下一篇主要根据源码介绍logrus的整个生命周期

    相关文章

      网友评论

          本文标题:Logrus源码阅读(1)--基本用法

          本文链接:https://www.haomeiwen.com/subject/yvtrlctx.html