我使用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...");
}
字符串
1条答案
按热度按时间hts6caw31#
正如上面的注解所暗示的,问题在于是否在非线程安全函数中调用翻转的逻辑。
下面是代码的工作版本:
字符串
现在只有一个由互斥锁保护的单一入口点,因此写操作是原子的,从而使竞争条件不可能发生。
我还确保了在重命名之前原始日志已经存在,所以现在在滚动之前至少有一行将被写入日志。