Руководство для разработчиков

Ошибки

Введение

В библиотеках COREmanager активно используется механизм исключений (exception). Архитектура системы подразумевает высокую вложенность вызовов, а исключения — простой способ доставить информацию об ошибке. При написании собственных компонентов стоит придерживаться следующих правил:

  1. Никогда не подавляйте исключения, возникшие при внутренних вызовах isp_api::InternalCall. Это может привести систему в нестабильное состояние. Так как часть операций по обработке запроса может быть выполнена не полностью. Если вы обнаружили ошибку, но все же хотите попробовать выполнить запрос до конца — лучше воспользоваться механизмом повторного выполнения запроса isp_api::RestartRequest.
  2. Никогда не перехватывайте исключения без определения типа catch (...) или выкидывайте это же исключение наружу throw /* без параметров /;
  3. Всегда передавайте язык или сессию при внутренних или внешних запросах. Это позволит вам получить сообщение об ошибке в локализованном виде.

Типы исключений

Исключения в библиотеках COREmanager можно разделить по алгоритму их обработки на следующие типы:

Исключения mgr_err::Error

Почти все исключения в библиотеках COREmanager — исключения типа mgr_err::Error . Этот тип исключений унаследован от std::exception. Так что, если вы хотите перехватить все исключения, лучше перехватывать mgr_err::Error или std::exception. В COREmanager мы не используем типизацию исключений. Во-первых, исключения могут возникать при удаленных запросах, а их сериализация и десериализация в этом случае не всегда возможна (локальная версия COREmanager, получившая исключение от удаленного сервиса, может просто не содержать требуемого класса исключений). Во вторых, как показывает практика, довольно редко возникает необходимость в типизации исключений (99% всех catch — это catch(const mgr_err::Error &) или catch(const std::exception &)).

Этот класс содержит состояние стека на момент возникновения исключения и xml документ, описывающий тип исключения.

Пример XML:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
  <error type="xml" object="xpath" report="yes" lang="ru">
    <param name="object" type="msg" msg="Ошибка в XPath '__value__'">xpath</param>
    <param name="value">bad XPath</param>
    <stack>
      <action level="30" user="root">xmlerror</action>
    </stack>
    <group>Возникла ошибка при работе с XML документом. __object__</group>
    <msg>Возникла ошибка при работе с XML документом. Ошибка в XPath 'bad XPath'</msg>
  </error>
</doc>

Атрибуты и дочерние узлы узла error:

атрибут external Значение "yes" говорит о том, что исключение было получено от удаленного сервиса. Такие исключения не проходят специальной обработки.

атрибут report Значение "yes" говорит о том, что необходимо сформировать отчет для разработчиков. Такие исключения не должны доходить до пользователя, но, если они все же дойдут, пользователю предложат сообщить о таком случае разработчикам продукта.

атрибут help Если указан, к сообщению об ошибке будет добавлена ссылка на документацию. Имя статьи будет взято из значения атрибута.

атрибут type Тип исключения — произвольная строка. Используется при локализации.

атрибут lang Язык, который был использован для локализации.

атрибут object Значение параметра object

атрибут value Значение параметра value

узел param Параметр исключения. Он, в свою очередь, имеет атрибуты @name — имя параметра, @type — тип параметра (для параметров требующих локализации должен иметь значение "msg"), @msg — локализованное значение параметра. Значение параметра берется из содержимого узла.

узел stack Стек вызовов isp_api::InternalCall на момент возникновения исключения. Каждый дочений узел action содержит атрибуты @level и @user — уровень доступа и имя пользователя с правами которого выполнялось действие. Имя функции записано в содержимом узла action.

узлы detail, group и default локализованное описание исключения.

узел msg скомпонованное локализованное описание исключения.

Локализация

Классы исключений

Несмотря на то, что мы не используем типизацию исключений, не все исключения обрабатываются одинаково. В библиотеках COREmanager деление исключений основывается на атрибутах узла error. По типу обработки можно выделить следующие группы:

Ошибки функций (локальные ошибки). Это ошибки возникающие из-за неверной настройки системы или неверно введенных параметрах. Такие ошибки должны быть максимально понятны конечному пользователю.

Специальные ошибки. Очень похожи на ошибки из предыдущей группы. Но их обработка отличается. Примером такой ошибки может быть ошибка типа notconfigured. В случае, если запрос был через API (out=xml), эта ошибка вернется пользователю так же, как и ошибки из первой группы. Но в случае, если эта ошибка возникнет при обращении к панели через web интерфейс, пользователь увидит форму с предложением заполнить недостающие данные. Еще к этой группе можно отнести ошибки авторизации, в случае возникновения которых в web интерфейсе, пользователя перенаправляют на форму регистрации.

Ошибки библиотек. Это могут быть как ошибки, вызванные нехваткой ресурсов, так и алгоритмические ошибки, когда программист некорректно использует какие-либо библиотечные функции. Таких ошибок быть не должно. А их возникновение приводит к формированию отчета, который будет предложено отправить разработчику. (атрибут report).

Ошибки от удаленных сервисов. Это ошибки, пришедшие от удаленных сервисов, работающих на базе COREmanager. Могут иметь xml, аналогичный ошибкам из любой другой группы. Но никакой специальной обработки такие ошибки не проходят. (атрибут external)

Служебные исключения

Помимо mgr_err::Error COREmanager использует ряд исключений, не унаследованных ни от mgr_err::Error, ни от std::exception. Такие ошибки не должны перехватываться пользователем никогда.

Assert критическая ошибка. Это тот класс ошибок, которые не должны возникать никогда. Но если она возникла, дальнейшее выполнение приложения бессмысленно или даже опасно. Данный тип исключений порождается макросом ASSERT . В отличии от стандартного Assert наш не завершает выполнение приложения а приводит к завершению выполнения текущего потока. При этом COREmanager предпримет попытку откатить все изменения сделанные в процессе выполнения запроса.

Restart требование повторить выполнения запроса заново. Этот тип исключений используется функцией isp_api::RestartRequest

Skip сообщает о том, что в дальнейшей обработке запроса нет необходимости. Пользователь получит ответ в том виде, в котором он был на момент возникновения этого исключения

Локализация ошибок

Локализация ошибки может зависеть от уровня доступа пользователя, который ее увидит. Для этого вначале идет поиск сообщения с суффиксом __XX, где XX — число, минимальный уровень доступа, который должен иметь пользователь, чтобы его увидеть. Если подходящего сообщения не найдено, используется сообщение без суффикса.

Например, пользователь с уровнем доступа 16 получает ошибку, для локализации которой может быть использовано сообщение с именем*msg_error_some_error*. Вначале будут последовательно проверены сообщения с именами от*msg_error_some_error16*до*msg_error_some_error1*. Если ни одно из них не будет найдено, будет использовано сообщение с именем*msg_error_some_error*.

В случае, если пользователь работает под аккаунтом другого пользователя (функция su), уровень доступа для формирования сообщения об ошибке будет взят у первого пользователя в цепочке (у того, которым он зашел в панель). Таким образом, администратор, работающий с правами пользователя и пользователь, вошедший в панель управления самостоятельно, могут видеть различные сообщения для одних и тех же ошибок

При локализации ошибки COREmanager пытается найти соответствующие сообщения, перебирая секции сообщений в следующем порядке:

  1. msgerror_<тип ошибки>
  2. Все секции сообщений функций, начиная с последней (функции, выполнение которой вызвало ошибку). Таких секций может быть несколько, если ошибка возникла при внутреннем вызове isp_api::InternalCall
  3. msgerror

В этих секциях COREmanager будет искать сообщения, соответствующие значениям параметров с типом msg. И сообщения с именами:

  1. msg_error_<тип>_<объект>
  2. msg_error_<тип>
  3. msg_error_unknown

Для формирования сообщения, которое увидит пользователь, будет взято наиболее подходящее (msg_error_<тип>_<объект> имеет наивысший приоритет) Затем, в полученной строке будут заменены все подстроки __<имя>__ соответствующие параметрам с типом msg на их локализованное значение. А затем, все подстроки __<имя>__ , соответствующие остальным параметрам.

Например, при работе со встроенными механизмами баз данных (mgr_db::Cache) в случае, если не удалось добавить запись в базу данных по причине того, что такая запись уже существует, возникнет ошибка с типом "exists", объект будет содержать имя таблицы базы данных, значение — значения дублирующихся полей, разделенные запятой, а также дополнительный параметр "cols", содержащий имена дублирующихся полей (разделенные запятой). Чтобы локализовать такую ошибку, создайте сообщение типа:

msg_error_exists_<имя_таблицы>

А текст сообщения может иметь следующий вид: __cols__ со значением __value__ уже существует.