Сообщения об ошибках на сервере
Эта страница переведена при помощи нейросети GigaChat.
Сообщения об ошибках, предупреждения и обычные сообщения, выдаваемые в коде сервера должны создаваться функцией 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(то есть, значение, которое было текущим, когда был достигнут вызовereport; измененияerrnoв вспомогательных процедурах отчетности не повлияют на него. Это было бы неверно, если бы написалиstrerror(errno) явно в списке параметровerrmsg; соответственно, не делайте этого).%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 - целочисленное значение, которое определяет, какая форма множественного числа необходима, а остальные аргументы форматируются в соответствии с выбранной строкой формата. Подробнее см. в разделе «Рекомендации по написанию сообщений»;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, но с поддержкой различных множественных форм сообщения. Подробнее смотрите в разделе Для программиста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, но с поддержкой различных множественных форм сообщения. Подробнее см. раздел «Рекомендации по написанию сообщений»;errhint(const char \*msg, ...)предоставляет дополнительное сообщение «подсказка»; это должно использоваться при предложении советов о том, как исправить проблему, а не фактических подробностей о том, что пошло не так. Строка сообщения обрабатывается так же, как и дляerrmsg;errhint_plural(const char \*fmt\_singular, const char \*fmt\_plural, unsigned long n, ...)похож наerrhint, но с поддержкой различных множественных форм сообщения. Подробнее см. раздел «Рекомендации по написанию сообщений»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; для таких сообщений эта функция предпочитается из-за простоты записи.
Советы по написанию хороших сообщений об ошибках можно найти в разделе «Руководство по стилю сообщения об ошибке»