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

效果:

 

posted @ 2018-08-20 20:02 水车 阅读(...) 评论(...) 编辑 收藏