Комментарии 6
От статьи ожидал увидеть несколько иного, а не только предложения "структурируйте вывод журнала":
Необычных ситуации возникновения и последующей обработки ошибок
Чем руководствоваться при подготовке статических значений ошибок - писать заранее константы с текстом или хардкодом в месте создания ошибки? Как выделить атомарно-полезный текст вывода ошибки?
Насколько объемным должен быть журнал - вывод ошибки от библиотеки и трассировка должны быть по умолчанию, но что можно, и нужно ли, указывать дополнительно?
Распределение по уровням журнала - допустим библиотека журнала предоставляет несколько уровней (о ней ниже) записи, для удовлетворения всех нужд - Debug, Error, Warning, Info, Panic, а приложение - много-модульный web-сервис, в котором может происходить много операций одновременно с разными частями. Падения и грубые ошибки попадают в категории Err и Panic. А как распределить информационные ошибки - нужно ли регистрировать каждый чих, для отслеживания движения пользователя по системе? Ошибка авторизации в аккаунте - это Warning или Info?
Для журнала использую библиотеку https://github.com/uber-go/zap - она базируется на создании структурированного вывода ошибки, заранее объявляя поле пользовательского сообщения, поля для ошибки, при необходимости - автоматически добавляет поле с трассировкой. Немаловажным является, что и расширений куча, за счет того, что можно добавить hook-и при возникновении некого события и выполнить некое дополнительное действие. Таким образом у меня выполняется управление файлами журнала (аля logrotate) и уведомление на почту при падениях приложения.
Таким образом у меня выполняется управление файлами журнала (аля logrotate) и уведомление на почту при падениях приложения.
Т.е. у вас в конфигурации есть информация о названии лог файлов и пути до них, как по мне это только усложняет конфиг и создаёт доп места которые могут поломаться или забыться, кроме того поменять название или путь до файлов логов не получиться без рестарта приложения. Я придерживаюсь правила что, приложение должно стримить в стандартный вывод откуда этот поток может забирать управляющий процесс который ваше приложение запускает, типа супервизора или пода если это всё в кубере и перенаправлять в файл или логсташ/кафка/етц. То же самое и про ротацию. Приложению ни к чему знать о ротации логов ведь это не влияет на его работу, а только создаёт доп логику.
А трассировку вы как формируете?
понимаю конечно о чем вы, но
как по мне: ENOTFOUND, EINTERNAL... выглядит вырвиглазно.
Для создания ошибки мы решили не делать отдельный конструктор, потому как структура не такая объемная. Для того чтобы разработчики не ошиблись при создании ошибки (например забыли
&
или не создали ошибку безErr
иMessage
), мы используем собственный линтер дляgolangci-lint
Так если есть вещи которые важны при конструировании (обязательное поле Err и Message), может все таки есть смысл завести конструктор? Зачем валидировать линтером то, что можно повалидировать компилятором?
Всё что вы описали - слабо аргументировано вызывает много вопросов. Например, вы пишете
Преимуществом собственного типа ошибки стала легкость, с которой мы могли писать тесты, зависящие от ошибок, а также писать чувствительный к ошибкам код вне тестов.
Но в Go ошибки обычно передаются через fmt.Errorf("%w .....") и наследование публичного объекта-ошибки. И как бы этого достаточно и для тестов зависящих от ошибок, и для собственно обработки ошибок, и для преобразовывания ошибки в HTTP-статус, и даже для расчёта сообщения для пользователя. В вашем примере можно было было бы просто обернуть ошибку и прокинуть на верх, либо вообще ни как её не обрабатывать. И затем на самом верху стека, в хендлере, сделать проверку if errors.Is(err, pgx.ErrNoRows) {
Поэтому Ваш собственный тип ошибки выглядит как не нужное усложнение. По крайней мере на таком уровне аргументации.
Далее, вы пишете про graylog и говорите что прикрепляете к ошибке стек трейс чтобы удовлетворить greylog-у. Но greylog вовсе не самое распространённо и далеко не самое лучшее решение, есть другие, и большинство из них вообще не предполагает наличия стек-трейса для сообщения в журнал. Для стектрейсов есть sentry, которая позволяет программисту вообще не иметь дело со стек трейсами и ничего про них не знать.
Как обрабатывать ошибки в Golang – рассказываем на собственном примере