53.2. Сообщение об ошибках внутри сервера#

53.2. Сообщение об ошибках внутри сервера

53.2. Сообщение об ошибках внутри сервера #

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

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

Типичный вызов функции 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 - это целочисленное значение, которое определяет, какая форма множественного числа требуется, а остальные аргументы форматируются в соответствии с выбранным форматирующим строкой. Дополнительную информацию см. Раздел 54.2.2.

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

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

  • errdetail_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...) похож на errdetail, но с поддержкой различных форм множественного числа сообщения. Дополнительную информацию см. в Раздел 54.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, но с поддержкой различных форм множественного числа сообщения. Дополнительную информацию см. в Раздел 54.2.2.

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

  • errhint_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...) похож на errhint, но с поддержкой различных форм множественного числа сообщения. Дополнительную информацию см. в Раздел 54.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: сообщения в журнале постмастера. Это следует использовать только для отладочных сообщений с подробным выводом, где повторное включение контекста приведет к раздутию журнала.

Примечание

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

Есть старая функция elog, которая до сих пор широко используется. Вызов elog:

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

точно равнозначно:

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

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

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



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