Как получить трассировку стека, указывающую на истинную причину ошибки в Golang?

предположим, у меня есть такой код:

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

в случае err != nil Я вам что-то вроде этого:

panic: some error explanation here

goroutine 1 [running]:
main.main()
    /tmp/blabla/main.go:6 +0x80

эта трассировка стека полностью законна, но иногда эти сообщения об ошибках могут не прояснить, что произошло,и поэтому я хотел бы углубиться в исходный код сторонней библиотеки, чтобы исследовать, что именно вызывает эту ошибку. Однако, когда мой код паникует, как это, нет никакого способа получить фактическое место, которое вернуло это ошибка.

немного больше разъяснений: поскольку я прихожу из мира JVM, где выбрасываются исключения, я могу полностью проследить, какая именно строка кода выбрасывает исключение и, таким образом, может легко найти место и увидеть, что пошло не так. GO stack trace заканчивается именно там, где мой код паникует и, следовательно, не слишком полезен в моем случае.

Я создал игровую площадку здесь и в идеале я хотел бы иметь возможность отследить ошибку до места, откуда она была фактически возвращена, а не паника. (например, в строке 17, return "", errors.New("some error explanation here"))

это вообще возможно?

4 ответов


коротко: это невозможно. С ошибки это значения, они не относятся каким-либо особым образом. Из-за этого, когда функция (обычно) возвращается, стек больше не доступен (т. е. другой вызов функции может перезаписать память, используемую стеком функций returning-error).

существует инструмент под названием след который был представлен с go1.5, но на данный момент нет всеобъемлющего учебника, доступного ни один из тех, кого я нашел, не говорит, что эта функция будут включены.


как указывали другие, ошибки трассировки в go не тривиальны. Есть такие проекты, как jujo или/ошибки, которые позволяют обернуть ошибки, а затем отследить эти ошибки обратно. Чтобы это работало жестко, вы должны использовать их последовательно на протяжении всего проекта, и это не поможет вам с ошибками в сторонних библиотеках или с ошибками, которые обрабатываются и никогда не возвращаются.

потому что это такая распространенная проблема, и я очень бесился от этого. Я написал небольшую утилиты отладки это добавит код отладки для файлов go, которые регистрируют каждую возвращенную ошибку (значение, реализующее error) и функция, в которой она была возвращена в STDOUT (если вам нужно более продвинутое ведение журнала, просто взломайте регистратор в проекте, это очень просто).

установка

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

использование

для отладки всех файлов в текущей директории:

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

для удаления добавленного кода отладки от файлы:

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

затем просто скомпилируйте и запустите свой код или тестовые примеры.

Пример Вывода

[...]
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
[...]

как вы можете видеть из этого вывода, очень легко сказать, в какой функции первоначально произошла ошибка. Как только вы это узнаете, вы можете использовать отладчик для получения большего контекста.


взгляните на: https://github.com/efimovalex/stackerr

это то, что вы ищете?

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()
}

результат:

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)

Я думаю, что это можно сделать более легко. Вы можете попробовать ошибки, оборачивая использование пакета ошибок golang "default":

вам нужно определить интерфейс для реализации по вашей ошибке:

type stackTracer interface {
    StackTrace() errors.StackTrace
}

затем используйте его при обертывании / обработке ошибки:

err, ok := errors.(stackTracer) // ok is false if errors doesn't implement stackTracer

stack := err.StackTrace()
fmt.Println(stack)