45.3. 錯誤消息風格指導

這份風格向導的目的是希望能把所有 PostgreSQL 生成的消息維護一個一致的,用戶友好的風格。

45.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

基本原理:保持主消息的簡短可以使它的內容有效, 並且讓客戶端的屏幕空間布局可以做出給錯誤信息保留一行就足夠的假設。 而詳細信息和提示信息可以轉移到一個冗餘模式裡,或者使一個彈出的錯誤細節的窗口。 同樣,詳細信息和提示信息通常都會在服務器日志裡消除,以節約空間。 對實現細節的引用最好避免,因為畢竟用戶不知道細節。

45.3.2. 格式化

不要在消息文本裡放任何有關格式化的特定的假設。 除非是客戶端或者服務器日志為了復合自己需要回卷了長行。 在長信息裡,新行字符(\n)可以用于分段建議。 不要用新行結束一條消息。不要使用tab或者其它格式化字符。 (在錯誤環境下的顯示裡,系統會自動給獨立級別的環境,比如,函數調用,增加新行。)

基本原理:信息不一定非得在終端類型的顯示器上顯示。 在 GUI 顯示或者在瀏覽器裡,這些格式指示器最好被忽略。

45.3.3. 引號

在需要的時候,英文文本應該使用雙引號引起來。 其它語言的文本應該一致地使用一種引號,這種用法應該和出版習慣以及其它程序的計算輸出一致。

基本原理:選擇雙引號而不是單引號從某種角度來說是隨機選擇, 但是應該是最優的選擇。有人建議過根據 SQL 傳統,在不同對象類型上使用不同的引號(也就是說,字串單引號,標識符雙引號)。 但是這是一種語言內部的技巧,許多用戶甚至都不熟悉,並且也不能厭戰到其它類型的引號場合, 也不能翻譯成其它語言,而且也沒啥意義。

45.3.4. 使用引號

總是用引號分隔文件名,用戶提供的標識符,以及其它可能包含字的變量。 不要用引號包含那些不會包含字的變量(比如,操作符名字)。

在後端裡有些函數會根據需要在他們的輸出上加雙引號(比如,format_type_be())。 不要在這類函數的輸出上加額外的引號。

基本原理:對象的名字嵌入到信息裡面之後,可能造成歧義。 在一個插入的名氣的啟示和終止的位置保持一致。 但是不要在信息裡混雜大量不必要的或者重復的引號。

45.3.5. 語法和標點

對于主錯誤信息和詳細/提示信息,規則不同:

主錯誤信息:首字母不要大寫。不要用句號結束信息。絕對不要用嘆號結束一條信息。

詳細和提示信息:使用完整的句子,並且用句號終止每個語句。句子首字母大寫。

基本原理:避免標點可以讓客戶端應用比較容易把信息嵌入到各種語言環境中。 並且,主消息也經常不是完整的句子。(並且,如果信息長得超過一個句子,那麼就應該把他們分裂撐主信息和詳細信息部分。) 不過,細節和提示信息長得多並且可能需要包含在多個句子中。 為了保持一致,這些句子應該遵循完整得句子得風格,即使他們只有一個句子。

45.3.6. 大寫字符與小寫字符比較

消息用語使用小寫字符,包括主錯誤信息的首字母。 如果消息中出現 SQL 命令和關鍵字,用大寫。

基本原理:這樣很容易讓所有東西看起來都一樣,因為有些消息是完整的句子,有些不是。

45.3.7. 避免被動語氣

使用主動語氣。如果有主語,那麼就使用完整的句子("A 不能做 B")。 如果主語是程序自己,那麼就使用電報風格的語言;不要用"我"作為程序的主語。

基本原理:程序不是人。否則不要裝成人。

45.3.8. 現代時與過去時的比較

如果嘗試某事失敗,但可能下次嘗試的時候成功(可能是修補了某些問題之後),那麼使用過去時。 如果錯誤肯定是永久的,那麼用現代時。

下面的兩個形式的句子之間的差別並不小

        could not open file "%s": %m

        cannot open file "%s"

第一個句子的意思是打開某個文件的企圖失敗。這個信息應該給出一個原因, 比如說"磁盤滿"或者"文件不存在"之類的。 過去時的語氣應該是合適的,因為下次磁盤可能不再是滿的,或者有問題的文件存在了。

第二種形式表示打開指定文件的功能根本就不在程序裡存在, 或者是這麼做概念上是錯誤的。現代時語氣是合適的,因為這個條件將無條件存在。

基本原理:當然,普通用戶將不會僅僅從信息的時態上得出大量的結論,但是既然語言提供給我們語法,那麼我們就應該正確使用。

45.3.9. 對象類型

在引用一個對象的名字的時候,說明它是什麼類型的對象。

基本原理:否則,沒人知道 "foo.bar.baz" 是什麼。

45.3.10. 方括弧

方括弧只用在(1)命令語法裡表示可選的參數,或者(2)表示一個數組下標。

基本原理:任何其它的東西都不能對應這兩種中所週知的習慣用法並且會讓人混淆。

45.3.11. 組裝錯誤信息

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

        could not open file %s: %m

基本原理:我們很難估計所有可能放在這裡的錯誤代碼並且把它放在一個平滑的句子裡, 所以需要某種方式的標點。也曾經建議把嵌入的文本放在圓括弧裡,但是如果嵌入文本是信息的最重要部分,那麼就不太自然, 而這種情況是很經常的。

45.3.12. 錯誤的原因

消息應該總是說明為什麼發生錯誤。比如:

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

如果不知道原因,那麼你最好修補代碼。

45.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

如果確實必要,在詳細信息裡提出系統調用。 (在某些場合下,提供給系統調用的具體數值是適合放在詳細信息裡。)

基本原理:用戶不知道這些函數都幹些啥。

45.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

找到與存在的對比. 如果程序使用一個相當復雜的算法來定位一個資源(比如,一個路徑搜索), 並且算法失敗了,那麼說程序無法"找到"改資源是合理的。 但是,如果語氣的資源位置是已知的但是程序無法在那裡訪問它,那麼說這個資源不"存在"。 這種情況下用"找到"聽起來語氣比較弱並且會混淆事實。

45.3.15. 正確地拼寫

用單詞的全拼。比如,避免下面這樣的縮寫:

基本原理:這樣將改善一致性。

45.3.16. 本地化

請記住,錯誤信息文本是需要翻譯成其它語言的。 遵循 Section 46.2.2 裡面的指導以避免給翻譯家造成太多麻煩。