Разработка программного обеспечения связана не только с написанием кода, но и с его отладкой. И отладка должна быть по возможности комфортной.
С некоторыми ошибками мы пишем в лог стек вызовов. Используемая нами IDE (Idea, GoLand) позволяет по скопированному стеку вызовов получить комфортную навигацию по файлам (Analyze external stack traces). К сожалению, эта возможность хорошо работает только в том случае, если бинарый файл собран на том же хосте, на котором запущена IDE.
Этот пост посвящён тому, как мы пытались подружить формат стека вызовов и IDE.
Исходный код можно скачать по адресу: https://github.com/bozaro/go-stack-trace
Собственно, программа (stacktrace/main.go):
package main
import (
"fmt"
"github.com/Masterminds/cookoo"
"github.com/pkg/errors"
)
func main() {
// Build a new Cookoo app.
registry, router, context := cookoo.Cookoo()
// Fill the registry.
registry.AddRoutes(
cookoo.Route{
Name: "TEST",
Help: "A test route",
Does: cookoo.Tasks{
cookoo.Cmd{
Name: "hi",
Fn: HelloWorld,
},
},
},
)
// Execute the route.
router.HandleRequest("TEST", context, false)
}
func HelloWorld(cxt cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) {
fmt.Printf("%+v\n", errors.New("Hello World"))
return true, nil
}
И небольшой go.mod:
module github.com/bozaro/go-stack-trace
go 1.20
require (
github.com/Masterminds/cookoo v1.3.0
github.com/pkg/errors v0.9.1
)
Пример вывода:
➜ GO111MODULE=off GOPATH=$(pwd) go get -d github.com/bozaro/go-stack-trace/stacktrace
➜ GO111MODULE=off GOPATH=$(pwd) go run github.com/bozaro/go-stack-trace/stacktrace
Hello World
main.HelloWorld
/home/bozaro/gopath/src/github.com/bozaro/go-stack-trace/stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
/home/bozaro/gopath/src/github.com/Masterminds/cookoo/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
/home/bozaro/gopath/src/github.com/Masterminds/cookoo/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
/home/bozaro/gopath/src/github.com/Masterminds/cookoo/router.go:131
main.main
/home/bozaro/gopath/src/github.com/bozaro/go-stack-trace/stacktrace/main.go:27
runtime.main
/usr/lib/go-1.20/src/runtime/proc.go:250
runtime.goexit
/usr/lib/go-1.20/src/runtime/asm_amd64.s:1598
Здесь всё просто: мы видим полные пути до каждого файла.
При этом все пути расположены либо в src директории GoLang, либо в каталоге GOPATH.
К сожалению, такой стек будет указывать на существующие файлы только в том случае, когда исполняемый файл собран в окружении с тем же расположением каталогов. В нашем случае, когда у части разработчиков MacOS, а сборка для боевого окружения осуществляется под Linux, это требование невыполнимо.
К счастью, есть флаг -trimpath, который отрезает вариативную часть от стека вызовов:
➜ GO111MODULE=off GOPATH=$(pwd) go run -trimpath github.com/bozaro/go-stack-trace/stacktrace
Hello World
main.HelloWorld
github.com/bozaro/go-stack-trace/stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
github.com/Masterminds/cookoo/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
github.com/Masterminds/cookoo/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
github.com/Masterminds/cookoo/router.go:131
main.main
github.com/bozaro/go-stack-trace/stacktrace/main.go:27
runtime.main
runtime/proc.go:250
runtime.goexit
runtime/asm_amd64.s:1598
В этом случае все пути будут либо относительно GOPATH, либо относительно src в директории GoLang.
Получился вполне переносимый вид стека вызовов.
Сравним вывод стека вызовов без него:
➜ git clone https://github.com/bozaro/go-stack-trace.git .
➜ go run ./stacktrace
Hello World
main.HelloWorld
/home/bozaro/github/go-stack-trace/stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
/home/bozaro/go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
/home/bozaro/go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
/home/bozaro/go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:131
main.main
/home/bozaro/github/go-stack-trace/stacktrace/main.go:27
runtime.main
/usr/lib/go-1.20/src/runtime/proc.go:250
runtime.goexit
/usr/lib/go-1.20/src/runtime/asm_amd64.s:1598
И аналогичный вывод с -trimpath:
➜ go run -trimpath ./stacktrace
Hello World
main.HelloWorld
github.com/bozaro/go-stack-trace/stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
github.com/Masterminds/cookoo@v1.3.0/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
github.com/Masterminds/cookoo@v1.3.0/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
github.com/Masterminds/cookoo@v1.3.0/router.go:131
main.main
github.com/bozaro/go-stack-trace/stacktrace/main.go:27
runtime.main
runtime/proc.go:250
runtime.goexit
runtime/asm_amd64.s:1598
Без -trimpath мы по прежнему видим полные пути до каждого файла. При этом у нас явно прослеживаются три источника с исходными файлами:
В итоге удовлетворительный формат переносимого стека вызовов получил такой вид:
main.HelloWorld
stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:131
main.main
stacktrace/main.go:27
runtime.main
GOROOT/src/runtime/proc.go:250
runtime.goexit
GOROOT/src/runtime/asm_amd64.s:1598
То есть:
Преобразование пути из вида без -trimpath при этом выглядит тривиально.
Но у этого метода есть ряд недостатков:
В нашем случае в большинстве случаев стек брался из логов и там он был уже в удобоваримом виде, так что мы этот вариант серьезно не рассматривали.
...
INFO: Running command line: bazel-bin/stacktrace/stacktrace_/stacktrace
Hello World
main.HelloWorld
stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
external/com_github_masterminds_cookoo/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
external/com_github_masterminds_cookoo/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
external/com_github_masterminds_cookoo/router.go:131
main.main
stacktrace/main.go:27
runtime.main
GOROOT/src/runtime/proc.go:250
runtime.goexit
src/runtime/asm_amd64.s:1598
Мы не требуем от разработчиков использование сборки и запуска файлов через Bazel по ряду причин. Основные из них:
Для этого через x_defs, а потом и embed передавали в программу отдельно генерированный файл, который содержал соответствие external-имени желаемому префиксу в стеке вызовов.
Также сделали ряд преобразований для обработки путей генерируемых файлов.
Проблема стала менее острой, но результат всё равно был неудовлетворительным:
То есть, по сути, это должен был быть деобфускатор стека вызовов, но сама эта обфускация нам только мешала
У утилиты $(go env GOTOOLDIR)/compile также есть параметр -trimpath. Но этот параметр уже не булевый флаг, а перечень для замены префиксов.
В результате, мы добавили в правила go_library и go_repository дополнительные атрибуты, чтобы можно было влиять на стек вызовов:
diff --git a/deps.bzl b/deps.bzl
index ffe4981..d917282 100644
--- a/deps.bzl
+++ b/deps.bzl
@@ -5,6 +5,7 @@ def go_dependencies():
name = "com_github_masterminds_cookoo",
importpath = "github.com/Masterminds/cookoo",
sum = "h1:zwplWkfGEd4NxiL0iZHh5Jh1o25SUJTKWLfv2FkXh6o=",
+ stackpath = "go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0",
version = "v1.3.0",
)
go_repository(
@@ -12,4 +13,5 @@ def go_dependencies():
importpath = "github.com/pkg/errors",
sum = "h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=",
version = "v0.9.1",
+ stackpath = "go/pkg/mod/github.com/pkg/errors@v0.9.1",
)
Пример вывода в ветке bazel:
➜ git checkout bazel
➜ bazel run //stacktrace
INFO: Running command line: bazel-bin/stacktrace/stacktrace_/stacktrace
Hello World
main.HelloWorld
stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:131
main.main
stacktrace/main.go:27
runtime.main
GOROOT/src/runtime/proc.go:250
runtime.goexit
src/runtime/asm_amd64.s:1598
ПРИМЕЧАНИЕ: Патч на Gazelle почему-то сам не подхватывается. Если во время запуска примера произойдёт ошибка вида flag provided but not defined: -stack_path_prefix, то для её исправления нужно пересобрать саму Gazelle. В данном случае проще всего сбросить кэш Bazel: bazel clean --expunge && bazel shutdown.
С некоторыми ошибками мы пишем в лог стек вызовов. Используемая нами IDE (Idea, GoLand) позволяет по скопированному стеку вызовов получить комфортную навигацию по файлам (Analyze external stack traces). К сожалению, эта возможность хорошо работает только в том случае, если бинарый файл собран на том же хосте, на котором запущена IDE.
Этот пост посвящён тому, как мы пытались подружить формат стека вызовов и IDE.
А какие вообще варианты отображения стека предоставляет go build?
В go build для влияния на формат вывода стека есть две ручки:- флаг -trimpath – приводит отображение стека вызовов к одинаковому виду, вне зависимости от локального расположения файлов;
- переменная окружения GOROOT_FINAL – позволяет заменить префикс до системных библиотек в стеке при выключенном флаге -trimpath.
Программа для сравнения отображения стека
Рассматривать отображение стека будем на примере небольшой программы.Исходный код можно скачать по адресу: https://github.com/bozaro/go-stack-trace
Собственно, программа (stacktrace/main.go):
package main
import (
"fmt"
"github.com/Masterminds/cookoo"
"github.com/pkg/errors"
)
func main() {
// Build a new Cookoo app.
registry, router, context := cookoo.Cookoo()
// Fill the registry.
registry.AddRoutes(
cookoo.Route{
Name: "TEST",
Help: "A test route",
Does: cookoo.Tasks{
cookoo.Cmd{
Name: "hi",
Fn: HelloWorld,
},
},
},
)
// Execute the route.
router.HandleRequest("TEST", context, false)
}
func HelloWorld(cxt cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) {
fmt.Printf("%+v\n", errors.New("Hello World"))
return true, nil
}
И небольшой go.mod:
module github.com/bozaro/go-stack-trace
go 1.20
require (
github.com/Masterminds/cookoo v1.3.0
github.com/pkg/errors v0.9.1
)
Старый добрый GOPATH
Для порядка начнём со старого доброго GOPATH.Пример вывода:
➜ GO111MODULE=off GOPATH=$(pwd) go get -d github.com/bozaro/go-stack-trace/stacktrace
➜ GO111MODULE=off GOPATH=$(pwd) go run github.com/bozaro/go-stack-trace/stacktrace
Hello World
main.HelloWorld
/home/bozaro/gopath/src/github.com/bozaro/go-stack-trace/stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
/home/bozaro/gopath/src/github.com/Masterminds/cookoo/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
/home/bozaro/gopath/src/github.com/Masterminds/cookoo/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
/home/bozaro/gopath/src/github.com/Masterminds/cookoo/router.go:131
main.main
/home/bozaro/gopath/src/github.com/bozaro/go-stack-trace/stacktrace/main.go:27
runtime.main
/usr/lib/go-1.20/src/runtime/proc.go:250
runtime.goexit
/usr/lib/go-1.20/src/runtime/asm_amd64.s:1598
Здесь всё просто: мы видим полные пути до каждого файла.
При этом все пути расположены либо в src директории GoLang, либо в каталоге GOPATH.
К сожалению, такой стек будет указывать на существующие файлы только в том случае, когда исполняемый файл собран в окружении с тем же расположением каталогов. В нашем случае, когда у части разработчиков MacOS, а сборка для боевого окружения осуществляется под Linux, это требование невыполнимо.
К счастью, есть флаг -trimpath, который отрезает вариативную часть от стека вызовов:
➜ GO111MODULE=off GOPATH=$(pwd) go run -trimpath github.com/bozaro/go-stack-trace/stacktrace
Hello World
main.HelloWorld
github.com/bozaro/go-stack-trace/stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
github.com/Masterminds/cookoo/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
github.com/Masterminds/cookoo/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
github.com/Masterminds/cookoo/router.go:131
main.main
github.com/bozaro/go-stack-trace/stacktrace/main.go:27
runtime.main
runtime/proc.go:250
runtime.goexit
runtime/asm_amd64.s:1598
В этом случае все пути будут либо относительно GOPATH, либо относительно src в директории GoLang.
Получился вполне переносимый вид стека вызовов.
Go Modules
При использовании Go Modules поведение флага -trimpath разительно меняется.Сравним вывод стека вызовов без него:
➜ git clone https://github.com/bozaro/go-stack-trace.git .
➜ go run ./stacktrace
Hello World
main.HelloWorld
/home/bozaro/github/go-stack-trace/stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
/home/bozaro/go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
/home/bozaro/go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
/home/bozaro/go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:131
main.main
/home/bozaro/github/go-stack-trace/stacktrace/main.go:27
runtime.main
/usr/lib/go-1.20/src/runtime/proc.go:250
runtime.goexit
/usr/lib/go-1.20/src/runtime/asm_amd64.s:1598
И аналогичный вывод с -trimpath:
➜ go run -trimpath ./stacktrace
Hello World
main.HelloWorld
github.com/bozaro/go-stack-trace/stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
github.com/Masterminds/cookoo@v1.3.0/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
github.com/Masterminds/cookoo@v1.3.0/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
github.com/Masterminds/cookoo@v1.3.0/router.go:131
main.main
github.com/bozaro/go-stack-trace/stacktrace/main.go:27
runtime.main
runtime/proc.go:250
runtime.goexit
runtime/asm_amd64.s:1598
Без -trimpath мы по прежнему видим полные пути до каждого файла. При этом у нас явно прослеживаются три источника с исходными файлами:
- рабочий каталог с репозиторием (в данном примере: $HOME/github/go-stack-trace);
- системные библиотеки GoLang из $GOROOT/src (в данном примере: /usr/lib/go-1.20/src);
- сторонние библиотеки из $GOMODCACHE (в данном примере: $HOME/go/pkg/mod);
- файлы из текущего модуля в рабочем каталоге получают имена с именем модуля из go.mod в качестве префикса (в данном примере: $HOME/github/go-stack-trace → github.com/bozaro/go-stack-trace);
- системные библиотеки GoLang из $GOROOT/src получают имена файлов без префикса;
- сторонние библиотеки в качестве префикса получают имя модуля с версией (в данном примере: /home/bozaro/go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0 → github.com/Masterminds/cookoo@v1.3.0 – особенно обращаю внимание на то, что слово Masterminds в пути к файлу и имени модуля пишется по-разному).
Какой stack trace удобен для IDE?
Внезапно оказывается, что, если открыть проект из репозитория в Idea/GoLand и попробовать проанализировать любой из вышеприведённых стеков вызовов, то навигации по исходным файлам не будет:- варианты стека вызовов для GOPATH не подходят, так как этот мини-проект использует Go Modules и у него другое размещение файлов;
- вариант для Go Modules без -trimpath не подойдёт, так как, вероятнее всего, ваш домашний каталог будет отличатся от /home/bozaro;
- вариант для Go Modules с -trimpath не подойдёт, так как в IDE он не поддержан (https://youtrack.jetbrains.com/issue/GO-13827), а из всех путей, которые видны в стеке, суффиксами существующих файлов окажутся только файлы из Go SDK.
В итоге удовлетворительный формат переносимого стека вызовов получил такой вид:
main.HelloWorld
stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:131
main.main
stacktrace/main.go:27
runtime.main
GOROOT/src/runtime/proc.go:250
runtime.goexit
GOROOT/src/runtime/asm_amd64.s:1598
То есть:
- пути до файлов проекта отображаются относительно корня проекта;
- в качестве путей до сторонних зависимостей используется путь до модуля относительно $GOMODCACHE, но с префиксом go/pkg/mod (IDE найдет этот путь, когда проект лежит в домашнем каталоге, и переменные окружения GOPATH и GOMODCACHE имеют значение «по умолчанию»);
- в качестве путей до файлов из Go SDK просто берём слово GOROOT. Нам так и не удалось придумать путь, чтобы IDE находило подобные файлы без плясок.
Как получить стек вызовов в нужном формате?
Я вижу следующие пути, как можно получить стек вызовов в нужном формате:- повлиять на сборку, чтобы в отладочной информации были нужные пути файлов;
- перед выводом преобразовывать стек вызовов в нужный формат;
- сделать внешнюю утилиту, которая преобразовывает стек вызовов в нужный формат.
Повлиять на сборку
Мы не можем повлиять на сборку в случае с Go Build, чтобы сразу получить нужный формат стека вызовов.Преобразование стека перед выводом
В нашем случае мы повсеместно используем библиотеку github.com/joomcode/errorx, а в ней есть метод для преобразования в нужный формат стека вызовов перед выводом: https://pkg.go.dev/github.com/joomcode/errorx#InitializeStackTraceTransformerПреобразование пути из вида без -trimpath при этом выглядит тривиально.
Но у этого метода есть ряд недостатков:
- если стек вызовов прошел мимо этого фильтра, то он останется в исходом формате;
- некоторые места, например pprof, гарантированно передаются в исходном формате.
Внешняя утилита
Использование внешней утилиты сильно усложняет общий сценарий разбора стека вызовов.В нашем случае в большинстве случаев стек брался из логов и там он был уже в удобоваримом виде, так что мы этот вариант серьезно не рассматривали.
Второй раунд после перехода на сборку через Bazel
В целом, преобразование стека перед выводом в лог нас устраивало вплоть до перехода на сборку через Bazel. Но сборка через Bazel вывела проблему на новый уровень.Формат стека вызовов после сборки bazel
➜ bazel run //stacktrace...
INFO: Running command line: bazel-bin/stacktrace/stacktrace_/stacktrace
Hello World
main.HelloWorld
stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
external/com_github_masterminds_cookoo/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
external/com_github_masterminds_cookoo/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
external/com_github_masterminds_cookoo/router.go:131
main.main
stacktrace/main.go:27
runtime.main
GOROOT/src/runtime/proc.go:250
runtime.goexit
src/runtime/asm_amd64.s:1598
Мы не требуем от разработчиков использование сборки и запуска файлов через Bazel по ряду причин. Основные из них:
- мы генерируем BUILD-файлы своей утилитой и не хотим требовать перегенерацию файлов на каждый чих (она быстрая, но не мгновенная);
- синхронизация IDE и BUILD-файлов довольно медленная.
- сторонние библиотеки начинают ссылаться на external-каталог, который IDE не видит;
- нельзя тривиальным образом получить путь до модуля в GOMODCACHE – потеряна информация о версии модуля;
- генерируемые файлы могут получать совершенно неожиданный префикс вида bazel-out/k8-fastbuild-ST-2df1151a1acb/....
Преобразование стека перед выводом
Первоначально пытались собрать набор правил, которые позволяют сформировать из имеющегося стека вызовов нечто приемлемое.Для этого через x_defs, а потом и embed передавали в программу отдельно генерированный файл, который содержал соответствие external-имени желаемому префиксу в стеке вызовов.
Также сделали ряд преобразований для обработки путей генерируемых файлов.
Проблема стала менее острой, но результат всё равно был неудовлетворительным:
- в pprof оставался полный кошмар;
- часть путей преобразовывались неправильно;
- вся конструкция в целом была довольно сложной и хрупкой.
Внешняя утилита
Идти по этому пути не хотелось: помимо всей сложности и хрупкости при преобразовании стека перед выводом, добавлялась еще проблема подкладывания этой утилите информации, которую мы зашивали в исполняемый файл, а именно соответствие external-имени желаемому префиксу в стеке вызовов.То есть, по сути, это должен был быть деобфускатор стека вызовов, но сама эта обфускация нам только мешала
Повлиять на сборку, чтобы были нужные пути файлов
В случае использования Bazel сборка идёт на более низком уровне, чем Go Build. Появилась надежда поправить сборку, чтобы сразу иметь удобные пути до файлов.У утилиты $(go env GOTOOLDIR)/compile также есть параметр -trimpath. Но этот параметр уже не булевый флаг, а перечень для замены префиксов.
В результате, мы добавили в правила go_library и go_repository дополнительные атрибуты, чтобы можно было влиять на стек вызовов:
- https://github.com/bazelbuild/rules_go/pull/3307
- https://github.com/bazelbuild/bazel-gazelle/pull/1379
diff --git a/deps.bzl b/deps.bzl
index ffe4981..d917282 100644
--- a/deps.bzl
+++ b/deps.bzl
@@ -5,6 +5,7 @@ def go_dependencies():
name = "com_github_masterminds_cookoo",
importpath = "github.com/Masterminds/cookoo",
sum = "h1:zwplWkfGEd4NxiL0iZHh5Jh1o25SUJTKWLfv2FkXh6o=",
+ stackpath = "go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0",
version = "v1.3.0",
)
go_repository(
@@ -12,4 +13,5 @@ def go_dependencies():
importpath = "github.com/pkg/errors",
sum = "h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=",
version = "v0.9.1",
+ stackpath = "go/pkg/mod/github.com/pkg/errors@v0.9.1",
)
Пример вывода в ветке bazel:
➜ git checkout bazel
➜ bazel run //stacktrace
INFO: Running command line: bazel-bin/stacktrace/stacktrace_/stacktrace
Hello World
main.HelloWorld
stacktrace/main.go:31
github.com/Masterminds/cookoo.(*Router).doCommand
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:209
github.com/Masterminds/cookoo.(*Router).runRoute
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:164
github.com/Masterminds/cookoo.(*Router).HandleRequest
go/pkg/mod/github.com/!masterminds/cookoo@v1.3.0/router.go:131
main.main
stacktrace/main.go:27
runtime.main
GOROOT/src/runtime/proc.go:250
runtime.goexit
src/runtime/asm_amd64.s:1598
ПРИМЕЧАНИЕ: Патч на Gazelle почему-то сам не подхватывается. Если во время запуска примера произойдёт ошибка вида flag provided but not defined: -stack_path_prefix, то для её исправления нужно пересобрать саму Gazelle. В данном случае проще всего сбросить кэш Bazel: bazel clean --expunge && bazel shutdown.
Битва за удобный для IDE stack trace в Go (с Bazel и без)
Разработка программного обеспечения связана не только с написанием кода, но и с его отладкой. И отладка должна быть по возможности комфортной. С некоторыми ошибками мы пишем в лог стек вызовов....
habr.com