c++ 我假定的线程安全的旋转文件记录器只在调试模式下旋转,并且只在发布模式下不会崩溃

xoefb8l8  于 2023-08-09  发布在  其他
关注(0)|答案(1)|浏览(90)

我使用Qt的日志框架编写了一个线程安全的旋转文件日志记录器。当前日志文件(app.log)超过一定大小后,应将其重命名为app_prev.log(如果存在旧的app_prev.log,则覆盖旧的app_prev.log),并应创建一个新的空app.log并写入其中。我将最大日志大小设置为10个字节,以便它立即旋转日志来测试它。当我在发布模式下运行它时,app.log会被重写(正如预期的那样),但app_prev.log保持不变(即我丢失了在旋转之前app.log中的内容,而不是它们在app_prev中)。当我在调试模式下运行它时,它按预期工作-但就在日志写入之后,我得到一个sigpipe(断管)信号,它终止了我的程序...我不知道我做错了什么...
下面是我的代码的最小版本:

#include <QString>
#include <QFile>
#include <iostream>
#include <QMutexLocker>
#include <QMutex>

static constexpr int MAX_LOG_SIZE = 10; //1024*1024*10;  //10MB max log size.
static constexpr auto LOG_NAME = "app.log";
static constexpr auto PREV_LOG_NAME = "app_prev.log";

class LogFile {
public:

    static void write(QString msg) {
        QMutexLocker<QMutex> l(&_m);
        if (!_f.isOpen())
        {
            if (!_f.open(QFile::Append))
            {
                std::cerr << msg.toStdString() << std::endl;
                return;
            }
        }
        _f.write(msg.toUtf8());
        _f.flush();
    }

    static int size() {
        QMutexLocker<QMutex> l(&_m);
        return _f.size();
    }

    static void rollover() {
        QMutexLocker<QMutex> l(&_m);
        _f.close();
        if (QFile::exists(PREV_LOG_NAME))
        {
            if (!QFile::remove(PREV_LOG_NAME))
                qFatal("Faile deleting prev log file.");
        }
        while (QFile::exists(PREV_LOG_NAME)) {
            sleep(1);
        }
        if (!_f.rename(PREV_LOG_NAME))
            qFatal("Failed renaming current log to prev log");
        _f.setFileName(LOG_NAME);
        if (!_f.open(QFile::WriteOnly))
            qFatal("Failed opening log for writing");
    }

private:
    static QFile _f;
    static QMutex _m;
};

QFile LogFile::_f = QFile(LOG_NAME);
QMutex LogFile::_m;

void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{

    auto line = (qFormatLogMessage(type, context, msg) + "\n");
    if (line.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...");
}

字符串

hts6caw3

hts6caw31#

正如上面的注解所暗示的,问题在于是否在非线程安全函数中调用翻转的逻辑。
下面是代码的工作版本:

#include <QString>
#include <QFile>
#include <iostream>
#include <QMutexLocker>
#include <QMutex>

static constexpr int MAX_LOG_SIZE = 10; //1024*1024*10;  //10MB max log size.
static constexpr auto LOG_NAME = "app.log";
static constexpr auto PREV_LOG_NAME = "app_prev.log";

class LogFile {
public:
    static void write(QString msg) {
        QMutexLocker<QMutex> l(&_m);
        if (!_f.isOpen())
        {
            if (!_f.open(QFile::Append))
            {
                std::cerr << msg.toStdString() << std::endl;
                return;
            }
        }
        if (_f.exists() && (line.size() + LogFile::size()) >= MAX_LOG_SIZE)
            LogFile::rollover();
        _f.write(msg.toUtf8());
        _f.flush();
    }

private:
    static void rollover() {
        _f.close();
        if (QFile::exists(PREV_LOG_NAME))
        {
            if (!QFile::remove(PREV_LOG_NAME))
                qFatal("Faile deleting prev log file.");
        }
        while (QFile::exists(PREV_LOG_NAME)) {
            sleep(1);
        }
        if (!_f.rename(PREV_LOG_NAME))
            qFatal("Failed renaming current log to prev log");
        _f.setFileName(LOG_NAME);
        if (!_f.open(QFile::WriteOnly))
            qFatal("Failed opening log for writing");
    }

private:
    static QFile _f;
    static QMutex _m;
};

QFile LogFile::_f = QFile(LOG_NAME);
QMutex LogFile::_m;

void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{

    auto line = (qFormatLogMessage(type, context, msg) + "\n");
    LogFile::write(line);
}

字符串
现在只有一个由互斥锁保护的单一入口点,因此写操作是原子的,从而使竞争条件不可能发生。
我还确保了在重命名之前原始日志已经存在,所以现在在滚动之前至少有一行将被写入日志。

相关问题