2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4

55.3. 错误消息风格指南 #

本风格指南旨在为 PostgreSQL 生成的所有消息提供一种一致的、对用户友好的风格。

内容归属 #

主消息应简短、客观,并避免提及实现细节,如特定的函数名。“简短”意味着“在正常情况下应能显示在一行内”。如果需要,可以使用详细消息来保持主消息的简短,或者当需要提及实现细节(例如失败的具体系统调用)时。主消息和详细消息都应客观。提示消息可用于提供解决问题的建议,尤其是当建议不总是适用时。

例如,而不是

IpcMemoryCreate: shmget(key=%d, size=%u, 0%o) failed: %m
(plus a long addendum that is basically a hint)

写入

Primary:    could not create shared memory segment: %m
Detail:     Failed syscall was shmget(key=%d, size=%u, 0%o).
Hint:       The addendum, written as a complete sentence.

理由:保持主消息简短有助于使其切题,并允许客户端应用程序根据假设一行足以显示错误消息来布局屏幕空间。详细消息和提示消息可以被隐藏在冗长模式下,或者可能是一个弹出式错误详细信息窗口。此外,详细信息和提示通常会从服务器日志中隐藏以节省空间。最好避免提及实现细节,因为用户不需要了解这些细节。

格式 #

不要在消息文本中加入任何关于格式的具体假设。假定客户端和服务器日志会根据自己的需求进行换行。在长消息中,可以使用换行符(\n)来指示建议的段落分隔。不要在消息末尾添加换行符。不要使用制表符或其他格式化字符。(在错误上下文显示中,会自动添加换行符来分隔上下文级别,如函数调用。)

理由:消息不一定显示在终端类型的显示器上。在 GUI 显示或浏览器中,这些格式化指令充其量会被忽略。

引号 #

英文文本在需要引用时应使用双引号。其他语言的文本应始终使用一种与出版习俗和程序计算机输出一致的引号。

理由:选择双引号而不是单引号在某种程度上是随意的,但往往是首选用法。有人建议根据 SQL 约定(即字符串用单引号,标识符用双引号)来选择引号的类型。但这是一个语言内部的技术问题,许多用户甚至不熟悉,它无法扩展到其他类型的引用术语,它无法翻译成其他语言,而且相当没有意义。

引号的使用 #

始终使用引号来分隔文件名、用户提供的标识符、配置文件变量名以及其他可能包含单词的变量。不要用引号来标记不包含单词的变量(例如,运算符名称)。

后端有一些函数会根据需要自动对输出进行双引号处理(例如,format_type_be())。不要对这些函数的输出添加额外的引号。

理由:对象可能具有在消息中嵌入时会产生歧义的名称。在标记插入名称的开始和结束位置时要保持一致。但不要在消息中添加不必要或重复的引号。

语法和标点符号 #

主错误消息和详细/提示消息的规则不同

主错误消息:不要大写首字母。不要在消息末尾加句号。不要考虑在消息末尾加感叹号。

详细消息和提示消息:使用完整的句子,并在每个句子末尾加句号。大写句子的第一个单词。句号后加两个空格(如果是英文文本;在其他语言中可能不合适)。

错误上下文字符串:不要大写首字母,也不要在字符串末尾加句号。上下文字符串通常不应是完整的句子。

理由:避免标点符号可以帮助客户端应用程序将消息嵌入到各种语法上下文中。通常,主消息本来就不是完整的句子。(如果它们足够长,包含多个句子,则应将其拆分为主部分和详细部分。)然而,详细消息和提示消息较长,可能需要包含多个句子。为了保持一致性,即使只有一个句子,它们也应遵循完整句子的风格。

大写与小写 #

消息文本使用小写,包括主错误消息的首字母。如果消息中出现 SQL 命令和关键字,则使用大写。

理由:以这种方式使一切看起来更一致更容易,因为有些消息是完整的句子,有些则不是。

避免被动语态 #

使用主动语态。当有动作主语时,使用完整的句子(“A 无法执行 B”)。如果主语是程序本身,则使用电报式风格,不带主语;不要使用“”来指代程序。

理由:程序不是人类。不要假装是。

现在时与过去时 #

如果尝试某事失败,但下次可能成功(也许在修复某些问题后),则使用过去时。如果失败肯定永久存在,则使用现在时。

