目录
三、轮转日志器创建(spdlog::rotating_logger_mt)(输出到文件)
四、彩色日志记录器(spdlog::stdout_color_mt)(输出控制台)
五、自定义日志输出格式(spdlog::set_pattern)
前情提要
spdlog源码生成静态库教程https://blog.csdn.net/Mason___/article/details/157584740?spm=1001.2014.3001.5501
有了静态库我们就要搞到项目里用起来,VS2019使用静态库和动态库
https://blog.csdn.net/Mason___/article/details/157472930?spm=1001.2014.3001.5501
加载完了之后就要着手封装调用静态库接口以及使用,这是本文要讲的,代码放在最下面。
一、日志级别接口
spdlog提供6个标准日志级别(按严重程度递增):
trace:最详细的跟踪信息(如变量值、循环迭代、函数调用流程)。debug:调试阶段信息(如程序状态、分支条件)。info:常规运行信息(如服务启动、配置加载)。warn:潜在问题警告(如磁盘空间不足、无效输入)。error:可恢复的错误(如文件打开失败、网络超时)。critical:不可恢复的严重错误(如内存耗尽、关键组件崩溃)。
典型使用场景
| 级别 | 适用场景 |
|---|---|
trace | 调试复杂算法、跟踪变量变化、分析性能瓶颈。 |
debug | 检查模块初始化、验证中间结果、确认流程分支。 |
info | 记录服务启动/停止、配置变更、关键业务事件。 |
warn | 提示非致命问题(如使用默认配置、临时降级)。 |
error | 记录可恢复的异常(如数据库连接断开后重试)。 |
critical | 触发紧急响应(如数据损坏、核心服务崩溃)。 |
二、输出控制(spdlog::set_level)
-
级别过滤:低于当前设置级别的日志会被忽略。
spdlog::set_level(spdlog::level::warn); // 仅输出warn及以上级别 spdlog::trace("This won't appear"); // 被过滤 spdlog::warn("This will appear"); // 正常输出 -
性能优化:低于设置级别的日志不会触发I/O操作(减少开销)。
-
注意事项:
(1)生产环境建议:
开发阶段:使用debug或trace级别。
生产阶段:使用info或warn级别,减少日志量。
(2)性能影响:
trace()调用本身开销极低(内联函数),但频繁输出可能影响I/O性能。
异步日志(async_logger)可缓解此问题。
(3)格式化支持:
所有级别均支持格式化字符串(如"Value: {}")和占位符(如%l表示级别缩写)。
三、轮转日志器创建(spdlog::rotating_logger_mt)(输出到文件)
1. 功能概述
- 日志轮转(Rotating):当日志文件达到指定大小后,自动切换到新文件,并保留有限数量的历史文件。
- 多线程安全:通过
_mt后缀标识,内部使用锁保证线程安全。 - 典型用途:需要控制日志文件大小且避免磁盘占满的场景(如长期运行的服务)。
2. 构造函数参数
auto logger = spdlog::rotating_logger_mt(
"logger_name", // 日志器名称(用于全局注册)
"logs/rotating.log", // 日志文件路径
1024 * 1024 * 5, // 单个文件最大大小(字节,此处为5MB)
3 // 保留的历史文件数量
);
- 参数说明:
name:日志器唯一标识,可通过spdlog::get(name)获取。filename:当前日志文件路径。max_size:文件轮转的大小阈值(字节)。max_files:保留的历史文件数量(超过则删除最旧的文件)。
3.关键特性
(1) 自动轮转逻辑
当前文件达到 max_size 时:
1.将当前文件重命名为 filename.1,原 .1 改为 .2,依此类推。
2.创建新的 filename 文件继续写入。
3.若历史文件数量超过 max_files,删除最旧的文件(如 .3)。
(2) 线程安全
内部使用 spdlog::details::thread_pool 处理并发写入,适合多线程环境。
(3) 异常处理
若初始化失败(如路径不可写),抛出 spdlog::spdlog_ex 异常:
try {
auto logger = spdlog::rotating_logger_mt("logger", "invalid_path.log", 1024, 3);
} catch (const spdlog::spdlog_ex& ex) {
std::cerr << "Logger init failed: " << ex.what() << std::endl;
}
4.代码示例
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
int main() {
// 创建轮转日志器(5MB/文件,保留3个历史文件)
auto logger = spdlog::rotating_logger_mt("rotating", "logs/app.log", 1024 * 1024 * 5, 3);
// 设置日志级别和格式
logger->set_level(spdlog::level::debug);
logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
// 记录日志
for (int i = 0; i < 10000; ++i) {
logger->info("Log message {}", i);
}
return 0;
}
输出文件结构(日志量超过5MB后):
logs/
├── app.log # 当前日志文件
├── app.log.1 # 第一个历史文件
├── app.log.2 # 第二个历史文件
└── app.log.3 # 第三个历史文件(最旧)
四、彩色日志记录器(spdlog::stdout_color_mt)(输出控制台)
spdlog::stdout_color_mt 是 spdlog 日志库中用于创建多线程安全的彩色控制台日志记录器的函数。
1. 功能概述
- 多线程安全:支持多线程环境下的日志输出。
- 彩色控制台:自动为不同日志级别(如
info、warn、error)添加颜色,提升可读性。 - 跨平台支持:在 Linux/macOS 使用 ANSI 转义码,在 Windows 通过 API 控制台颜色。
2. 函数签名
template<typename Factory = spdlog::synchronous_factory>
std::shared_ptr<spdlog::logger> stdout_color_mt(
const std::string& logger_name,
spdlog::color_mode mode = spdlog::color_mode::automatic
);
参数说明:
1. logger_name:日志记录器的唯一标识,用于后续获取或管理该实例(如通过spdlog::get(logger_name))。
2. mode(可选,默认为 automatic)
color_mode::automatic:自动检测终端是否支持颜色(如重定向到文件时禁用颜色)。
color_mode::always:强制启用颜色,即使终端可能不支持(可能导致乱码)。
color_mode::never:完全禁用颜色。
3.基础用法
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
int main() {
// 创建默认彩色日志记录器(自动检测颜色支持)
auto logger = spdlog::stdout_color_mt("console");
logger->info("Welcome to spdlog!"); // 输出绿色 info 消息
logger->error("Something went wrong"); // 输出红色 error 消息
}
五、自定义日志输出格式(spdlog::set_pattern)
spdlog::set_pattern 是用于自定义日志输出格式的核心函数,通过占位符灵活控制时间、级别、线程ID等信息的显示方式。
1. 常用占位符
| 占位符 | 含义 | 示例 |
|---|---|---|
%Y | 年(4位) | 2024 |
%m | 月(01-12) | 02 |
%d | 日(01-31) | 02 |
%H | 时(00-23) | 14 |
%M | 分(00-59) | 30 |
%S | 秒(00-59) | 45 |
%e | 毫秒(3位) | 123 |
%l | 日志级别(小写) | info |
%L | 日志级别(大写) | INFO |
%t | 线程ID | 1234 |
%s | 源文件名 | main.cpp |
%# | 行号 | 42 |
%v | 日志内容 | This is a message |
%^ | 级别颜色开始(需配合%$) | 彩色日志(如红色error) |
%$ | 级别颜色结束 | 恢复默认颜色 |
2. 默认格式
// 设置全局日志格式(所有日志器默认使用此格式)
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");
spdlog::info("This is an info message");
// 输出示例:[2024-02-02 14:30:45.123] [info] [1234] This is an info message
3. 彩色日志
// 设置包含颜色的格式(需包含stdout_color_sink)
spdlog::set_pattern("[%^%l%$] %v");
spdlog::info("This is colored info"); // 默认绿色
spdlog::error("This is colored error"); // 默认红色
4.支持动态调整格式和多日志不同格式
auto logger = spdlog::stdout_color_mt("console");
logger->set_pattern("[%H:%M:%S] %v"); // 简洁格式
logger->info("Simple message");
logger->set_pattern("[%Y-%m-%d] [%l] %v"); // 详细格式
logger->warn("Detailed warning");
// 创建两个日志器,分别设置不同格式
auto file_logger = spdlog::basic_logger_mt("file", "app.log");
file_logger->set_pattern("[%Y-%m-%d %H:%M:%S] %v");
auto console_logger = spdlog::stdout_color_mt("console");
console_logger->set_pattern("[%^%l%$] %v");
file_logger->info("This goes to file"); // 无颜色,含时间戳
console_logger->info("This goes to console"); // 彩色,无时间戳
六、刷新策略控制(flush_on)
flush_on 是 spdlog 中用于控制日志刷新策略的核心函数,其作用是指定在特定日志级别及以上时强制刷新缓冲区,确保日志立即写入目标(如文件/控制台),避免数据滞留。
1. 核心作用
- 默认行为:spdlog 为提升性能,会先将日志存入内存缓冲区,按条件(如缓冲区满、程序退出)批量写入。
flush_on的功能:强制在记录指定级别及以上日志时,立即刷新缓冲区,确保日志实时落地。
2. 函数签名
void flush_on(spdlog::level::level_enum log_level);
- 参数:
log_level(如spdlog::level::warn、spdlog::level::error)。 - 效果:当记录
log_level或更高级别日志时,缓冲区会被刷新。
3.典型使用场景
场景1:确保错误日志立即写入
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
int main() {
auto logger = spdlog::basic_logger_mt("file_logger", "app.log");
logger->set_level(spdlog::level::debug); // 设置全局日志级别
logger->flush_on(spdlog::level::error); // 仅在记录 error 及以上时刷新
logger->info("This info may stay in buffer"); // 不触发刷新
logger->error("This error will flush immediately"); // 触发刷新,info 也会被写入
}
场景2:调试时实时查看日志
auto debug_logger = spdlog::stdout_color_mt("debug");
debug_logger->flush_on(spdlog::level::debug); // 所有 debug 及以上日志立即显示
debug_logger->debug("Real-time debug message"); // 立即输出到控制台
4. 与 flush_every 的对比
| 函数 | 触发条件 | 适用场景 |
|---|---|---|
flush_on | 记录指定级别及以上日志时 | 关键日志(如错误)需实时写入 |
flush_every | 固定时间间隔(如每3秒) | 非关键日志的定期刷新 |
示例:组合使用
logger->flush_on(spdlog::level::error); // 错误日志立即刷新
spdlog::flush_every(std::chrono::seconds(5)); // 其他日志每5秒刷新一次
5.底层实现原理
- 缓冲区管理:spdlog 内部维护一个日志消息队列,
flush_on会修改队列的刷新阈值。 - 线程安全:在多线程环境下,
flush_on的设置和触发均通过锁保护,确保安全。
6. 注意事项
- 性能权衡:频繁刷新(如
flush_on(spdlog::level::debug))会降低性能,建议仅对关键级别使用。 - 与
set_level的区别:set_level控制哪些日志被记录。flush_on控制已记录的日志何时写入目标。
作者封装和使用spdlog静态库接口代码示例如下:
BDLog.h
#pragma once
#include "plccore_global.h"
#include <string>
PLCCORE_EXPORT void InitLogModule(const QString& strLogPath, int level = 0);
PLCCORE_EXPORT void BDLogTrace(const std::string& msg);
PLCCORE_EXPORT void BDLogDebug(const std::string& msg);
PLCCORE_EXPORT void BDLogInfo(const std::string& msg);
PLCCORE_EXPORT void BDLogWarn(const std::string& msg);
PLCCORE_EXPORT void BDLogError(const std::string& msg);
PLCCORE_EXPORT void BDLogCritical(const std::string& msg);
BDLog.cpp
#include "BDLog.h"
#include <memory>
#include <iostream>
#include <QString>
#include <QTextCodec>
#include <QDir>
#include <algorithm>
#include "../spdlog-1.x/include/spdlog/spdlog.h"
#include "../spdlog-1.x/include/spdlog/common.h"
#include "../spdlog-1.x/include/spdlog/sinks/rotating_file_sink.h"
std::shared_ptr<spdlog::logger> mylogger = nullptr;
std::string ConvertPath(const QString& strPath)
{
auto path = QDir::toNativeSeparators(strPath);
auto code = QTextCodec::codecForName("GB2312");
std::string retpath = code->fromUnicode(path.toStdString().c_str()).data();
return std::move(retpath);
}
void InitLogModule(const QString& strLogPath, int level/* = 0*/)
{
try
{
QString path = strLogPath.left(strLogPath.lastIndexOf(QLatin1Char('/')));
QDir dir(path);
if (!dir.exists()){
return;
}
QStringList filters;
filters << QString("*.log");
dir.setFilter(QDir::Files | QDir::NoSymLinks);
dir.setNameFilters(filters);
auto fileList = dir.entryInfoList();
const size_t keep_count = 5;
if (fileList.size() > keep_count) {
std::partial_sort(fileList.begin(), fileList.begin() + keep_count, fileList.end(),
[&](const QFileInfoList::value_type& _left, const QFileInfoList::value_type& _right) {
return _left.fileName() > _right.fileName();
});
for (auto it = fileList.begin() + keep_count; it != fileList.end(); ++it) {
QFile file(it->absoluteFilePath());
if (file.exists()) {
file.remove();
}
}
}
// 参数1 日志标识符, 参数2 日志文件名
mylogger = spdlog::rotating_logger_mt("Log", ConvertPath(strLogPath).c_str(), 1024 * 1024 * 5, 10);
// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]
mylogger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");
mylogger->set_level((spdlog::level::level_enum)(level));
mylogger->flush_on(spdlog::level::trace);
}
catch (const spdlog::spdlog_ex& ex)
{
std::cout << "Log initialization failed: " << ex.what() << std::endl;
}
}
void BDLogTrace(const std::string& msg)
{
if (mylogger) {
mylogger->trace(msg);
}
}
void BDLogDebug(const std::string& msg)
{
if (mylogger) {
mylogger->debug(msg);
}
}
void BDLogInfo(const std::string& msg)
{
if (mylogger) {
mylogger->info(msg);
}
}
void BDLogWarn(const std::string& msg)
{
if (mylogger) {
mylogger->warn(msg);
}
}
void BDLogError(const std::string& msg)
{
if (mylogger) {
mylogger->error(msg);
}
}
void BDLogCritical(const std::string& msg)
{
if (mylogger) {
mylogger->critical(msg);
}
}
3177

被折叠的 条评论
为什么被折叠?



