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

haohongfan · · 1439 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

选择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的整个生命周期


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:haohongfan

查看原文:Logrus源码阅读(1)--基本用法

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1439 次点击  
加入收藏 微博
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传