How do I get a stack trace indicating the actual cause of the error in the Golang?

Let's say I have a code like this:

value, err := some3rdpartylib.DoSomething() if err != nil { panic(err) } 

In the case of err != nil I get something like this:

 panic: some error explanation here goroutine 1 [running]: main.main() /tmp/blabla/main.go:6 +0x80 

This stack trace is legitimate, but sometimes these error messages may not clarify what happened, and therefore I would like to delve into the source code of a third-party library to find out what exactly causes this error to be returned. However, when my panic code is like this, there is no way to get the actual location that returned this error.

A bit more clarification: since I come from the JVM world where exceptions are thrown, I can completely trace which line of code the exception was thrown and, therefore, can easily find the place and see what went wrong. The stack trace ends exactly where my code panics and therefore is not very useful in my case.

I created a playground here , and ideally I would like to trace the error to the place where it was actually returned from, not the panic. (e.g. line 17, return "", errors.New("some error explanation here") )

Is it possible?

+8
source share
4 answers

In short: this is impossible. Since errors are values , they are not handled in any special way. Because of this, when a function (usually) returns, the stack is no longer available (i.e., another call to the function may overwrite the memory used by the stack return function).

There is a tool called trace that was introduced in go1.5, but there is currently no comprehensive guide available, and none of those that I have found say that this type of function will be enabled.

+4
source

Take a look at: https://github.com/efimovalex/stackerr

Is this what you are looking for?

 package main import "github.com/efimovalex/stackerr" import "fmt" func f1() *stackerr.Err { err := stackerr.Error("message") return err.Stack() } func f2() *stackerr.Err { err := f1() return err.Stack() } type t1 struct{} func (t *t1) f3() *stackerr.Err { err := f2() return err.Stack() } func main() { ts := t1{} err := ts.f3() err.Log() } 

Result:

 2017/08/31 12:13:47 Error Stacktrace: -> github.com/efimovalex/stackerr/example/main.go:25 (main.main) -> github.com/efimovalex/stackerr/example/main.go:19 (main.(*t1).f3) -> github.com/efimovalex/stackerr/example/main.go:12 (main.f2) -> github.com/efimovalex/stackerr/example/main.go:7 (main.f1) 
+1
source

As others noted, trace errors in go are not trivial. There are projects such as jujo / errors that allow you to wrap errors and then track these errors. To make this difficult, you must constantly use them throughout your project, and this will not help you with errors in third-party libraries or with errors that will be processed and will never be returned.

Because it is such a common problem, and I'm really annoyed by it. I wrote a small debugging utility that will add debugging code to send files that register every error returned (a value that implements error ), and a function in which it was returned to STDOUT (if you need more advanced logging, just hack the logger into project, itโ€™s very simple).

Installation

 go get github.com/gellweiler/errgotrace go install github.com/gellweiler/errgotrace 

Using

Debugging all files in the current directory:

 $ find . -name '*.go' -print0 | xargs -0 errgotrace -w 

To remove the added debug code from go files:

 $ find . -name '*.go' -print0 | xargs -0 errgotrace -w -r 

Then just simply compile and run your code or your test cases.

Output result

 [...] 2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectKey: EOF token found 2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectItem: EOF token found 2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectKey: EOF token found 2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectItem: EOF token found 2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectKey: At 3:4: nested object expected: LBRACE got: ASSIGN 2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectItem: At 3:4: nested object expected: LBRACE got: ASSIGN 2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectList: At 3:4: nested object expected: LBRACE got: ASSIGN 2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.Parse: At 2:31: literal not terminated 2017/12/13 00:54:39 [ERRGOTRACE] parser.Parse: At 2:31: literal not terminated 2017/12/13 00:54:39 [ERRGOTRACE] hcl.parse: At 2:31: literal not terminated 2017/12/13 00:54:39 [ERRGOTRACE] hcl.ParseBytes: At 2:31: literal not terminated 2017/12/13 00:54:39 [ERRGOTRACE] formula.parse: parsing failed [...] 

As you can see from this conclusion, it is very easy to determine in which function an error occurred. Once you know this, you can use the debugger to get more context.

+1
source

I think there is an easier way to achieve this. You can try to wrap errors using the golang "default" error package:

You need to determine the interface that your error will implement:

 type stackTracer interface { StackTrace() errors.StackTrace } 

Then use it when handling / handling the error:

 err, ok := errors.(stackTracer) // ok is false if errors doesn't implement stackTracer stack := err.StackTrace() fmt.Println(stack) // here you'll have your stack trace 
+1
source

All Articles