56.2. Вывод сообщений об ошибках в коде сервера

Сообщения об ошибках, предупреждения и обычные сообщения, выдаваемые в коде сервера должны создаваться функцией ereport или родственной её предшественницей elog. Использование этой функции достаточно сложно и требует дополнительного объяснения.

У каждого сообщения есть два обязательных элемента: уровень важности (от DEBUG до PANIC) и основной текст сообщения. В дополнение к ним есть необязательные элементы, из которых часто используется код идентификатора ошибки, соответствующий определению SQLSTATE в спецификации SQL. Макрос ereport сам по себе является просто оболочкой, которая существует в основном для синтаксического удобства, чтобы выдача сообщения выглядела как вызов одной функции в коде C. Единственный параметр, который принимает непосредственно ereport, — это уровень важности. Основной текст и любые дополнительные элементы сообщения генерируются в вызове ereport в результате использования вспомогательных функций, таких как errmsg.

Типичный вызов ereport выглядит примерно так:

ereport(ERROR,
        errcode(ERRCODE_DIVISION_BY_ZERO),
        errmsg("division by zero"));

В нём задаётся уровень важности ERROR (заурядная ошибка). В вызове errcode указывается код ошибки SQLSTATE по макросу, определённому в src/include/utils/errcodes.h. Вызов errmsg даёт текст основного сообщения.

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

ereport(ERROR,
        (errcode(ERRCODE_DIVISION_BY_ZERO),
         errmsg("division by zero")));

Эти дополнительные скобки были обязательными до PostgreSQL версии 12, но сейчас они не требуются.

Более сложный пример:

ereport(ERROR,
        errcode(ERRCODE_AMBIGUOUS_FUNCTION),
        errmsg("function %s is not unique",
               func_signature_string(funcname, nargs,
                                     NIL, actual_arg_types)),
        errhint("Unable to choose a best candidate function. "
                "You might need to add explicit typecasts."));

В нём демонстрируется использование кодов форматирования для включения значений времени выполнения в текст сообщения. Также в нём добавляется дополнительное сообщение «подсказки». Вызовы вспомогательных функций можно записывать в любом порядке, но обычно сначала вызываются errcode и errmsg.

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

