#qt
spdlog库简介 spdlog 是一个高性能的 C++ 日志库,它设计时充分考虑了速度和易用性,具有以下特点 高效与快速:Spdlog 专注于提供极致的性能,在大量日志记录场景下也能保持较低的延迟和较高的吞吐量。 轻量化设计:Spdlog 是头文件(header-only)库,这意味着用户只需要包含相应的头文件即可开始使用,无需编译链接额外的库文件。 跨平台支持:它支持多种操作系统,包括但不限于 Windows、Linux 和 macOS,并且在这些平台上都能够良好运行。 丰富的日志级别:Spdlog 支持常见的日志级别,如 TRACE、DEBUG、INFO、WARN、ERROR、CRITICAL 等,用户可以根据需要选择不同级别的日志输出。 格式化与定位信息:通过集成 fmt 库,Spdlog 允许用户自定义日志消息的格式,可以轻松地包含时间戳、线程ID、文件名、行号以及函数名等上下文信息。 多目标输出:可以将日志输出到控制台、普通文本文件、循环写入文件(rotating log files)、每日生成新文件(daily logs)、系统日志等目标,同时也支持异步写入以提高性能。 线程安全:对于多线程环境,Spdlog 提供了线程安全的日志接口,确保在并发环境下日志记录的正确性和完整性。 异步模式:提供可选的异步日志记录机制,能够将日志操作放入后台线程执行,从而避免阻塞主线程。 条件日志:根据预定义的条件开关,可以动态启用或禁用特定级别的日志输出。
git链接
https://github.com/gabime/spdlog.git
先从git链接,拉取对应的代码库, 需要用到的, 是这里的库文件
![[Pasted image 20240706164624.png]]
新建一个测试工程,把开源代码,拷贝过来即可
![[Pasted image 20240706164731.png]]
编译方式用的Qt默认MinGW
![[Pasted image 20240706170105.png]]
新建c++的一对文件,命名为 Logger即可,主要是用来调用,开源库相关
头文件
#ifndef LOGGER_H
#define LOGGER_H
#include <memory>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/fmt/ostr.h>
#include <QString>
#include <QDialog>
namespace Log {
extern QDialog* dialog;
extern QString output;
}
class Logger
{
public:
static void initialize(const QString& log_dir=""); // 初始化日志系统
static void shutdown(); // 清理日志系统
static void setLevel(spdlog::level::level_enum logLevel); // 设置日志级别
static void setDialog(QDialog* dialog);
static void setColor(const QString& color) {m_logColor = color;}
private:
static void qtMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
private:
static std::shared_ptr<spdlog::logger> m_logger;
static QString m_logFilePath;
static QString m_logColor;
};
#endif // LOGGER_H
cpp
#include "logger.h"
#include <QStandardPaths>
#include <QDir>
#include <QDate>
#include <QDebug>
#include <QCoreApplication>
std::shared_ptr<spdlog::logger> Logger::m_logger;
QString Logger::m_logFilePath;
QString Logger::m_logColor;
namespace Log {
QDialog* dialog=nullptr;
QString output;
}
void Logger::initialize(const QString& log_dir)
{
QString name = QDate::currentDate().toString("yyyy-MM-dd") + ".log";
if(log_dir.isEmpty()){
// QDir dir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
QDir dir = QCoreApplication::applicationDirPath();
dir.mkpath("logs");
dir.cd("logs");
m_logFilePath = dir.filePath(name);
}else{
QDir dir(log_dir);
dir.mkpath(".");
m_logFilePath = dir.filePath(name);
}
// 创建标准输出的彩色日志
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(m_logFilePath.toLocal8Bit().toStdString(), 1024*1024, 3);
// 设置模式字符串,%^ 和 %$ 分别代表颜色范围的开始和结束
console_sink->set_pattern("%^[%H:%M:%S.%e] [%L] [%t] %v%$");
file_sink->set_pattern("[%H:%M:%S.%e] [%L] [%t] %v");
spdlog::sinks_init_list sink_list = { console_sink, file_sink };
m_logger = std::make_shared<spdlog::logger>("logger", sink_list.begin(), sink_list.end());
m_logger->set_level(spdlog::level::info); // 默认日志级别
// spdlog::flush_every(std::chrono::seconds(3)); // 设置自动间隔
qInstallMessageHandler(qtMessageHandler);
qInfo() << "*************************** Log Start ********************************";
}
void Logger::shutdown()
{
qInfo() << "*************************** Log End ********************************\n";
spdlog::drop_all(); // 清理所有日志实例
}
void Logger::setLevel(spdlog::level::level_enum logLevel)
{
m_logger->set_level(logLevel);
}
void Logger::setDialog(QDialog *dialog)
{
Log::dialog = dialog;
}
void Logger::qtMessageHandler(QtMsgType type, const QMessageLogContext &/*context*/, const QString &msg)
{
QByteArray local_msg = msg.toLocal8Bit();
QString log_color;
QString log_type;
switch(type)
{
case QtDebugMsg:
log_color = "black";
log_type = "[D]";
m_logger->debug(local_msg.constData());
break;
case QtWarningMsg:
log_color = "orange";
log_type = "[W]";
m_logger->warn(local_msg.constData());
break;
case QtCriticalMsg:
log_color = "red";
log_type = "[E]";
m_logger->critical(local_msg.constData());
break;
case QtFatalMsg:
log_color = "red";
log_type = "[C]";
m_logger->error(local_msg.constData());
break;
case QtInfoMsg:
log_color = "blue";
log_type = "[I] ";
m_logger->info(local_msg.constData());
break;
default:
break;
}
m_logger->flush();
if(!m_logColor.isEmpty()){
log_color = m_logColor;
}
QString log = QString("<font color='%1'>%2 %3</font>").arg(log_color, log_type, msg);
m_logColor.clear();
Log::output.append(log+"<br>");
if(Log::dialog){
// QMetaObject::invokeMethod(Log::dialog, "flush", Qt::QueuedConnection);
QMetaObject::invokeMethod(Log::dialog, "append", Qt::QueuedConnection, Q_ARG(QString, log));
}
}
新建一个测试的Dialog窗体,命名是DebugDialog即可,主要是用来,测试spdlog框架,是否正常使用; 新建一个textedit文本输入
![[Pasted image 20240706170941.png]]
如果需要,只读,勾选这个属性即可;
![[Pasted image 20240729143353.png]]
根节点设置一下宽高
![[Pasted image 20240706170911.png]]
整布局,文本框,就会自动适配了
![[Pasted image 20240706171007.png]]
头文件
#ifndef DEBUGDIALOG_H
#define DEBUGDIALOG_H
#include <QDialog>
namespace Ui {
class DebugDialog;
}
class DebugDialog : public QDialog
{
Q_OBJECT
public:
explicit DebugDialog(QWidget *parent = nullptr);
~DebugDialog();
protected:
void closeEvent(QCloseEvent *event);
private:
void initShortcut();
void findSelectedText(bool up);
QString getSelectedText();
public slots:
void flush();
void append(const QString& log);
private:
Ui::DebugDialog *ui;
};
#endif // DEBUGDIALOG_H
cpp文件
#include "debugdialog.h"
#include "ui_debugdialog.h"
#include "logger.h"
#include <QAction>
#include <QApplication>
#include <QIcon>
#include <QScrollBar>
#include <QMenu>
#include <QDebug>
#include <QInputDialog>
#include <QShortcut>
DebugDialog::DebugDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::DebugDialog)
{
setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint | Qt::WindowMinMaxButtonsHint);
ui->setupUi(this);
this->resize(960, 540);
// this->setWindowIcon(QIcon(QPixmap(":/icon/扫码-电脑-悬停.png")));
this->setWindowTitle("调试日志");
ui->textEdit->setContextMenuPolicy(Qt::NoContextMenu);
Logger::setDialog(this);
connect(qApp, &QApplication::aboutToQuit, this, &DebugDialog::deleteLater);
connect(this, &DebugDialog::rejected, this, &DebugDialog::deleteLater);
this->setShortcutEnabled(true);
initShortcut();
flush();
}
DebugDialog::~DebugDialog()
{
Logger::setDialog(nullptr);
delete ui;
}
void DebugDialog::closeEvent(QCloseEvent *event)
{
Logger::setDialog(nullptr);
QDialog::closeEvent(event);
}
void DebugDialog::initShortcut()
{
QShortcut *search = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F), this);
connect(search, &QShortcut::activated, this, [this](){
// qDebug() << "搜索内容";
QInputDialog dialog(this);
dialog.setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint);
dialog.setInputMode(QInputDialog::TextInput);
dialog.setWindowTitle("查找");
dialog.setOkButtonText("确定");
dialog.setCancelButtonText("取消");
dialog.setLabelText("搜索内容:");
dialog.setTextValue(getSelectedText());
dialog.resize(this->width()/2, dialog.height());
if(dialog.exec() == QDialog::Accepted){
QString text = dialog.textValue();
if(text.isEmpty()) return;
if(!ui->textEdit->find(text)){
ui->textEdit->moveCursor(QTextCursor::Start);
ui->textEdit->find(text);
}
}
});
QShortcut *find_up = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up), this);
connect(find_up, &QShortcut::activated, this, [this](){
findSelectedText(true);
});
QShortcut *find_down = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down), this);
connect(find_down, &QShortcut::activated, this, [this](){
findSelectedText(false);
});
}
void DebugDialog::findSelectedText(bool up) {
QString text = getSelectedText();
if(text.isEmpty()) return;
QTextDocument::FindFlags flags;
flags |= QTextDocument::FindCaseSensitively; // 设置区分大小标志
if (up) {
flags |= QTextDocument::FindBackward; // 设置向后查找标志
}
bool found = ui->textEdit->find(text, flags);
if (!found) {
// 如果没有找到,根据查找方向移动光标并重新查找
QTextCursor::MoveOperation moveOp = up ? QTextCursor::End : QTextCursor::Start;
ui->textEdit->moveCursor(moveOp);
ui->textEdit->find(text, flags);
}
}
QString DebugDialog::getSelectedText()
{
QTextCursor cursor = ui->textEdit->textCursor(); // 获取当前文本光标
if (cursor.hasSelection()) { // 检查是否有选中的文本
QString text = cursor.selectedText(); // 获取选中的文本
// qDebug() << "Selected text:" << text;
return text;
} else {
// qDebug() << "No text is selected";
return "";
}
}
void DebugDialog::flush()
{
QString output = Log::output;
output.chop(4);
ui->textEdit->setHtml(output);
ui->textEdit->verticalScrollBar()->setValue(ui->textEdit->verticalScrollBar()->maximum());
}
void DebugDialog::append(const QString &log)
{
ui->textEdit->append(log);
ui->textEdit->verticalScrollBar()->setValue(ui->textEdit->verticalScrollBar()->maximum());
}
main主函数调用和测试
#include <QDir>
#include <QStandardPaths>
#include "logger.h"
#include "debugdialog.h"
#include <QDebug>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Dialog w;
// w.show();
QDir dir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
Logger::initialize(dir.absoluteFilePath("QiuQiuKit/logs"));
Logger::setLevel(spdlog::level::debug);
Logger::setColor("#F56C6C");
if(!Log::dialog){
DebugDialog *dialog = new DebugDialog;
dialog->setWindowTitle("qiuqiu");
dialog->show();
}else{
if(Log::dialog->isMinimized()){
Log::dialog->showNormal();
}else{
Log::dialog->show();
}
Log::dialog->raise();
}
qDebug().noquote() << "Game.Log:" << QString("testMessage");
return a.exec();
}
![[Pasted image 20240706171156.png]]