英文:
Is it possible to wrap log.Logger functions without losing the line number prefix?
问题
当使用log.Lshortfile
标志时,记录器会在所有日志行前加上记录器函数调用的文件名和行号,例如:
<pre>myfile.go:14: Hello, world!</pre>
如果我像这样包装日志函数:
func info(pattern string, args ...interface{}) {
myLogger.Printf(pattern + "\n", args...)
}
这个函数发出的每一行都会以Printf
调用的行号作为前缀。这是预期的行为,但期望的行为是每一行都以调用info
的行号作为前缀。
有没有什么办法可以解决这个问题?
英文:
When using the log.Lshortfile
flag, the logger prefixes all log lines with the file name and line number of the logger function call, for example:
<pre>myfile.go:14: Hello, world!</pre>
If I wrap the log function like this, for instance:
func info(pattern string, args ...interface{}) {
myLogger.Printf(pattern + "\n", args...)
}
Every line emitted by this function is going to be prefixed with the line number of the Printf
call. That is as expected, but the desired behavior is for each line to be prefixed with the line number of the line where info
is called.
Is there any way around it?
答案1
得分: 17
log.Logger
的方法调用Logger.Output()
方法将消息发送到适当的输出。Logger.Output()
允许您传递calldepth
(要跳过的帧数)。
不幸的是,log.Logger
的方法中包含了calldepth
的固定值,因此无法提供偏移量来跳过包装函数的帧。
但一个更好的选择是从您的包装函数中调用Logger.Output()
,这样您就不必自己处理帧和行数。还要注意,如果要记录的消息不以换行符结尾,您不需要附加换行符\n
,因为log.Logger
类型已经处理了这个。
因此,一个更好和更短的选择是:
var myLogger = log.New(os.Stdout, "[my]", log.Lshortfile)
func info(pattern string, args ...interface{}) {
myLogger.Output(2, fmt.Sprintf(pattern, args...))
}
测试代码:
func main() {
log.SetFlags(log.Lshortfile)
log.Println("hello")
info("world")
}
输出结果(在Go Playground上尝试):
main.go:11: hello
[my]main.go:12: world
如您所见,info()
打印了正确的行号(与上一行中log.Println()
打印的行号相比多1)。
英文:
Methods of log.Logger
call the Logger.Output()
method to send the message to the appropriate output. Logger.Output()
allows you to pass the calldepth
(the number of frames to skip).
Unfortunately methods of log.Logger
contain the calldepth
"wired in", so you can't provide an offset to skip the wrapper function's frame.
But a much better alternative is to call this Logger.Output()
from your wrapper, so you don't have to bother with frames and lines yourself. Also note that you don't need to append a newline "\n"
, as the log.Logger
type already does that if the message to be logged does not end with a newline.
So a better and shorter alternative:
var myLogger = log.New(os.Stdout, "[my]", log.Lshortfile)
func info(pattern string, args ...interface{}) {
myLogger.Output(2, fmt.Sprintf(pattern, args...))
}
Testing it:
func main() {
log.SetFlags(log.Lshortfile)
log.Println("hello")
info("world")
}
Output (try it on the Go Playground):
main.go:11: hello
[my]main.go:12: world
As you can see, info()
prints the proper line number (+1 compared to the line number printed by log.Println()
in the previous line).
答案2
得分: 0
我本来打算将这个作为我的当前解决方法包含在问题中,但我想这也是一个有效的答案。我希望有人告诉我一个我可能错过的记录器配置选项,让我可以调整记录器在调用runtime.Caller
时使用的深度。
一个解决方法是移除log.Lshortfile
标志,并手动实现这个行为:
func info(format string, args ...interface{}) {
_, file, line, _ := runtime.Caller(1)
prefix := fmt.Sprintf("%v:%v: ", path.Base(file), line)
if logger != nil {
logger.Printf(prefix+format+"\n", args...)
}
}
英文:
I was going to include this in the question as my current workaround, but I suppose it is a valid answer. I'm hoping somebody can tell me of a logger config option I've missed that lets me adjust the depth the logger uses when it calls runtime.Caller
.
A workaround is to remove the log.Lshortfile
flag and implement the behavior manually:
func info(format string, args ...interface{}) {
_, file, line, _ := runtime.Caller(1)
prefix := fmt.Sprintf("%v:%v: ", path.Base(file), line)
if logger != nil {
logger.Printf(prefix+format+"\n", args...)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论