• 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")
        
    }

    效果:

  • 相关阅读:
    第四篇Scrum冲刺博客
    第三篇Scrum冲刺博客
    蔡勒公式和吉姆拉尔森公式计算星期几
    事后诸葛亮
    Alpha阶段项目复审
    团队作业6——复审与事后分析
    第7篇 Scrum 冲刺博客
    第6篇 Scrum 冲刺博客
    第5篇 Scrum 冲刺博客
    第4篇 Scrum 冲刺博客
  • 原文地址:https://www.cnblogs.com/shuiche/p/9507760.html
Copyright © 2020-2023  润新知