以下形式的句子之间存在细微的语义差异:

could not open file "%s": %m

cannot open file "%s"

第一种形式表示打开文件的尝试失败了。消息应给出原因,例如“磁盘已满”或“文件不存在”。过去时是合适的,因为下次磁盘可能不再满,或者文件可能存在。

第二种形式表示程序中根本不存在打开命名文件的功能,或者在概念上是不可能的。现在时是合适的,因为这种情况将无限期地持续下去。

理由:诚然,普通用户无法仅从消息的时态中得出太多结论,但由于语言为我们提供了语法,我们应该正确使用它。

对象类型 #

引用对象名称时,请说明对象的类型。

理由:否则没人知道“foo.bar.baz”指的是什么。

括号 #

方括号仅用于(1)命令语法中表示可选参数,或(2)表示数组下标。

理由:其他任何用法都不符合广为人知的习惯用法,会使人困惑。

组装错误消息 #

当消息包含来自其他地方生成的文本时,请按此风格嵌入:

could not open file %s: %m

理由:很难考虑到所有可能的错误代码来将此内容粘贴成一个流畅的句子,因此需要某种标点符号。也有人建议将嵌入的文本放在括号中,但这并不自然,因为嵌入的文本通常是消息中最重要的一部分,这种情况很常见。

错误原因 #

消息应始终说明错误发生的原因。例如:

BAD:    could not open file %s
BETTER: could not open file %s (I/O failure)

如果原因不明,最好修复代码。

函数名 #

不要在错误文本中包含报告例程的名称。我们有其他机制可以在需要时找出这些信息,而对于大多数用户来说,这不是有用的信息。如果错误文本在没有函数名的情况下不太有意义,请重新措辞。

BAD:    pg_strtoint32: error in "z": cannot parse "z"
BETTER: invalid input syntax for type integer: "z"

也要避免提及被调用的函数名;而是说明代码试图做什么

BAD:    open() failed: %m
BETTER: could not open file %s: %m

如果确实有必要,请在详细消息中提及系统调用。(在某些情况下,提供传递给系统调用的实际值可能适合作为详细消息的信息。)

理由:用户不知道所有这些函数的功能。

应避免使用的棘手词语 #

无法(Unable)。 无法”近乎被动语态。最好使用“不能(cannot)”或“不能(could not)”,视情况而定。

错误(Bad)。  像“错误的结果”这样的错误消息很难被智能地解释。最好写出为什么结果是“错误”的,例如,“格式无效”。

非法(Illegal)。 非法”表示违反法律,其他则是“无效”。更好的是,说明为什么它是无效的。

未知(Unknown)。  尽量避免使用“未知”。考虑“错误:未知响应”。如果你不知道响应是什么,你怎么知道它是错误的?“无法识别(Unrecognized)”通常是更好的选择。另外,请务必包含被抱怨的值。

BAD:    unknown node type
BETTER: unrecognized node type: 42

查找 vs. 存在(Find vs. Exists)。  如果程序使用非平凡的算法来定位资源(例如,路径搜索)并且该算法失败,那么说程序无法“找到”资源是可以的。另一方面,如果已知资源的预期位置但程序无法访问,则说资源“不存在”。在这种情况下使用“找到”听起来很弱,并混淆了问题。

可能(May) vs. 能够(Can) vs. 也许(Might)。 May”暗示许可(例如,“你可以借我的耙子。”),在文档或错误消息中很少使用。“Can”暗示能力(例如,“我可以举起那个原木。”),而“Might”暗示可能性(例如,“今天可能会下雨。”)。使用正确的词语可以澄清含义并辅助翻译。

缩写。  避免使用缩写,如“不能(can't)”;而是使用“不能(cannot)”。

非负(Non-negative)。  避免使用“非负”,因为它含糊不清,不确定是否接受零。最好使用“大于零”或“大于或等于零”。

拼写正确 #

将单词完整拼出。例如,避免使用

  • spec

  • stats

  • parens

  • auth

  • xact

理由:这将提高一致性。

本地化 #

请记住,错误消息文本需要翻译成其他语言。请遵循 56.2.2 节 中的指南,以避免给翻译者增加困难。

提交更正

如果您在文档中看到任何不正确、与您对特定功能的经验不符或需要进一步说明的内容,请使用 此表单 报告文档问题。