You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 10:33:28