Привіт! Сьогодні я хотів би обговорити тему, яка часто трапляється в нашій повсякденній роботі, а саме - повернення та обгортання помилок, що є критично важливим аспектом розробки надійних Go-додатків з кількох причин:
errors.Is
та errors.As
дає можливість перевіряти конкретні типи помилок на будь-якому рівні програми, незалежно від того, наскільки глибоко вони обгорнуті.<aside> 💡
Розглянемо приклад: у веб-сервісі низькорівнева помилка бази даних може бути обгорнута на рівні репозиторію, потім на рівні сервісу, і нарешті на рівні HTTP-хендлера, при цьому:
Це особливо корисно при розробці мікросервісів, де правильна обробка та логування помилок критичні для діагностики проблем у продакшені.
Стандартний пакет errors
в Go підтримує об'єднання кількох помилок на додаток до більш поширеного обгортання за допомогою %w
.
Я не часто бачив це використання на практиці; думаю, більшість людей або рефакторять код, щоб уникнути множинних помилок, повертають слайс []error
, або використовують uber-go/multierr. Давайте розглянемо це!
var (
ErrDatabaseConflict = errors.New("connection refused")
ErrCodeServiceExample = errors.New("cannot get user profile")
ErrCodeHandlerExample = errors.New("internal error")
)
err1 := fmt.Errorf("G-switch failed: %w %w %w", ErrDatabaseConflict, ErrCodeServiceExample, ErrCodeHandlerExample)
// 2009/11/10 23:00:00 G-switch failed: connection refused cannot get user profile internal error
log.Fatal(err1)
Давайте розберемо цей код детальніше:
ErrDatabaseConflict
, ErrCodeServiceExample
, ErrCodeHandlerExample
) за допомогою errors.New()
err1
за допомогою fmt.Errorf()
, яка об'єднує всі три помилки використовуючи дієслово %w
%w
в одному fmt.Errorf()
дозволяє створити ланцюжок обгорнутих помилокlog.Fatal()
ми бачимо всі три помилки в одному повідомленні, розділені пробіламиЦе корисно, коли потрібно зберегти інформацію про декілька помилок, що сталися одночасно, і передати їх вище по стеку викликів як єдину помилку.
Другий спосіб використовує функцію errors.Join
, представлену в Go 1.20. Функція приймає змінну кількість аргументів типу error
, відкидає всі nil
значення та об'єднує решту наданих помилок. Повідомлення форматується шляхом об'єднання рядків, отриманих викликом методу Error()
кожного аргументу, розділених символом нового рядка.
err2 := errors.Join(
ErrDatabaseConflict,
ErrServiceCodeExample,
ErrHandlerCodeExample,
)
// 2009/11/10 23:00:00 connection refused
// cannot get user profile
// internal error
log.Fatal(err2)
На даний момент ми розглянули два способи, якими Go підтримує обгортання помилок: пряме обгортання та об'єднані помилки.