Skip to content

Latest commit

 

History

History
101 lines (68 loc) · 4.39 KB

README-ru.md

File metadata and controls

101 lines (68 loc) · 4.39 KB

go-check-err-chains

Линтер проверяет что текст ошибок содержит префикс указывающий на пакет/функцию/метод в котором произошла ошибка.

Проверка проводится только для экспортируемых функций.

Пример:

package pkg

func Get(key string) error {
    id, err := strconv.Atoi(key)
    if err != nil {
        return errors.New("pkg.Get: %w; key=%s", err, key) // good
    }
    if empty {
        return sql.ErrNoRows // bad
    }
    return nil
}

Зачем

Этот линтер – попытка навести порядок влогах.

По логам часто не понятно где произошла проблема.

У этой проблемы уже есть есть пара решений, но все имеют существенные недостатки:

  1. Использовать логер снабжающий записи указанием на файл и строку где лог был написан.

    Файл и номер строки логер указать может, но это будет место где вызвали log.Error, а не место в котором произошла ошибка.

    Это значит что ошибки нужно логировать прямо там где они произошли. В итоге логи начинают писать повсюду и с большой избыточностью.

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

  2. Использовать для ошибок сторонние библиотеки типа pkg/errors

    Есть множество библиотек которые сохраянют стек вызовов при создания ошибки.

    Это не плохой подход, но есть ряд минусов:

    • повсюду нужно использовать одну определенную библиотеку для ошибок
    • плохо сочетается с нативным врапингом через %w

На данный момент оптимальным компромисом кажется идти идиоматическим путем принятым в стандартной библиотеке:

Примеры ошибок из стандартной библиотеки:

  • strconv.Atoi: parsing "x": invalid syntax
  • plugin.Open("file.so"): realpath failed
  • bytes.Buffer: too large
  • open file.go: no such file or directory
  • strings.Builder.Grow: negative count

Видно что строгой схемы нет, но паттерн прослеживается: вначале имя пакета/метода/функции, затем двоеточие и далее подробности.

package pkg

func Get(key string) error {
    id, err := strconv.Atoi(key)
    if err != nil {
        return errors.New("pkg.Get: %w; key=%s", err, key)
    }
    // ...
}

pkg.Get("abc") дает ошибку:

pkg.Get: strconv.Atoi: parsing "abc": invalid syntax; key=abc

У такого подхода ряд плюсов:

  • Хорошо стыкуется с ошибками стандартной библиотеки

  • Дружит с врапингом

  • Писать текст ошибки проще:

    failed to get package cause key abc is not valid ... vs pkg.Get: strconv.Atoi: parsing "abc": invalid syntax.

    Второй вариант инженеру проще и читать и писать.

  • Логи удобно грепать

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

Именнно эту проблему и решает этот линтер.