zoukankan      html  css  js  c++  java
  • go包之logrus显示日志文件与行号

    前言:

    logrus是go中比较好的一个log模块.github上的很多开源项目都在使用这个模块, 我在写这个博文时, github上的logrus的stars数已经有8214了.最近在用这个模块时,发现不能打印日志所在文件和行数.在开发过程中, 感觉这就不是很友好了. 

    项目地址: logrus github 地址

    提醒:此方法在并发情况下, 会导致系统奔溃. 请谨慎使用

    不记录文件名和行号处理办法

    logrus支持自定义的hook, 我这里就是使用hook的方式来记录

    一个Entry为一条日志记录, 我们的Hook需要做的就是在每一个条的日志记录里面添加调用时的文件名和行号. 核心就是实现自己的Fire方法, 以下是参考实现:

    1. 我们新建一个hook的文件. 写入如下代码

    package hooks
    
    import (
        "fmt"
        "runtime"
        "strings"
        "github.com/sirupsen/logrus"
    )
    // ContextHook for log the call context
    type contextHook struct {
        Field  string
        Skip   int
        levels []logrus.Level
    }
    // NewContextHook use to make an hook
    // 根据上面的推断, 我们递归深度可以设置到5即可.
    func NewContextHook(levels ...logrus.Level) logrus.Hook {
        hook := contextHook{
            Field:  "line",
            Skip:   5,
            levels: levels,
        }
        if len(hook.levels) == 0 {
            hook.levels = logrus.AllLevels
        }
        return &hook
    }
    
    // Levels implement levels
    func (hook contextHook) Levels() []logrus.Level {
        return logrus.AllLevels
    }
    
    // Fire implement fire
    func (hook contextHook) Fire(entry *logrus.Entry) error {
        entry.Data[hook.Field] = findCaller(hook.Skip)
        return nil
    }
    // 对caller进行递归查询, 直到找到非logrus包产生的第一个调用.
    // 因为filename我获取到了上层目录名, 因此所有logrus包的调用的文件名都是 logrus/...
    // 因此通过排除logrus开头的文件名, 就可以排除所有logrus包的自己的函数调用
    func findCaller(skip int) string {
        file := ""
        line := 0
        for i := 0; i < 10; i++ {
            file, line = getCaller(skip + i)
            if !strings.HasPrefix(file, "logrus") {
                break
            }
        }
        return fmt.Sprintf("%s:%d", file, line)
    }
    // 这里其实可以获取函数名称的: fnName := runtime.FuncForPC(pc).Name()
    // 但是我觉得有 文件名和行号就够定位问题, 因此忽略了caller返回的第一个值:pc
    // 在标准库log里面我们可以选择记录文件的全路径或者文件名, 但是在使用过程成并发最合适的,
    // 因为文件的全路径往往很长, 而文件名在多个包中往往有重复, 因此这里选择多取一层, 取到文件所在的上层目录那层.
    func getCaller(skip int) (string, int) {
        _, file, line, ok := runtime.Caller(skip)
        //fmt.Println(file)
        //fmt.Println(line)
        if !ok {
            return "", 0
        }
        n := 0
        for i := len(file) - 1; i > 0; i-- {
            if file[i] == '/' {
                n++
                if n >= 2 {
                    file = file[i+1:]
                    break
                }
            }
        }
        return file, line
    }

    2. 添加hook到logrus  -- ( logrus定义为了全局使用 )

    package main
    
    import (
        log "github.com/sirupsen/logrus"
        zzxHook "study/ginStudy/hook"
    )
    
    func main() {
        var Logger = log.New()
        Logger.Hooks.Add(zzxHook.NewContextHook())
        Logger.WithFields(log.Fields{
            "animal": "walrus",
        }).Info("A walrus appears")
    }

    效果:

     

    2. 添加hook到logrus  -- ( logrus普通使用方式 )

    package main
    
    import (
        log "github.com/sirupsen/logrus"
        zzxHook "study/ginStudy/hook"
    )
    
    func main() {
        log.AddHook(zzxHook.NewContextHook())
        log.WithFields(log.Fields{
            "animal": "walrus",
        }).Info("A walrus appears")
        
    }

    效果:

  • 相关阅读:
    面试官:软件测试没搞懂这些,哪里来的自信投简历? 刁钻问得高频的面试题(含答案)
    软件测试行业的职业素养?——《高级软件测试-高级软件测试分析师》第一章
    什么?你正在学web自动化测试?那这些Selenium的基本操作你了解过吗?
    没想到 Google 排名第一的编程语言,为什么会这么火?
    为什么大家都在用Fiddler?
    看了很多文章,就这篇说明白了什么是接口测试(含视频教程)
    Win 10 安裝 Docker 及相關設置
    Dynamics CRM
    Dynamics CRM
    Dynamics CRM
  • 原文地址:https://www.cnblogs.com/shuiche/p/9507760.html
Copyright © 2011-2022 走看看