目錄
1.自定義異常類spdlog_ex
1.1.通用異常
1.2.系統調用異常
1.3.what()函數
2.異常的使用
2.1.拋出異常
2.2.控制異常使用
1.自定義異常類spdlog_ex
標準庫異常類(std::exception)系列,能滿足大多數使用異常的場景,但對系統調用異常及錯誤信息缺乏支持。spdlog通過繼承std::exception,擴展對系統調用的支持,實現自定義異常類spdlog_ex。
spdlog_ex類聲明很簡單,在std::exception基礎上添加了string類型的msg_成員,提供支持errno的構造函數。
// include/spdlog/details/common.h// Log exception
class SPDLOG_API spdlog_ex : public std::exception
{
public:explicit spdlog_ex(std::string msg);spdlog_ex(const std::string &msg, int last_errno); // 提供系統調用錯誤號errno的支持const char *what() const SPDLOG_NOEXCEPT override;
private:std::string msg_; // 異常文本信息
};
1.1.通用異常
對于通用的異常,spdlog_ex并未做什么特別的事情,只是將用戶傳入的異常提示信息存放到msg_。
SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg): msg_(std::move(msg))
{}
1.2.系統調用異常
spdlog_ex對errno的支持,主要是將errno轉換為對應錯誤文本信息,存放到msg_字符串中。spdlog使用的是ftm庫提供的format_system_error來完成轉換工作,出于對memory_buf_t支持。當然,也可以使用C庫函數strerror(或者線程安全版本strerror_r)。
SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
{
#ifdef SPDLOG_USE_STD_FORMATmsg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what();
#elsememory_buf_t outbuf;fmt::format_system_error(outbuf, last_errno, msg.c_str());msg_ = fmt::to_string(outbuf);
#endif
}
1.3.what()函數
what()是基類std::exception定義的virtual函數,用戶通常通過該接口獲取異常信息。spdlog_ex也是簡單的返回存放異常信息的msg_。
SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
{return msg_.c_str();
}
2.異常的使用
2.1.拋出異常
前面是講如何實現spdlog_ex,但如何在spdlog中拋出一個異常對象呢?直接調用throw spdlog_ex(..)?
spdlog提供了重載函數形式的接口:throw_spdlog_ex。
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
{SPDLOG_THROW(spdlog_ex(msg, last_errno));
}SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
{SPDLOG_THROW(spdlog_ex(std::move(msg)));
}
throw_spdlog_ex本質上也是throw spdlog_ex(..),為何要通過一個宏定義SPDLOG_THROW來進行呢?
這就涉及到下面要講的控制異常使用。
2.2.控制異常使用
有些APP并不希望第三方庫拋出異常,而有些無所謂。為此,spdlog提供兩種模式:拋出異常,不拋出異常,通過宏定義SPDLOG_NO_EXCEPTIONS來控制。
當沒有定義宏SPDLOG_NO_EXCEPTIONS時,正常拋出異常對象;
當定義了宏SPDLOG_NO_EXCEPTIONS時,拋出異常替換為直接終止程序(abort)
#ifdef SPDLOG_NO_EXCEPTIONS
# define SPDLOG_TRY
# define SPDLOG_THROW(ex) \do \{ \printf("spdlog fatal error: %s\n", ex.what()); \std::abort(); \} while (0)
# define SPDLOG_CATCH_STD
#else
# define SPDLOG_TRY try
# define SPDLOG_THROW(ex) throw(ex)
# define SPDLOG_CATCH_STD \catch (const std::exception &) {}
#endif
通過這種方式,spdlog異常處理更加靈活,更好適配APP對是否拋出異常的需求。
因此,在spdlog中,捕獲異常的代碼塊try-catch,看起來會是這樣:
// message all threads to terminate gracefully join them
SPDLOG_INLINE thread_pool::~thread_pool()
{SPDLOG_TRY{for (size_t i = 0; i < threads_.size(); i++){post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);}for (auto &t : threads_){t.join();}}SPDLOG_CATCH_STD
}
當然,也可以使用自定義捕獲(catch)代碼塊,替換SPDLOG_CATCH_STD,看起來會是這樣:
SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
{for (auto &sink : sinks_){SPDLOG_TRY{sink->flush();}SPDLOG_LOGGER_CATCH(source_loc())}
}#ifndef SPDLOG_NO_EXCEPTIONS
# define SPDLOG_LOGGER_CATCH(location) \catch (const std::exception &ex) \{ \if (location.filename) \{ \err_handler_(fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), location.filename, location.line)); \} \else \{ \err_handler_(ex.what()); \} \} \catch (...) \{ \err_handler_("Rethrowing unknown exception in logger"); \throw; \}
#else
# define SPDLOG_LOGGER_CATCH(location)
#endif