44.3. 错误消息风格指导

这份风格向导的目的是希望能把所有 PostgreSQL 生成的消息维护一个一致的,用户友好的风格。

44.3.1. 何去何从

主信息应该简短,基于事实,并且避免引用类似特定函数名等这样的实现细节。 "简短"意味着"在正常情况下应该能放在一行里"。 如果必要,比如你觉得需要提到失败的特定系统调用之类的实现细节,可以使用一个详细信息以保持主信息的简短。 使用一个提示消息给出一个修补问题的提示,特别是在提出的建议可能并不总是有效的情况下。

比如,我们可以不这么写:

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

基本原理:保持主消息的简短可以使它的内容有效, 并且让客户端的屏幕空间布局可以做出给错误信息保留一行就足够的假设。 而详细信息和提示信息可以转移到一个冗余模式里,或者使一个弹出的错误细节的窗口。 同样,详细信息和提示信息通常都会在服务器日志里消除,以节约空间。 对实现细节的引用最好避免,因为毕竟用户不知道细节。

44.3.2. 格式化

不要在消息文本里放任何有关格式化的特定的假设。 除非是客户端或者服务器日志为了复合自己需要回卷了长行。 在长信息里,新行字符(\n)可以用于分段建议。 不要用新行结束一条消息。不要使用tab或者其它格式化字符。 (在错误环境下的显示里,系统会自动给独立级别的环境,比如,函数调用,增加新行。)

基本原理:信息不一定非得在终端类型的显示器上显示。 在 GUI 显示或者在浏览器里,这些格式指示器最好被忽略。

44.3.3. 引号

在需要的时候,英文文本应该使用双引号引起来。 其它语言的文本应该一致地使用一种引号,这种用法应该和出版习惯以及其它程序的计算输出一致。

基本原理:选择双引号而不是单引号从某种角度来说是随机选择, 但是应该是最优的选择。有人建议过根据 SQL 传统,在不同对象类型上使用不同的引号(也就是说,字串单引号,标识符双引号)。 但是这是一种语言内部的技巧,许多用户甚至都不熟悉,并且也不能厌战到其它类型的引号场合, 也不能翻译成其它语言,而且也没啥意义。

44.3.4. 使用引号

总是用引号分隔文件名,用户提供的标识符,以及其它可能包含字的变量。 不要用引号包含那些不会包含字的变量(比如,操作符名字)。

在后端里有些函数会根据需要在他们的输出上加双引号(比如,format_type_be())。 不要在这类函数的输出上加额外的引号。

基本原理:对象的名字嵌入到信息里面之后,可能造成歧义。 在一个插入的名气的启示和终止的位置保持一致。 但是不要在信息里混杂大量不必要的或者重复的引号。

44.3.5. 语法和标点

对于主错误信息和详细/提示信息,规则不同:

主错误信息:首字母不要大写。不要用句号结束信息。绝对不要用叹号结束一条信息。

详细和提示信息:使用完整的句子,并且用句号终止每个语句。句子首字母大写。

基本原理:避免标点可以让客户端应用比较容易把信息嵌入到各种语言环境中。 并且,主消息也经常不是完整的句子。(并且,如果信息长得超过一个句子,那么就应该把他们分裂撑主信息和详细信息部分。) 不过,细节和提示信息长得多并且可能需要包含在多个句子中。 为了保持一致,这些句子应该遵循完整得句子得风格,即使他们只有一个句子。

44.3.6. 大写字符与小写字符比较

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

基本原理:这样很容易让所有东西看起来都一样,因为有些消息是完整的句子,有些不是。

44.3.7. 避免被动语气

使用主动语气。如果有主语,那么就使用完整的句子("A 不能做 B")。 如果主语是程序自己,那么就使用电报风格的语言;不要用"我"作为程序的主语。

基本原理:程序不是人。否则不要装成人。

44.3.8. 现代时与过去时的比较

如果尝试某事失败,但可能下次尝试的时候成功(可能是修补了某些问题之后),那么使用过去时。 如果错误肯定是永久的,那么用现代时。

下面的两个形式的句子之间的差别并不小

could not open file "%s": %m

cannot open file "%s"

第一个句子的意思是打开某个文件的企图失败。这个信息应该给出一个原因, 比如说"磁盘满"或者"文件不存在"之类的。 过去时的语气应该是合适的,因为下次磁盘可能不再是满的,或者有问题的文件存在了。

第二种形式表示打开指定文件的功能根本就不在程序里存在, 或者是这么做概念上是错误的。现代时语气是合适的,因为这个条件将无条件存在。

基本原理:当然,普通用户将不会仅仅从信息的时态上得出大量的结论,但是既然语言提供给我们语法,那么我们就应该正确使用。

44.3.9. 对象类型

在引用一个对象的名字的时候,说明它是什么类型的对象。

基本原理:否则,没人知道 "foo.bar.baz" 是什么。

44.3.10. 方括弧

方括弧只用在(1)命令语法里表示可选的参数,或者(2)表示一个数组下标。

基本原理:任何其它的东西都不能对应这两种中所周知的习惯用法并且会让人混淆。

44.3.11. 组装错误信息

如果一个信息包含其它地方生成的文本,用下面的风格包含它:

could not open file %s: %m

基本原理:我们很难估计所有可能放在这里的错误代码并且把它放在一个平滑的句子里, 所以需要某种方式的标点。也曾经建议把嵌入的文本放在圆括弧里,但是如果嵌入文本是信息的最重要部分,那么就不太自然, 而这种情况是很经常的。

44.3.12. 错误的原因

消息应该总是说明为什么发生错误。比如:

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

如果不知道原因,那么你最好修补代码。

44.3.13. 函数名

不要在错误信息里包含报告过程的名字。需要的时候,我们有别的机制找出这个函数, 并且,对于大多数用户,这个信息也没什么用。如果错误信息在缺少函数名的情况下没有什么意义,那么重新措辞。

BAD:    pg_atoi: error in "z": can't parse "z"
BETTER: invalid input syntax for integer: "z"

也避免提及被调用的函数名字;应该说代码视图做什么:

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

如果确实必要,在详细信息里提出系统调用。 (在某些场合下,提供给系统调用的具体数值是适合放在详细信息里。)

基本原理:用户不知道这些函数都干些啥。

44.3.14. 尽量避免的字眼

Unable/不能. "Unable/不能"几乎是被动语气。最好使用 "cannot/无法" 或者 "could not"

Bad/坏的. 类似"bad result/坏结果"这样的信息真的是很难聪明地解释。 最好写出为什么结果是"bad/坏的",比如,"invalid format/非法格式"

Illegal/非法. "Illegal/非法" 表示违反了法律,其它的就是 "invalid/非法"。但是最好还是说非法。

Unknown/未知. 应该避免使用"unknown/未知"。想想:"error: unknown response"。如果你不知道响应是什么,你怎么知道是错误? "Unrecognized/无法识别的"通常是更好的选择。 还有最好药包括被比较的数值。

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

找到与存在的对比. 如果程序使用一个相当复杂的算法来定位一个资源(比如,一个路径搜索), 并且算法失败了,那么说程序无法"找到"改资源是合理的。 但是,如果语气的资源位置是已知的但是程序无法在那里访问它,那么说这个资源不"存在"。 这种情况下用"找到"听起来语气比较弱并且会混淆事实。

44.3.15. 正确地拼写

用单词的全拼。比如,避免下面这样的缩写:

基本原理:这样将改善一致性。

44.3.16. 本地化

请记住,错误信息文本是需要翻译成其它语言的。 遵循 Section 45.2.2 里面的指导以避免给翻译家造成太多麻烦。