spdlog日志库源码:异常类spdlog_ex

自定义异常类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_; // 异常文本信息
};

通用异常

对于通用的异常,spdlog_ex并未做什么特别的事情,只是将用户传入的异常提示信息存放到msg_。

SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
    : msg_(std::move(msg))
{}

系统调用异常

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_FORMAT
    msg_ = std::system_error(std::error_code(last_errno, std::generic_category()),  msg).what();
#else
    memory_buf_t outbuf;
    fmt::format_system_error(outbuf, last_errno, msg.c_str());
    msg_ = fmt::to_string(outbuf);
#endif
}

what()函数

what()是基类std::exception定义的virtual函数,用户通常通过该接口获取异常信息。spdlog_ex也是简单的返回存放异常信息的msg_。

SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
{
    return msg_.c_str();
}

异常的使用

抛出异常

前面是讲如何实现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来进行呢?
这就涉及到下面要讲的控制异常使用。

控制异常使用

有些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

spdlog库系列:spdlog库笔记汇总

posted @ 2022-10-27 11:21  明明1109  阅读(1106)  评论(0编辑  收藏  举报