Pull to refresh

Повышаем информативность ошибок в Go – github.com/ztrue/tracerr

Reading time 3 min
Views 5.7K
После многолетнего опыта работы с php и js, я привык иметь в ошибках стектрейс и смотреть на место, где произошла ошибка прямо из эррор-репорта. Пересев на Go пару лет назад, я был несколько удивлен, что в Go другие правила и нужно угадывать стектрейс по какой-нибудь строке типа `invalid character`. А если она произошла на проде и не известно, как ее воспроизвести, то это превращалось в целый аттракцион.

Поскольку я уверен, что не один от этого страдал, то сделал пакет, который умеет так:

golang error output

GitHub

Все, что он делает, это:

  1. Добавляет к ошибкам стектрейс.
  2. Отображает стектрейс и фрагменты исходников, где эта ошибка произошла (при наличии исходников, конечно).

Добавление стектрейса


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

// создать новую
err := tracerr.New("some error")

// можно методом Errorf, который работает так же, как и fmt.Errorf
err := tracerr.Errorf("some error %d", num)

// или обернуть существующую ошибку, добавив ей стектрейс
err = tracerr.Wrap(err)

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

Код может выглядеть как-то так:

func decodeFile(path string, data interface{}) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return tracerr.Wrap(err)
	}
	err = json.Unmarshal(b, data)
	// если err = nil, то останется nil
	return tracerr.Wrap(err)
}

Отображение стектрейса


После того, как ошибка через 100500 if err != nil { return err } вернется на Родину в main() (или там, где она обрабатывается), скорее всего Вы захотите ее отобразить или залогировать.
Для это есть несколько опций: все работают, как Print (выводит текст) или Sprint (возвращает текст):

1) Отобразить текст ошибки и стектрейс:

tracerr.Print(err)

2) Отобразить текст ошибки, стектрейс и фрагмент исходников (6 строк по дефолту):

tracerr.PrintSource(err)

3) То же, но в цвете, обычно более информативно:

tracerr.PrintSourceColor(err)

4) Можно передать параметром, сколько строк кода отобразить:

tracerr.PrintSource(err, 9)
tracerr.PrintSourceColor(err, 9)

5) Или передать 2 опциональных параметра, сколько до и сколько после строки с ошибкой отобразить:

tracerr.PrintSource(err, 5, 2)
tracerr.PrintSourceColor(err, 5, 2)

Вопросы


Я уже получил некоторый фидбек, поэтому позволю себе ответить заранее на некоторые вопросы, которые уже задавали.

В: Это подходит только для дебага? Есть дебаггер.
О: Это подходит не только для дебага, можно логировать ошибки с информацией о стектрейсе, и даже с фрагментами исходников, на проде, как по моему опыту, это существенно упростит потом эти ошибки разбирать.

В: Есть супер пакет pkg/errors, почему не использовать его?
О: Есть, я сам его вполне использовал и рад, но он не подошел мне по этим причинам:
1) Там нет простого способа отобразить стектрейс сразу с исходниками.
2) При повторном оборачивании ошибки (например, на уровень выше), стектрейс затирается менее информативным.
3) При каждом оборачивании обязательно передавать дополнительный текст ошибки, что мне кажется некоторым оверхедом при написании/чтении кода.

В: В Go ошибки это не исключения и так делать вообще нельзя.
О: Согласен, в Go ошибки это не исключения. Если Вам удобнее обрабатывать тысячи if err != nil { return err } каким-то другим способом — это Ваш выбор, конечно. Вы можете оборачивать только те ошибки, которые обрабатываете как исключения.

В: Стектрейс добавляет оверхед на производительность.
О: Да, добавляет, но это актуально только для мест, где ошибки создаются в огромных количествах, просто не добавляйте туда стектрейс, если это критично (уверен, что в большинстве случаев этот оверхед ничтожен).

В общем, надеюсь, этот пакет несколько облегчит Вашу гоферскую жизнь, буду рад любому фидбеку, спасибо.
Tags:
Hubs:
+42
Comments 31
Comments Comments 31

Articles