1. 程式人生 > >Postgresql原始碼ERROR日誌函式簡析

Postgresql原始碼ERROR日誌函式簡析

背景

  • 最近在閱讀Postgresql 10.3原始碼時發現,很多函式中列印了elog(ERROR)之後並沒有明顯的return,但從上下文邏輯看,此時必須返回錯誤、無法繼續執行了。難道elog(ERROR)自帶函式返回功能?帶著這個疑問,簡單梳理了一下elog的呼叫流程.

elog 原始碼淺析

  • elog 巨集定義
/*
 * 如果有可變引數巨集,我們將給編譯器一個暗示:當elevel>=ERROR時,函式呼叫不會返回
 * If we have variadic macros, we can give the compiler a hint about the
 * call
not returning when elevel >= ERROR. See comments for ereport(). * Note that historically elog() has called elog_start (which saves errno) * before evaluating "elevel", so we preserve that behavior here. */ #ifdef HAVE__BUILTIN_CONSTANT_P #define elog(elevel, ...) \ do { \ elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \ elog_finish(elevel, __VA_ARGS__); \ if
(__builtin_constant_p(elevel) && (elevel) >= ERROR) \ pg_unreachable(); \ } while(0) #else /* !HAVE__BUILTIN_CONSTANT_P */ #define elog(elevel, ...) \ do { \ int elevel_; \ elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \ elevel_ = (elevel); \ elog_finish(elevel_, __VA_ARGS__); \ if
(elevel_ >= ERROR) \ pg_unreachable(); \ } while(0)
  • 簡單分析下上述巨集定義:

    • 首先是判斷下是否支援__builtin_constant_p,這是編譯器gcc內建函式,用於判斷一個值是否為編譯時常量,如果是常數,函式返回1 ,否則返回0。如果為常量,可以在程式碼中做一些優化來減少表示式的複雜度
    • elog處理日誌列印,它通常會呼叫 write_log寫日誌
    • elog_finish最後會呼叫errfinish(0),對後者的描述如下:

    errfinish 結束錯誤報告迴圈,它會產生合適的錯誤報告並對錯誤棧執行pop操作。
    如果錯誤等級為ERROR或更嚴重級別,它將不會返回到呼叫方。

  • 再來看 errfinish 的程式碼:

    /*
    * errfinish --- end an error-reporting cycle
    *
    * Produce the appropriate error report(s) and pop the error stack.
    *
    * If elevel is ERROR or worse, control does not return to the caller.
    * See elog.h for the error level definitions.
    */
    void
    errfinish(int dummy,...)
    • ERROR,呼叫PG_RE_THROW, 類似於java、C++語言中的拋異常,結束當前呼叫;
    • FATAL,呼叫proc_exit(1),會按正常流程清理釋放資源並退出程序;
    • PANIC或更嚴重級別,在控制檯列印錯誤後直接abort.
  • ereportelog類似,不過它呼叫ereport_domain實現,該函式允許通過message domain的概念,將某些指定模組的日誌列印到一個單獨的目錄。
  • 總結

    • 要慎重評估程式碼中的日誌級別,不要輕易打elog(ERROR)ereport(ERROR)或更嚴重級別的日誌
    • 如果出現一些小錯誤,但當前操作仍可繼續的,可以打WARNING級別日誌
    • 如果出現錯誤且當前流程無法繼續、但不影響其他業務請求的,可列印ERROR
    • 如出現可能導致整個服務異常的問題,則列印FATAL甚至PANIC