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
    發表評論
    所有評論
    還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
    相關文章