日志基本控制日志等级怎么确定一条日志的等级主观记录注重为什么记录这条日志有明确的目的客观记录注重记什么为什么在前说明偏主观记什么在前说明偏客观trace记什么记录基于技术实现的步骤信息通常偏重记录正确路径为什么清楚当前代码的工作机制总有一天自己也会忘记这代码是干什么的debug为什么为了抓住那个 bug记什么为了找出错误想怎么记就怎么记问题解决后它们都会被删除info记什么记录基于业务的过程信息里程碑关键节点状态等为什么帮助发现系统是否正常同时延缓维护人员退化到必须看trace的时刻warn为什么系统的每一笔业务都还能正确执行但是出现反常信息记什么不影响系统每一笔业务正确执行的反常信息err为什么某一笔业务运行出错了记什么业务执行出错信息critical记什么能让系统某些功能直接罢工的事为什么这个错误要是出现系统必然出现大量错误甚至崩溃两级控制记录器拥有一个控制级别通过logger-level()查看。如果要记录的日志级别低于设置的级别这条日志就会被抛弃如果大于等于设置的级别日志会进入后续的槽。每个槽都有独立的最小级别控制Sink-level()规则和记录器一致。记录器和槽都可以通过level()查看日志级别set_level()修改日志级别默认规则新建的槽默认使用最低级别 (trace)。如果设置set_level(level::off)相当于关闭该记录器 / 槽拦截所有日志。工具函数level::to_string_view()得到级别枚举值名称level::to_short_c_str()得到大写单字母名称如err会得到E动态控制优点支持程序运行时调整日志级别缺点不输出的日志仍然会耗费一定资源使用规范把sink加入logger之前先设置它的级别默认最低级别trace用常量定义sink的加入顺序后续如确实需要调整级别使用该常量为下标sink加入logger后如需关闭其输出使用set_level(off)而非删除//日志级别调整 void func6() { int const IDX_CONSOLE_SINK 0;//控制台槽的下标 int const IDX_FILE_SINK 1;//文件槽的下标 //使用工厂方法创建全新的日志记录器(默认自带一个对应功能的槽) auto levelsLogger spdlog::stdout_color_mt(LevelsLogger); //创建文件槽 auto fileSink std::make_sharedspdlog::sinks::basic_file_sink_mt(log/levels.txt); //修改文件槽的级别 fileSink-set_level(spdlog::level::warn); //加入到记录器 levelsLogger-sinks().push_back(fileSink); //查看记录器的级别 levelsLogger-info(LevelsLogger 记录器级别 :{},spdlog::level::to_string_view(levelsLogger-level())); //查看各个槽的级别 for(auto sink:levelsLogger-sinks()) { levelsLogger-info(spdlog::level::to_short_c_str(sink-level())); } levelsLogger-info(this is info ); levelsLogger-debug(this is debug); //调整记录器的级别 levelsLogger-set_level(spdlog::level::debug); levelsLogger-debug(本记录器等级已经调整为debug); levelsLogger-warn(debug 和 info 级别日志不会输出到文件); levelsLogger-sinks()[IDX_FILE_SINK]-set_level(spdlog::level::debug); levelsLogger-debug(文件也能看到bug了); }//levels.txt内容 [2026-05-21 09:33:25.329] [LevelsLogger] [warning] debug 和 info 级别日志不会输出到文件 [2026-05-21 09:33:25.329] [LevelsLogger] [debug] 文件也能看到bug了日志格式控制学习 spdlog 中最简单的格式控制模式匹配1.使用{}作为占位符简单好用spdlog::info(服务器{}:{}开始监听,1.1.1.1,8080); //[2026-01-01 00:01:11:111][info] 服务器1.1.1.1:8080开始监听2.自定义输出内容匹配模式(pattern)logger 或 sink -set_pattern(格式指定串); 例logger-set_pattern([%Y年%m月%d日 %H:%M:%S]- 【%l】 %n::%^%v%$);格式符说明%Y-%m-%d %H:%M:%S日期和时间%l日志级别%^ %$设置颜色仅对控制台有效spdlog 1.15.2 版本仅支持使用一次%n记录器名称建议使用业务 / 层次名称%v原始日志内容//修改Pattern void func7() { spdlog::info(原有格式); spdlog::info(服务器已经在{}:{}开始监听,36.2.2.16,8080); spdlog::info(开始修改 Pattern); //创建带颜色的控制台日志记录器 auto mainLogger spdlog::stdout_color_mt(主站); //自定义格式 mainLogger-set_pattern([%Y年%m月%d日 %H:%M:%S]-%^ 【%l】 %n::%v%$); //创建文件sink auto fileSink std::make_sharedspdlog::sinks::basic_file_sink_mt(log/pattern.txt); fileSink-set_pattern([%Y-%m-%d %H:%M:%S] %l [%n] %v); mainLogger-sinks().push_back(fileSink); //调整为最低级别 mainLogger-set_level(spdlog::level::trace); //替换全局默认记录器 spdlog::set_default_logger(mainLogger); spdlog::info(自定义格式起作用了); spdlog::info(服务器已经在{}:{}开始监听,36.2.2.16,8080); spdlog::warn(这是一个warn); spdlog::error(服务器无法连接数据库); spdlog::critical(严重错误); spdlog::trace(这是一个trace); }//pattern.txt内容 [2026-05-21 11:31:51] info [主站] 自定义格式起作用了 [2026-05-21 11:31:51] info [主站] 服务器已经在36.2.2.16:8080开始监听 [2026-05-21 11:31:51] warning [主站] 这是一个warn [2026-05-21 11:31:51] error [主站] 服务器无法连接数据库 [2026-05-21 11:31:51] critical [主站] 严重错误 [2026-05-21 11:31:51] trace [主站] 这是一个trace异步记录器同步日志同步≠立即写入虽是同步模式目标存在缓冲区未灌满时日志未落地仍可能丢失业务线程阻塞同步模式下必须等待日志写入完成才能继续执行业务逻辑。由于缓冲区的存在同步日志的性能足以支撑大多数业务系统。重要日志可使用flush()强制刷新缓冲区spdlog::flush_every(时长)定期强制刷新内部创建线程编译需加-pthread//flush缓冲区 void func8() { auto fileSink std::make_sharedspdlog::sinks::basic_file_sink_mt(log/flush.txt); spdlog::default_logger()-sinks().push_back(fileSink); spdlog::info(写入日志对比屏幕输出flush.txt内容); spdlog::default_logger()-flush(); spdlog::info(已经强制刷新日志缓冲区); }//flush_every缓冲区 void func9() { //每三秒强制清空一次 spdlog::flush_every(std::chrono::seconds(3)); auto fileSink std::make_sharedspdlog::sinks::basic_file_sink_mt(log/flush_every.txt); spdlog::default_logger()-sinks().push_back(fileSink); spdlog::info(写入日志对比屏幕输出flush_every.txt内容并等待3秒); spdlog::info(写入日志对比屏幕输出flush_every.txt内容并等待2秒); spdlog::info(写入日志对比屏幕输出flush_every.txt内容并等待1秒); }异步日志机制主线程将日志任务放入队列交由后台工作线程异步执行支持多线程处理。可配置线程数、队列上限、溢出策略。#include spdlog/async.h // step 1: 初始化异步线程池队列大小、后台线程数 spdlog::init_thread_pool(8192, 1); // 参数异步任务队列上限(8K条)线程数(1~1000) // step 2: 定义异步工厂指定溢出策略 using factory spdlog::async_factory_implspdlog::async_overflow_policy::block; // step 3: 创建异步日志记录器 auto asyncColorLogger spdlog::stdout_color_mtfactory(惟一名称);任务溢出策略策略行为说明常用程度block阻塞等待直到队列有空位会卡业务线程常用discard_new直接丢弃当前最新的日志相对少用overrun_oldest保留新日志丢弃队列中最旧的日志相对多用预定义工厂直接使用无需自定义async_factory对应block策略async_factory_nonblock对应overrun_oldest策略//异步日志记录器 void func10() { spdlog::init_thread_pool(1000,1); //创建带颜色的异步控制台记录器 auto asyncColorLogger spdlog::stdout_color_mtspdlog::async_factory(AsyncLogger); //创建文件槽 auto fileSink std::make_sharedspdlog::sinks::basic_file_sink_mt(log/async_file.txt); asyncColorLogger-sinks().push_back(fileSink); spdlog::set_default_logger(asyncColorLogger); spdlog::info(异步记录器它有{}个槽,spdlog::default_logger()-sinks().size()); }//async_file.txt内容 [2026-05-21 14:11:16.053] [AsyncLogger] [info] 异步记录器它有2个槽异步 vs 同步 优缺点异步日志的短板① 日志记录延迟更大线程切换 队列调度② 程序意外退出时丢失日志更多队列 系统缓冲区③ 占用更多内存存储队列中的日志任务④ 参数配置更复杂队列大小 / 线程数 / 溢出策略。异步日志的优势高并发场景下对业务线程的性能影响降低10 倍。(原笔记GameServer-Learning/00-Notes/C/spdlog at main · maomianbaobumoyu/GameServer-Learning)