您如何获得Golang程序来打印刚刚调用的错误的行号?


93

我试图使用Go语言在Golang程序中抛出错误,log.Fatal但是log.Fatal也没有打印出log.Fatal运行该行的行。无法访问名为log.Fatal的行号吗?即有没有办法在抛出错误时获取行号?

我试图用谷歌搜索,但不确定如何。我能得到的最好的结果是打印堆栈跟踪,我认为这很好,但可能有点过多。我也不想debug.PrintStack()每次都需要行号时就写东西,我只是惊讶地发现没有任何内置函数可以用于这种log.FatalStackTrace()或非服装的东西。

另外,我不想制作自己的调试/错误处理工具的原因是,我不想让人们不得不学习如何使用我的特殊服装处理代码。我只想要一些标准,人们以后可以阅读我的代码,就像

“嗯,所以它抛出一个错误并执行X ...”

了解我的代码的人越少越好:)



在您打印行号的那一刻,这意味着我将不得不深入研究您的代码,因此“在这里人们越少了解我的代码越好”。您应该做的是清楚明确的错误。
Wessie 2014年

Answers:


121

您可以在自定义记录仪上设置标志,也可以在默认值上设置为包含LlongfileLshortfile

// to change the flags on the default logger
log.SetFlags(log.LstdFlags | log.Lshortfile)

因此,要使其正常工作,我只需要将其设置在一个包文件的顶部,该文件即可用于该包的所有文件?
曹2014年

4
是的,如果您使用的是自定义日志,则可以使用var mylog = log.New(os.Stderr, "app: ", log.LstdFlags | log.Lshortfile)
OneOfOne 2014年

我真的必须创建一个变量吗?我不能只在go文件的顶部执行log.SetFlags(log.LstdFlags | log.Lshortfile)吗?我收到一个错误:expected declaration, found 'INDENT' log尝试执行时log.SetFlags(log.LstdFlags | log.Lshortfile)。不得不为它创建一个变量让我很烦,为什么不能有一个log.Fatal("string", log.Flag)。但是创建一个新的变量日志确实可行。创建日志变量和东西是标准的事情吗?
Pinocchio 2014年

3
@Pinocchio:该错误是因为它不是有效的Go语言,您不能在顶层进行裸函数调用。将其放在init()或其他一些入口点中。
JimB 2014年

5
您必须将其放入func init() {}
OneOfOne 2014年

94

精简版, 没有直接内置的东西,但是您可以使用以下方法以最少的学习曲线来实现它 runtime.Caller

func HandleError(err error) (b bool) {
    if err != nil {
        // notice that we're using 1, so it will actually log where
        // the error happened, 0 = this function, we don't want that.
        _, fn, line, _ := runtime.Caller(1)
        log.Printf("[error] %s:%d %v", fn, line, err)
        b = true
    }
    return
}

//this logs the function name as well.
func FancyHandleError(err error) (b bool) {
    if err != nil {
        // notice that we're using 1, so it will actually log the where
        // the error happened, 0 = this function, we don't want that.
        pc, fn, line, _ := runtime.Caller(1)

        log.Printf("[error] in %s[%s:%d] %v", runtime.FuncForPC(pc).Name(), fn, line, err)
        b = true
    }
    return
}

func main() {
    if FancyHandleError(fmt.Errorf("it's the end of the world")) {
        log.Print("stuff")
    }
}

playground


11
虽然已经给出的答案可以很好地解决问题,但是您的解决方案提醒我存在一些很棒的东西-运行时包!可爱的东西:) golang.org/pkg/runtime
Gwyneth Llewelyn

fn从中分配的变量runtime.Caller()实际上是文件名,而不是函数引用。我认为fn是函数,而不是filename
sshow

1
太棒了!谢谢。这是runtime软件包用法的一个很好的例子。对于通过日志进行调试非常有帮助。
18augst

1

如果您确实需要堆栈跟踪,请查看https://github.com/ztrue/tracerr

我创建此程序包的目的是为了使堆栈跟踪和源代码片段都能更快地调试并记录错误,并提供更多详细信息。

这是一个代码示例:

package main

import (
    "io/ioutil"
    "github.com/ztrue/tracerr"
)

func main() {
    if err := read(); err != nil {
        tracerr.PrintSourceColor(err)
    }
}

func read() error {
    return readNonExistent()
}

func readNonExistent() error {
    _, err := ioutil.ReadFile("/tmp/non_existent_file")
    // Add stack trace to existing error, no matter if it's nil.
    return tracerr.Wrap(err)
}

这是输出: golang错误堆栈跟踪

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.