Qt线程安全滚动日志器异常:Debug崩溃Release不滚动
Qt滚动文件日志器问题修复
问题分析
你的日志器存在两个核心问题:
- Release模式下旧日志丢失:
- 日志大小判断错误:用字符串字符数而非实际写入的UTF-8字节数计算,导致滚动时机不准确;
- 重命名逻辑错误:使用已关闭的QFile对象调用
rename,在Release模式下文件系统缓存可能导致操作失效,应直接用静态QFile::rename方法; - 不必要的文件删除等待循环,不仅跨平台不兼容,还可能导致逻辑阻塞。
- Debug模式下SIGPIPE崩溃:
- 日志处理器中调用
qFatal会递归触发自定义日志处理逻辑,引发资源冲突或死循环,最终导致SIGPIPE信号; - 部分文件操作失败后的错误处理方式不当,加剧了递归调用问题。
- 日志处理器中调用
修复后的代码
#include <QString> #include <QFile> #include <iostream> #include <QMutexLocker> #include <QMutex> #include <QThread> static constexpr int MAX_LOG_SIZE = 10; // 测试用10字节,实际可改为10*1024*1024 static constexpr auto LOG_NAME = "app.log"; static constexpr auto PREV_LOG_NAME = "app_prev.log"; class LogFile { public: static void write(const QString& msg) { QMutexLocker<QMutex> l(&_m); if (!_f.isOpen()) { if (!_f.open(QFile::Append | QFile::Text)) { std::cerr << "Failed to open log file: " << msg.toStdString() << std::endl; return; } } QByteArray data = msg.toUtf8(); _f.write(data); _f.flush(); } static qint64 size() { QMutexLocker<QMutex> l(&_m); return _f.size(); } static void rollover() { QMutexLocker<QMutex> l(&_m); if (_f.isOpen()) { _f.close(); } // 删除旧的prev日志 if (QFile::exists(PREV_LOG_NAME)) { if (!QFile::remove(PREV_LOG_NAME)) { std::cerr << "Failed deleting prev log file." << std::endl; return; } } // 用静态方法重命名当前日志,避免已关闭对象的操作问题 if (!QFile::rename(LOG_NAME, PREV_LOG_NAME)) { std::cerr << "Failed renaming current log to prev log" << std::endl; return; } // 重新初始化QFile对象并打开新日志 _f.setFileName(LOG_NAME); if (!_f.open(QFile::WriteOnly | QFile::Text)) { std::cerr << "Failed opening new log file for writing" << std::endl; } } private: static QFile _f; static QMutex _m; }; QFile LogFile::_f(LOG_NAME); QMutex LogFile::_m; void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { QString line = qFormatLogMessage(type, context, msg) + "\n"; QByteArray lineBytes = line.toUtf8(); // 用实际字节数判断是否需要滚动 if (lineBytes.size() + LogFile::size() >= MAX_LOG_SIZE) { LogFile::rollover(); } LogFile::write(line); } #include <QApplication> int main(int argc, char* argv[]) { qSetMessagePattern("[%{time yyyy-MM-dd h:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}"); qInstallMessageHandler(myMessageOutput); QApplication a(argc, argv); // 多写几条日志测试滚动逻辑 qInfo("Testing 1..."); qInfo("Testing 2..."); qInfo("Testing 3..."); return a.exec(); }
关键修改点
- 日志大小计算:改用
QByteArray::size()获取实际写入的字节数,避免字符数与字节数不匹配导致的滚动时机错误; - 重命名操作:使用
QFile::rename(LOG_NAME, PREV_LOG_NAME)静态方法替代已关闭QFile对象的成员方法,确保文件系统操作可靠; - 错误处理:移除
qFatal调用,改用std::cerr输出错误,避免递归触发日志处理器; - 跨平台兼容:移除POSIX的
sleep(1),改用Qt原生API(此处已移除不必要的等待逻辑); - 文件打开模式:明确添加
QFile::Text模式确保换行符跨平台兼容,滚动后用WriteOnly模式创建空日志文件。
内容的提问来源于stack exchange,提问作者feature_engineer
相关产品推荐
相关产品推荐

