Postgresql原始碼ERROR日誌函式簡析
阿新 • • 發佈:2019-02-05
背景
- 最近在閱讀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
.
ereport
和elog
類似,不過它呼叫ereport_domain
實現,該函式允許通過message domain的概念,將某些指定模組的日誌列印到一個單獨的目錄。 總結
- 要慎重評估程式碼中的日誌級別,不要輕易打
elog(ERROR)
、ereport(ERROR)
或更嚴重級別的日誌 - 如果出現一些小錯誤,但當前操作仍可繼續的,可以打
WARNING
級別日誌 - 如果出現錯誤且當前流程無法繼續、但不影響其他業務請求的,可列印
ERROR
- 如出現可能導致整個服務異常的問題,則列印
FATAL
甚至PANIC