一种简单的 spdlog 封装

记录一种简单的 spdlog 封装

TODO: 如何在.h中隐藏 spdlog 依赖

cmake 集成

CMakeLists.txt如下:

cmake_minimum_required(VERSION 3.14)

project(test VERSION 0.0.1 LANGUAGES CXX)

# 使用 C++17 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 添加可执行文件
add_executable(test main.cpp mylog.h mylog.cpp)

add_subdirectory(vendor/spdlog-1.15.0/)
target_link_libraries(test PRIVATE spdlog::spdlog)

输出增强

Qt 中 UI 同步更新方法

创建UILogSink

// UILogSink.h
#ifndef UILOGSINK_H
#define UILOGSINK_H

#include <QMetaObject>
#include <QPlainTextEdit>
#include <QTextCursor>
#include <mutex>
#include <spdlog/sinks/base_sink.h>

template<typename Mutex>
class UILogSink : public spdlog::sinks::base_sink<Mutex>
{
public:
    UILogSink(QPlainTextEdit *widget)
        : widget_(widget)
    {}

protected:
    void sink_it_(const spdlog::details::log_msg &msg) override
    {
        // 格式化为字符串
        spdlog::memory_buf_t formatted;
        this->formatter_->format(msg, formatted);
        QString qtext = QString::fromStdString(fmt::to_string(formatted));

        // UI 更新必须在主线程
        if (widget_) {
            QMetaObject::invokeMethod(
                widget_,
                [=]() {
                    widget_->appendPlainText(qtext);
                    widget_->moveCursor(QTextCursor::End);
                },
                Qt::QueuedConnection);
        }
    }

    void flush_() override {}

private:
    QPlainTextEdit *widget_;
};

using UILogSink_mt = UILogSink<std::mutex>;

#endif // UILOGSINK_H

使用

#include "formlog.h"
#include "UILogSink.h"
#include "mylog.h"
#include "ui_formlog.h"

FormLog::FormLog(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::FormLog)
{
    ui->setupUi(this);

    auto ui_sink = std::make_shared<UILogSink_mt>(ui->txtLog);
    ui_sink->set_pattern("[%H:%M:%S] [%l] %v");

    MY_LOG.getLogger()->sinks().push_back(ui_sink);
}

FormLog::~FormLog()
{
    delete ui;
}

Qt 中输出 QString 等类型

Qt 中想直接输出 QString 类型数据,但是报错:

E:\project\train\tool\myYolo\vendor\spdlog-1.15.0\include\spdlog\fmt\bundled\base.h:1641: error: C2079: “_”使用未定义的 struct“fmt::v11::detail::type_is_unformattable_for<T,fmt::v11::context::char_type>”

参考 https://fmt.dev/9.1.0/api.html#formatting-user-defined-types

QString

template <> struct fmt::formatter<QString>: formatter<std::string> {
    template <typename FormatContext>
    auto format(QString s, FormatContext& ctx) const {
        return formatter<std::string>::format(s.toStdString(), ctx);
    }
};

QByteArray

template<>
struct fmt::formatter<QByteArray> : fmt::formatter<std::string>
{
    template<typename FormatContext>
    auto format(const QByteArray &s, FormatContext &ctx) const
    {
        return fmt::formatter<std::string>::format(s.toStdString(), ctx);
    }
};

QStringList

template <>
struct fmt::formatter<QStringList> : formatter<std::string>
{
    template <typename FormatContext>
    auto format(QStringList s, FormatContext &ctx) const
    {
        return formatter<std::string>::format(s.join(',').toStdString(), ctx);
    }
};

最终代码

mylog.h

#ifndef MYLOG_H
#define MYLOG_H

#include <QByteArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QString>
#include <memory>

#include "spdlog/spdlog.h"

class MyLog
{
public:
    static MyLog &getInstance();
    ~MyLog();
    void init(const std::string &file_name = "./log/myLog.log",
              size_t max_size = 10 * 1024 * 1024,
              size_t max_files = 10);
    static std::shared_ptr<spdlog::logger> getLogger();

private:
    MyLog();
    static std::shared_ptr<spdlog::logger> s_logger;
};

template<>
struct fmt::formatter<QString> : formatter<std::string>
{
    template<typename FormatContext>
    auto format(QString s, FormatContext &ctx) const
    {
        return formatter<std::string>::format(s.toStdString(), ctx);
    }
};

template<>
struct fmt::formatter<QByteArray> : fmt::formatter<std::string>
{
    template<typename FormatContext>
    auto format(const QByteArray &s, FormatContext &ctx) const
    {
        return fmt::formatter<std::string>::format(s.toStdString(), ctx);
    }
};

template<>
struct fmt::formatter<QStringList> : formatter<std::string>
{
    template<typename FormatContext>
    auto format(QStringList s, FormatContext &ctx) const
    {
        return formatter<std::string>::format(s.join(',').toStdString(), ctx);
    }
};

#define MY_LOG MyLog::getInstance()

#endif // MYLOG_H

mylog.cpp

#include "mylog.h"

#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"

std::shared_ptr<spdlog::logger> MyLog::s_logger = nullptr;

MyLog &MyLog::getInstance()
{
    static MyLog instance;
    return instance;
}

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

void MyLog::init(const std::string &file_name, size_t max_size, size_t max_files)
{
    std::vector<spdlog::sink_ptr> logSinks;
    logSinks.emplace_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
    logSinks.emplace_back(
        std::make_shared<spdlog::sinks::rotating_file_sink_mt>(file_name, max_size, max_files));

    logSinks[0]->set_pattern("%^[%T] %n: %v%$");
    logSinks[1]->set_pattern("[%T] [%l] %n: %v");

    s_logger = std::make_shared<spdlog::logger>("MySerial", begin(logSinks), end(logSinks));
    spdlog::register_logger(s_logger);
    s_logger->set_level(spdlog::level::trace);
    s_logger->flush_on(spdlog::level::trace);
}

std::shared_ptr<spdlog::logger> MyLog::getLogger()
{
    return s_logger;
}

MyLog::MyLog()
{
    init();
}

comment: