当前位置: 首页 > 工具软件 > spdlog > 使用案例 >

spdlog封装自己的日志库

太叔何平
2023-12-01

先上日志配置定义

struct logger_config {
    std::string name = "run_logger";
    spdlog::level::level_enum level = spdlog::level::trace;
    std::string format = "[%Y-%m-%d %H:%M:%S.%f] [%P] [%^%l%$] [%s:%# %!()] %v";
    std::string file_name = "../log/run.log";
    // by_size按大小分割 by_day按天分割 by_hour按小时分割
    std::string roll_type = "by_day";
    unsigned int reserve_count = 10;
    unsigned int roll_size = 1024 * 1024 * 100;  // 默认100 MB
    // 表示按天切割的时刻,具体是时刻通过rotation_hour:rotation_minute指定
    unsigned int rotation_hour = 0;
    unsigned int rotation_minute = 0;
    // 异步日志线程池大小
    unsigned int async_thread_pool_size = 1;
};

关于format的格式定义可以参考这里https://spdlog.docsforge.com/master/3.custom-formatting/#pattern-flags

定义自己的日志类logger

// log.h
class logger {
public:
    logger();
    ~logger();

    int set_config(const logger_config& config);

    void log(const char* file_name_in, int line_in, const char* func_name_in, spdlog::level::level_enum level,
             const std::string& msg);

private:
    std::shared_ptr<spdlog::logger> run_logger_;
    std::shared_ptr<spdlog::details::thread_pool> thread_pool_;
};
// log.cpp
logger::logger() {
    // 设置打印日志时异常处理函数
    spdlog::set_error_handler([](const std::string& msg) {
        std::cout << fmt::format("[{}:{} {}()] {}", __FILE__, __LINE__, __FUNCTION__, msg) << std::endl;
    });
}

logger::~logger() { spdlog::drop_all(); }

int logger::set_config(const logger_config& config) {
    int ret = 0;
    do {
        try {
            std::shared_ptr<spdlog::sinks::sink> sink_;
            if (config.roll_type == "by_size") {
                sink_ = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(config.file_name, config.roll_size,
                                                                               config.reserve_count, true);
            } else if (config.roll_type == "by_day") {
                sink_ = std::make_shared<spdlog::sinks::daily_file_sink_mt>(config.file_name, config.rotation_hour,
                                                                            config.rotation_minute, false, config.reserve_count);
            } else if (config.roll_type == "by_hour") {
                sink_ = std::make_shared<spdlog::sinks::hourly_file_sink_mt>(config.file_name, false, config.reserve_count);
            } else {
                ret = 1;
                LOG_ERROR("invalid roll_type: {}", config.roll_type.c_str());
                break;
            }
            auto formatter = std::make_unique<spdlog::pattern_formatter>(config.format, spdlog::pattern_time_type::local,
                                                                         spdlog::details::os::default_eol);
            // unique_ptr不能复制,无法通过值传递调用set_formatter,只能使用move
            sink_->set_formatter(std::move(formatter));
            thread_pool_ = std::make_shared<spdlog::details::thread_pool>(config.async_thread_pool_size, 1);
            run_logger_ = std::make_shared<spdlog::async_logger>(config.name, std::move(sink_), thread_pool_,
                                                                 spdlog::async_overflow_policy::block);
            // 设置默认刷新级别和打印级别
            run_logger_->flush_on(spdlog::level::trace);
            run_logger_->set_level(config.level);
            // 注册到spdlog
            spdlog::register_logger(run_logger_);
        } catch (const std::exception& ex) {
            ret = -1;
            LOG_ERROR("{}", ex.what());
        } catch (...) {
            ret = -1;
            LOG_ERROR("unknow exception");
        }
    } while (0);
    return ret;
}

void logger::log(const char* file_name_in, int line_in, const char* func_name_in, spdlog::level::level_enum level,
                 const std::string& msg) {
    try {
        run_logger_->log(spdlog::source_loc{file_name_in, line_in, func_name_in}, level, msg);
    } catch (const std::exception& ex) {
        LOG_ERROR("{}", ex.what());
    } catch (...) {
        LOG_ERROR("unknow exception");
    }
}
  • 注意这里我们创建的spdlog::async_logger日志,要设置线程池,我们没有使用spdlog的全局线程池,自己创建了一个默认给了大小1,因为只有一个线程的时候可以保证写的日志消息顺序
  • 在使用spdlog创建sink和logger的过程中都要捕获异常
  • 在使用spdlog打印日志时也要捕获异常,同时我们在构造函数中使用spdlog::set_error_handler注册了打印日志出错的回调函数
  • 最后我们在析构函数中进行清理工作调用spdlog::drop_all
 类似资料: