0%

前言

鉴于工作需求,日志记录工具是必不可少的存在。
现对 spdlog 在c++项目的使用方法进行探究。

项目地址

https://github.com/gabime/spdlog#compiled-version-recommended---much-faster-compile-times

官方说明文档地址

https://github.com/gabime/spdlog/wiki

使用方法

将项目源码中的 inlcude 中的 spdlog 文件夹添加到自己的项目中即可使用。
该项目通过 logger 作为对象进行调用,logger 接受用户的日志信息,对应日志的输入端,然后传递给 sink,sink对应日志的输出端。
所以通过建立多个 sink 对应一个 logger 就可以实现同日志输入但是输出多个不同等级要求的日志文件。

完整Demo

main文件初始化日志代码:

#include "mainwindow.h"
#include <QApplication>
#include <QSettings>
#include <QDir>
#include <QDebug>
#include "log4z/log4z.h"

#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h" // or "../stdout_sinks.h" if no colors needed



void initSPDLOG(){
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::warn);

    auto file_sink_trace = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/Trace/Trace.log", 23, 59, false);
    file_sink_trace->set_level(spdlog::level::trace);

    auto file_sink_debug = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/debug/debug.log", 23, 59, false, 0);
    file_sink_debug->set_level(spdlog::level::debug);

    auto file_sink_info = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/Info/Info.log", 23, 59, false, 0);
    file_sink_info->set_level(spdlog::level::info);

    auto file_sink_warn = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/warn/warn.log", 23, 59, false, 0);
    file_sink_warn->set_level(spdlog::level::warn);

    auto file_sink_err = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/err/err.log", 23, 59, false, 0);
    file_sink_err->set_level(spdlog::level::err);

    auto file_sink_critical = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/critical/critical.log", 23, 59, false, 0);
    file_sink_critical->set_level(spdlog::level::critical);


    // 组合sink列表
    spdlog::sinks_init_list sink_list = { console_sink, file_sink_trace, file_sink_debug, file_sink_info, file_sink_warn,
                                        file_sink_err, file_sink_critical};

    // 将sink列表设置到全局
    spdlog::set_default_logger(std::make_shared<spdlog::logger>("multi_sink", sink_list));
    // 设置日志内容输出格式
    spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] %^[%l]%$ %v");

    // spdlog 默认level为 INfo 需要手动改 trace
    spdlog::set_level(spdlog::level::trace);
    // 设置刷新,刷新本地文件,以便随时查看本地日志文件
    spdlog::flush_every(std::chrono::seconds(5));

}


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    AllocConsole();
    freopen("CONOUT$", "a+", stdout);

    // psdLog
    initSPDLOG();


    MainWindow w;
    w.show();

    return a.exec();
}

全局使用方法:

    int myMesg = 1;
    QString testMesg = "hello word";
    spdlog::trace("trace message {} {}", myMesg, testMesg.toStdString().c_str());
    spdlog::debug("debug message");
    spdlog::info("info message");
    spdlog::warn("waring message");
    spdlog::error("error message");
    spdlog::critical("critical message");

如需要添加新的输出 sink,如 qt 控件。

    auto qt_sink_trace = std::make_shared<spdlog::sinks::qt_sink_mt>(ui->plainTextEdit, "appendPlainText");
    qt_sink_trace->set_level(spdlog::level::trace);

    spdlog::get("multi_sink")->sinks().push_back(qt_sink_trace);

记下来对代码内容进行说明

新建 sink

#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h" // or "../stdout_sinks.h" if no colors needed


    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::warn);

    auto file_sink_trace = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/Trace/Trace.log", 23, 59, false);
    file_sink_trace->set_level(spdlog::level::trace);

sink 作为日志的输出,有很多种。 常用如下,其余可参考说明文档。

类名 特性
rotating_file_sink 可根据设定的大小自动分开日志文件
daily_file_sink 可根据设定的日期自动分开日志文件
simple_file_sink 固定一个文件
stdout_color_sink 标准输出

代码中使用时需要添加对应 sink 头文件。
不同 sink 需要的构造参数不同,可参考对应构造函数。
其中 truncate 设定 false 表示如果存在同名日志,在其后继续记录,否则清空从头开始记录。
每一个 sink 构造时分为 mt 和 st,根据官方文档说明 mt 表示 multithrad,st 表示 singlethread。
为了线程安全只用 mt 就好,st 特殊需求可能会用到。

新建 logger

    // 组合sink列表
    spdlog::sinks_init_list sink_list = { console_sink, file_sink_trace, file_sink_debug, file_sink_info, file_sink_warn,
                                        file_sink_err, file_sink_critical};

通过 sinks_init_list 将所有建立的 sink 放到一个列表里,变量名为 sink_list。

// 将sink列表设置到全局
    spdlog::set_default_logger(std::make_shared<spdlog::logger>("multi_sink", sink_list));

这段代码有两层意思,组合到了一起。
先通过 make_shared 建造了一个标准 logger,将 sink_list 作为构造参数传入,获得一个 logger,变量名为 multi_sink, 再通过 spdlog::set_dafult_logger 将刚刚创建的 multi_sink 设定为全局 logger。
这样在别的任意地方调用默认打印日志的方法就会调用 multi_sink 这个 logger。

    // 设置日志内容输出格式
    spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] %^[%l]%$ %v");

设定全局的日志输出格式。
如果有不同 sink 不同格式的需求,可以直接给单个 sink 进行设置。
设置方法详见说明文档。

    // spdlog 默认level为 INfo 需要手动改 trace
    spdlog::set_level(spdlog::level::trace);

spdlog 默认全局 level 为 INFO,如果不设置的话 debug 和 trace 的信息将无法输出。

    // 设置刷新,刷新本地文件,以便随时查看本地日志文件
    spdlog::flush_every(std::chrono::seconds(5));

不设置这个的话调试窗口的日志也能正常输出,但是本地文件流只有在程序关闭的时候才会全部写入。
通过设定5s刷新,就能随时查看本地文件的日志了。

全局输出日志

    #include "spdlog/spdlog.h"

    int myMesg = 1;
    QString testMesg = "hello word";
    spdlog::trace("trace message {} {}", myMesg, testMesg.toStdString().c_str());
    spdlog::debug("debug message");
    spdlog::info("info message");
    spdlog::warn("waring message");
    spdlog::error("error message");
    spdlog::critical("critical message");

在需要打印日志的地方引入头文件,然后直接进行全局日志输出方法调用。
需要传参就通过 {} 类python的写法,很方便。