Windows下boost::log彩色输出偏移问题求稳定修复方案
修复Windows下boost::log多日志源彩色输出错位的稳定方案
我之前在做Windows下的boost::log彩色输出时,也踩过一模一样的坑!用Win32 API直接改控制台颜色,在多日志源或者并发场景下,哪怕已经把sink设成同步模式,还是会出现颜色错位、部分行颜色不对的问题——核心原因就是设置颜色和输出日志这两个操作不是原子的,多个线程同时操作控制台时,颜色设置会被互相干扰。下面是我验证过的稳定修复方法:
核心思路
要保证「设置颜色→输出日志→恢复原颜色」这一整套操作是原子执行的,避免多个线程的控制台操作互相穿插。单纯依赖boost::log sink的同步机制不够,因为sink的同步只保证日志记录的处理顺序,但控制台是全局资源,需要额外的全局锁来保护整个输出流程。
具体实现步骤
1. 定义全局互斥锁
用来保护控制台的颜色设置和输出操作,确保同一时间只有一个线程在操作:
#include <mutex> #include <windows.h> // 全局互斥锁,保护控制台操作 std::mutex g_console_output_mutex;
2. 自定义带原子颜色控制的sink格式化器
修改你的console sink,在格式化器里包裹锁,把颜色设置、日志输出、颜色恢复做成原子操作:
#include <boost/log/core.hpp> #include <boost/log/sinks/sync_frontend.hpp> #include <boost/log/sinks/text_ostream_backend.hpp> #include <boost/log/trivial.hpp> #include <boost/log/expressions.hpp> namespace logging = boost::log; namespace sinks = boost::log::sinks; namespace expr = boost::log::expressions; void setup_colorful_logging() { // 创建同步控制台sink auto sink = boost::make_shared<sinks::synchronous_sink<sinks::basic_text_ostream_backend<char>>>(); sink->locked_backend()->add_stream(boost::shared_ptr<std::ostream>(&std::cout, boost::null_deleter())); // 获取标准输出控制台句柄 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) { // 处理句柄获取失败的情况 return; } // 设置格式化器,包含原子颜色控制 sink->set_formatter([hConsole](const logging::record_view& rec, logging::formatting_ostream& strm) { CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(hConsole, &csbi); const WORD original_color = csbi.wAttributes; // 保存原控制台颜色 // 根据日志级别设置目标颜色 WORD target_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; // 默认白色 auto severity = rec[logging::trivial::severity]; if (severity == logging::trivial::error) { target_color = FOREGROUND_RED | FOREGROUND_INTENSITY; // 亮红色 } else if (severity == logging::trivial::warning) { target_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // 亮黄色 } // 加锁,保证颜色设置和输出的原子性 std::lock_guard<std::mutex> lock(g_console_output_mutex); SetConsoleTextAttribute(hConsole, target_color); strm << rec[expr::smessage]; // 输出日志内容 SetConsoleTextAttribute(hConsole, original_color); // 恢复原颜色 }); // 将sink添加到日志核心 logging::core::get()->add_sink(sink); }
关键细节说明
- 全局锁的必要性:哪怕是同步sink,boost::log的同步只保证sink内部的日志处理顺序,但控制台是全局共享资源,多个sink或多线程操作时必须用全局锁来避免交叉操作。
- 恢复原颜色:每次输出后必须恢复控制台的原始颜色,否则后续非日志的控制台输出(比如用户输入提示)会继承日志的颜色,导致混乱。
- 兼容性:这种方案不依赖ANSI转义码,兼容所有Windows版本(包括Win7及以上),不需要开启控制台的ANSI支持。
额外优化(可选)
如果你的日志有复杂的格式(比如包含时间、线程ID等),可以把所有格式化操作都放在锁内执行,确保整个日志行的输出是连续的,避免被其他线程打断。
内容的提问来源于stack exchange,提问作者Vojtek
相关产品推荐
相关产品推荐

