是否可以在不丢失行号前缀的情况下包装 logrus.Logger 函数?
Is it possible to wrap logrus.Logger functions without losing the line number prefix?
当使用 wrapped logrus function/logger 时,记录器在所有日志行前加上记录器函数调用的文件名和行号,例如:
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()
有什么解决办法吗?
在 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)
}
看看这个或这个的一些变体是否适用于您的用例。我已经从上面的代码片段中删除了 application-specific 代码。
希望我没有误解,所以您需要调用记录器的“实际”路径和行号。下面的代码(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"
}
当使用 wrapped logrus function/logger 时,记录器在所有日志行前加上记录器函数调用的文件名和行号,例如:
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()
有什么解决办法吗?
在 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)
}
看看这个或这个的一些变体是否适用于您的用例。我已经从上面的代码片段中删除了 application-specific 代码。
希望我没有误解,所以您需要调用记录器的“实际”路径和行号。下面的代码(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"
}