Для ereport предлагаются следующие вспомогательные функции:

  • errcode(sqlerrcode) задаёт код идентификатора ошибки SQLSTATE для данной ошибки. Если эта функция не вызывается, подразумевается идентификатор ошибки ERRCODE_INTERNAL_ERROR при уровне важности ERROR или выше, либо ERRCODE_WARNING при уровне важности WARNING, иначе (при уровне NOTICE или ниже) — ERRCODE_SUCCESSFUL_COMPLETION. Хотя эти значения по умолчанию довольно разумны, всегда стоит подумать, насколько они уместны, прежде чем опустить вызов errcode().

  • errmsg(const char *msg, ...) задаёт основной текст сообщения об ошибке и, возможно, значения времени выполнения, которые будут в него включаться. Эти включения записываются кодами формата в стиле sprintf. В дополнение к стандартным кодам формата, принимаемым функцией sprintf, можно использовать код формата %m, который вставит сообщение об ошибке, возвращённое строкой strerror для текущего значения errno. [16] Для %m не требуется соответствующая запись в списке параметров errmsg. Заметьте, что эта строка будет пропущена через gettext, то есть может быть локализована, до обработки кодов формата.

  • errmsg_internal(const char *msg, ...) действует как errmsg, но её строка сообщения не будет переводиться и включаться в словарь сообщений для интернационализации. Это следует использовать для случаев, которые «не происходят никогда», так что тратить силы на их перевод не стоит.

  • errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...) действует подобно errmsg, но поддерживает различные формы сообщения с множественными числами. Параметр fmt_singular задаёт строку формата на английском для единственного числа, fmt_plural — формат для множественного числа, n задаёт целое число, определяющее, какая именно форма множественного числа требуется, а остальные аргументы форматируются согласно выбранной строке формата. За дополнительными сведениями обратитесь к Подразделу 57.2.2.

  • errdetail(const char *msg, ...) задаёт дополнительное «подробное» сообщение; оно должно использоваться, когда есть дополнительная информация, которую неуместно включать в основное сообщение. Строка сообщения обрабатывается так же, как и для errmsg.

  • errdetail_internal(const char *msg, ...) действует как errdetail, но её строка сообщения не будет переводиться и включаться в словарь сообщений для интернационализации. Это следует использовать для подробных сообщений, на перевод которых не стоит тратить силы, например, когда это техническая информация, непонятная большинству пользователей.

  • errdetail_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...) действует подобно errdetail, но поддерживает различные формы сообщения с множественными числами. За дополнительными сведениями обратитесь к Подразделу 57.2.2.

  • errdetail_log(const char *msg, ...) подобна errdetail, но выводимая строка попадает только в журнал сервера, и никогда не передаётся клиенту. Если используется и errdetail (или один из её эквивалентов), и errdetail_log, тогда одна строка передаётся клиенту, а другая отправляется в журнал. Это полезно для вывода подробных сообщений, имеющих конфиденциальный характер или большой размер, так что передавать их клиенту нежелательно.

  • errdetail_log_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...) действует подобно errdetail_log, но поддерживает различные формы сообщения с множественными числами. За дополнительными сведениями обратитесь к Подразделу 57.2.2.

  • errhint(const char *msg, ...) передаёт дополнительное сообщение «подсказки»; это позволяет предложить решение проблемы, а не просто сообщить факты, связанные с ней. Строка сообщения обрабатывается так же, как и для errmsg.

  • errhint_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...) действует подобно errhint, но поддерживает различные формы сообщения с множественными числами. За дополнительными сведениями обратитесь к Подразделу 57.2.2.

  • errcontext(const char *msg, ...) обычно не вызывается непосредственно с места вызова ereport, а используется в функциях обратного вызова error_context_stack и выдаёт информацию о контексте, в котором произошла ошибка, например, о текущем положении в функции PL. Строка сообщения обрабатывается так же, как и для errmsg. В отличие от других вспомогательных функций, внутри вызова ereport её можно вызывать неоднократно; добавляемые таким образом последовательные сообщения складываются через символы перевода строк.

  • errposition(int cursorpos) задаёт положение ошибки в тексте запроса. В настоящее время это полезно только для ошибок, выявляемых на этапах лексического и синтаксического анализа запроса.

  • errtable(Relation rel) определяет отношение, имя и схема которого должны быть включены во вспомогательные поля сообщения об ошибке.

  • errtablecol(Relation rel, int attnum) определяет столбец, имя которого, вместе с именем таблицы и схемы, должно быть включено во вспомогательные поля сообщения об ошибке.

  • errtableconstraint(Relation rel, const char *conname) задаёт имя ограничения таблицы, которое вместе с именем таблицы и схемы должно быть включено во вспомогательные поля сообщения об ошибке. В данном контексте индекс считается ограничением, независимо от того, имеется ли для него запись в pg_constraint. Заметьте, что при этом в качестве rel нужно передавать нижележащее отношение, а не сам индекс.

  • errdatatype(Oid datatypeOid) задаёт тип данных, имя которого, вместе с именем схемы, должно включаться во вспомогательные поля сообщения об ошибке.

  • errdomainconstraint(Oid datatypeOid, const char *conname) задаёт имя ограничения домена, которое вместе с именем домена и схемы должно включаться во вспомогательные поля сообщения об ошибке.

  • errcode_for_file_access() — вспомогательная функция, выбирающая подходящий идентификатор SQLSTATE при сбое в системном вызове, в котором происходит обращение к файловой системе. Какой код ошибки генерировать, она определяет по сохранённому значению errno. Обычно это используется в сочетании с %m в основном сообщении об ошибке.

  • errcode_for_socket_access() — вспомогательная функция, выбирающая подходящий идентификатор SQLSTATE при сбое в системном вызове, в котором происходит обращение к сокетам.

  • errhidestmt(bool hide_stmt) может быть вызвана для подавления вывода поля ОПЕРАТОР: (STATEMENT:) в журнал сервера. Обычно это уместно, когда само сообщение включает текст текущего оператора.

  • errhidecontext(bool hide_ctx) может быть вызвана для подавления вывода поля КОНТЕКСТ: (CONTEXT:) в журнал сервера. Это следует использовать только для подробных отладочных сообщений, в которых одна и та же информация о контексте, выводимая в журнал, будет только чрезмерно замусоривать его.

Примечание

В вызове ereport следует использовать максимум одну из функций errtable, errtablecol, errtableconstraint, errdatatype или errdomainconstraint. Данные функции существуют для того, чтобы приложения могли извлечь имя объекта базы данных, связанного с условием ошибки, так, чтобы для этого им не требовалось разбирать текст ошибки, возможно локализованный. Эти функции должны использоваться в случае ошибок, для которых может быть желательной автоматическая обработка. Для версии PostgreSQL 9.3 этот подход распространяется полностью только на ошибки класса SQLSTATE 23 (нарушение целостности ограничения), но в будущем область его применения может быть расширена.

Существует также более старая, но тем не менее активно используемая функция elog. Вызов elog:

elog(level, "format string", ...);

полностью равнозначен вызову:

ereport(level, errmsg_internal("format string", ...));

Заметьте, что код ошибки SQLSTATE всегда определяется неявно, а строка сообщения не подлежит переводу. Таким образом, elog следует использовать только для внутренних ошибок и отладки на низком уровне. Любое сообщение, которое может представлять интерес для обычных пользователей, должно проходить через ereport. Тем не менее в системе есть достаточно много внутренних проверок для случаев, «которые не должны происходить», и в них по-прежнему широко используется elog; для таких сообщений эта функция предпочитается из-за простоты записи.

Советы по написанию хороших сообщений об ошибках можно найти в Разделе 56.3.



[16] То есть значение, которое было текущим, когда была вызвана ereport; изменения errno во вспомогательных функциях выдачи сообщений на него не повлияют. Это будет не так, если вы запишете strerror(errno) явно в списке параметров errmsg; поэтому делать так не нужно.

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy