Структурирование исключений. Что вы указываете в качестве exeption code?

К своему стыду не так давно понял, что работа с exception'ами очень ёмкая и довольно сложная тема, которая выходит далеко за пределы простого try .. catch, и которая при правильном подходе позволяет значительно упростить написание кода, тестов, логирования и устранение ошибок.

Я переопределил классы всех базовых и SPL исключений в PHP чтобы иметь возможность отделять ошибки своего приложения, от ошибок сторонних библиотек. Создал классы своих, часто используемых общих исключений вида NegativeValueException, OutOfEnumException и подобных, которые в коде используются много где. Создал классы важных, специфичных исключений, типа InvalidProjectNameException которые в коде используется только в одном месте. Т.е. со структурой вроде как порядок, и я могу легко отличать одну ошибку от другой.

Но остался 1 вопрос: что писать в exception code когда я выбрасываю исключение? Мне не удалось найти каких-либо best practices по этому вопросу. Я знаю несколько возможных сценариев, но мне кажется что все они костыльны

1. Коды состояния http

В ряде случаев отдавать коды 403, 404 и т.д. вполне уместно. Но этот метод актуален скорее для работы с чем-то "внешним". Т.е. если мы говорим о бэкенде веб-приложения, то уместно будет отдавать скажем ошибку 500 наружу для фронтенда в случае каких-то внутренних ошибок сервера, но разброс таких ошибок может быть очень велик. Контроллер может отдать ошибку 500 обрабатывая исключения, выброшенные сервисным слоем, моделью или репозиторием. Т.е. под этот внешний код может попадать множество "внутренних" исключений. Эти внтуренние исключения само-собой будут залогированы, но когда к тебе обращается клиент с жалобой типа "у меня тут ошибка 500 выскочила", то то искать причину нужно в логах, отфильтровывая ее по множеству параметров. Выводить наружу текст ошибки конкретного исключения я считаю плохой практикой

2. Вести справочник кодов.
Можно попробовать вести нечто вроде документации, каждому классу исключения принудительно задавать свой код. Или даже для каждого конкретного случая выбрасывания исключения задавать свой код. Этот код можно выбрасывать наружу, отображая его пользователю. В таком случае идентифицировать ошибку со слов клиента будет очень легко, но ведение подобного справочника превратиться в ад, в котором будет легко запутаться. Либо я просто не знаю правильных методов это делать

3. Коды нужны только для того, чтобы отличить ситуацию, в которой выбрасывается одно и то же исключение.

Например, у меня есть метод, который принимает в качестве аргументов 2 email адреса. Каждый из них валидируется, и в случае ошибки выбрасывается InvalidEmailException. Т.е. один метод класса в разных местах может выбросить одно и то же исключение. Это не удобно тестировать, и с целью упрощения тестирования при выбрасывании исключения мы можем каждому в качестве кода задать номер, и в тестах отличать исключение выброшенное от проверки 1 и 2 email'ов. Это единственное нормальное применение кодов которое я вижу в настоящий момент

4. Записывать в код исключения ID пользователя
Про это я где-то читал, по-моему даже на хабре. Но считаю это ужасной практикой. ID пользователя можно вытащить далеко не всегда и не везде (если конечно соблюдать принципы SOLID). Да и эту задачу должен решать логгер, а не коды исключений

Поделитесь пожалуйста опытом, как вы обрабатываете исключения и что указываете в кодах?

UPD: вопрос не про логирование и поиск конкретной ошибки в логах. С этим у меня успешно справляется sentry. Я спрашиваю про то, как кто использует коды в исключениях, и я лишь привел примеры того, как их можно юзать с их плюсами и минусами
  • Вопрос задан
  • 1160 просмотров
Пригласить эксперта
Ответы на вопрос 3
qonand
@qonand
Software Engineer
Как по мне создание справочника кодов бессмысленная и геморройная вещь.
Почему геморройная - Вы сами на этот вопрос ответили.
Почему бессмысленная: Вы думаете что клиенты будут обращаться в техподдержку и сразу говорить код ошибки? на практике такого не будет... клиент обратившись просто скажет "Я тут клацнул кнопку а оно перестало работать", и сотрудникам поддержки все равно придется уточнять что и как там у него случилось. Так что как-бы коды ничего не изменят. К тому же по началу справочник будет добросовестно вестись, а в процессе эксплуатации клиент/начальник скажет какому-то программисту Васе - запили как фичу на проекте по быстрому, он ее сделает но в справочник данные не внесет, ибо нафик ему нужен этот гемор. Поэтому как бы актуальность справочника скорее всего на практике пострадает.

Зачем вообще коды? Возникла ошибка - вернули 500 статус и описание проблемы, записали подробную информацию в лог и все. Обратился клиент в тех. поддержку - посмотрели что за исключение возникло, посмотрели его stack-trace в логе и все. Грамотно сформированный лог + система поиска по нем решают все проблемы с поддержкой.
Ответ написан
FanatPHP
@FanatPHP
Чебуратор тега РНР
В этом вопросе перемешаны мухи и котлеты. Вопрос, вроде бы, про некое "структурирование исключений" (то есть, ждешь про иерархию классов), а на самом деле автора интересует вопрос взаимодействия с пользователями и идентификации ошибок.

Главная неувязка - какая связь между кодом исключения и уникальным кодом ошибки, служащим для идентификации конкретного места в логах? Это совершенно разные сущности, не имеющие ничего общего. Генерацией уникальных кодов должен заниматься обработчик ошибок, ни ни к кодам исключений, ни к самим исключениями, ни тем более к их иерархии это все не имеет отношения.
Ответ написан
@xfg
Понял, вас интересует для чего нужно свойство code в исключениях. К сожалению, я так и не смог нагуглить точного описания для чего в php это свойство добавили к исключению и для каких целей php предлагает его использовать сегодня.

Но я думаю, что так сложилось исторически. В PHP 3 появились самые первые средства для работы с ООП. Не такие мощные и функциональные, какими вы их видите сегодня в PHP 5/7. Тогда механизм исключений в php не позволял добавлять множественные catch блоки. Это не имело смысла, так как в php не существовало возможности типизировать аргументы функций. Соответственно единственная возможность отличить одно исключение от другого внутри блока catch это использовать свойство code. Время прошло, добавили типизацию, множественные catch блоки, а свойство code осталось, как и многое другое в php. Использовать свойство code в настоящее время вряд ли имеет какой-либо смысл.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы