【问题标题】:Is it possible to wrap logrus.Logger functions without losing the line number prefix?是否可以在不丢失行号前缀的情况下包装 logrus.Logger 函数?
【发布时间】:2020-08-30 13:36:52
【问题描述】:

当使用包装好的 logrus 函数/记录器时,记录器会在所有日志行前加上记录器函数调用的文件名和行号,例如:

INFO[0000]logging.go:39 myfolder/logging.Info()

如果我像这样包装日志函数,例如: 包日志记录

import (
    "fmt"
    "github.com/sirupsen/logrus"
    "os"
    "path"
    "runtime"
)

var (
    log *logrus.Logger
)

func init() {

    log = logrus.New()
    log.SetReportCaller(true)
    log.Formatter = &logrus.TextFormatter{
        CallerPrettyfier: func(f *runtime.Frame) (string, string) {
            filename := path.Base(f.File)
            return fmt.Sprintf("%s()", f.Function), fmt.Sprintf("%s:%d", filename, f.Line)
        },
    }
}

func Info(args ...interface{}) {
        log.Info(args...)
}

此函数发出的每一行都将以logging function 调用的行号为前缀。这与预期的一样,但期望的行为是每行都以调用 Info 的行的行号为前缀。

所需的输出应该是:

INFO[0000]myfile.go:39 myfolder/myfile.myfunction()

有什么办法吗?

【问题讨论】:

    标签: go logging callstack


    【解决方案1】:

    在 logrus 中是不可能做到的。我有类似的要求,最终做了以下对我们有用的事情。

    package mylog
    
    import (
        "fmt"
        "github.com/Sirupsen/logrus"
        "runtime"
        "strings"
    )
    
    var logger = logrus.New()
    
    func SetLogFormatter(formatter logrus.Formatter) {
        logger.Formatter = formatter
    }
    
    // Info logs a message at level Info on the standard logger.
    func Info(args ...interface{}) {
        if logger.Level >= logrus.InfoLevel {
            entry := logger.WithFields(logrus.Fields{})
            entry.Data["file"] = fileInfo(2)
            entry.Info(args...)
        }
    }
    
    func fileInfo(skip int) string {
        _, file, line, ok := runtime.Caller(skip)
        if !ok {
            file = "<???>"
            line = 1
        } else {
            slash := strings.LastIndex(file, "/")
            if slash >= 0 {
                file = file[slash+1:]
            }
        }
        return fmt.Sprintf("%s:%d", file, line)
    }
    

    看看这个或它的一些变体是否适用于您的用例。我已经从上面的代码 sn-p 中删除了应用程序特定的代码。

    【讨论】:

    • 这对我有用,谢谢@mayank-patel。我的建议是您也应该将此解决方案添加到logrus github issue,因为这是该问题的第一个谷歌搜索结果。
    • 嗨@mayank,调用函数中跳过参数的目的是什么?
    【解决方案2】:

    希望我没有误解,所以您想要调用记录器的“实际”路径和行号。下面的代码(以 json 格式为例)应该可以满足您的需求。如果您想添加更多信息,例如函数名称等,只需修改caller()方法即可。

    logrus.SetReportCaller(true)
    // ...
    
    logrus.SetFormatter(&logrus.JSONFormatter{
        CallerPrettyfier:  caller(),
        FieldMap: logrus.FieldMap{
            logrus.FieldKeyFile: "caller",
        },
    })
    
    // caller returns string presentation of log caller which is formatted as
    // `/path/to/file.go:line_number`. e.g. `/internal/app/api.go:25`
    func caller() func(*runtime.Frame) (function string, file string) {
        return func(f *runtime.Frame) (function string, file string) {
            p, _ := os.Getwd()
    
            return "", fmt.Sprintf("%s:%d", strings.TrimPrefix(f.File, p), f.Line)
        }
    }
    
    {
      "caller": "/internal/controller/create.go:21",
      "level": "info",
      "msg": "i am a dummy log",
      "time": "2020-08-30T19:17:48+01:00"
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-03
      • 1970-01-01
      • 2022-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-20
      相关资源
      最近更新 更多