Qt5:如何使用qDebug()登录一个文件,多线程应用
Qt5: How to use qDebug() to log in a file, multi-thread application
我几天前开始使用 Qt5。我的应用程序需要一个 logger,我决定使用 qDebug
,但似乎必须是 "redirected" 才能将日志保存在文件中。
我使用 qInstallMessageHandler
来做到这一点,并且我编写了自己的处理程序,如下所示(灵感来自这里的其他人)。
好像还行,但我不是大牛,请问:
是否可以在多线程应用程序中使用它?
此外,如果它是 ok/safe 用于 多线程 应用程序,是否可以以某种方式对其进行改进?
void MessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
mutex.lock();
QDateTime dateTime(QDateTime::currentDateTime());
QString timeStr(dateTime.toString("dd-MM-yyyy HH:mm:ss:zzz"));
QString contextString(QString("(%1, %2)").arg(context.file).arg(context.line));
QFile outFile("file.log");
outFile.open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream stream(&outFile);
stream << timeStr << " " << contextString << ": " << msg << endl;
mutex.unlock();
}
您在 Qt 文档中找不到任何地方表明 qDebug
是线程安全的。所以从多个线程同时调用它是不安全的,如果你不使用锁定机制,你确实会遇到混合输出。
如果您使用 QMutexLocker
,您的锁定方法会更好,因为它是 Qt 文档强烈推荐的:
void MessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
QMutexLocker locker(&mutex);
...
}
第二种方法是提供一个 worker class,它有用于写入日志的槽。然后,您可以在新线程中拥有它的一个实例,并使用 QMetaObject::invokeMethod
和 Qt::QueuedConnection
连接类型在消息处理程序中调用它的槽。这样,来自每个线程的每个调用都会在工作线程中排队和处理,并且它可能会有更好的性能,因为所有工作都在一个单独的线程中完成。
您的方法看起来简单明了。我会一直这样。
有一件事您可以改进:在应用程序启动时只打开文件一次,并在关闭应用程序时将其关闭。打开文件是一项昂贵的操作。
您可以从多个线程写入同一个打开的文件,因为您的互斥体确保只有一个线程同时写入。
我看到没有回复您对迟钝的担忧,我相信这时候您已经想通了。您可能可以通过减少关键区域内的代码量来提高性能。文件操作很慢,所以尽量减少花在这些操作上的时间。这可以包括保持文件打开、缓冲日志并定期写入、在锁定区域之外构建条目以及
void MessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
QDateTime dateTime(QDateTime::currentDateTime());
QString timeStr(dateTime.toString("dd-MM-yyyy HH:mm:ss:zzz"));
QString contextString(QString("(%1, %2)").arg(context.file).arg(context.line));
QString entryString("%1 %2: %3");
entryString = entryString.arg(timeStr).arg(contextString).arg(msg);
QFile outFile("file.log");
mutex.lock();
outFile.open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream stream(&outFile);
stream << entryString << endl;
mutex.unlock();
}
我几天前开始使用 Qt5。我的应用程序需要一个 logger,我决定使用 qDebug
,但似乎必须是 "redirected" 才能将日志保存在文件中。
我使用 qInstallMessageHandler
来做到这一点,并且我编写了自己的处理程序,如下所示(灵感来自这里的其他人)。
好像还行,但我不是大牛,请问: 是否可以在多线程应用程序中使用它?
此外,如果它是 ok/safe 用于 多线程 应用程序,是否可以以某种方式对其进行改进?
void MessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
mutex.lock();
QDateTime dateTime(QDateTime::currentDateTime());
QString timeStr(dateTime.toString("dd-MM-yyyy HH:mm:ss:zzz"));
QString contextString(QString("(%1, %2)").arg(context.file).arg(context.line));
QFile outFile("file.log");
outFile.open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream stream(&outFile);
stream << timeStr << " " << contextString << ": " << msg << endl;
mutex.unlock();
}
您在 Qt 文档中找不到任何地方表明 qDebug
是线程安全的。所以从多个线程同时调用它是不安全的,如果你不使用锁定机制,你确实会遇到混合输出。
如果您使用 QMutexLocker
,您的锁定方法会更好,因为它是 Qt 文档强烈推荐的:
void MessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
QMutexLocker locker(&mutex);
...
}
第二种方法是提供一个 worker class,它有用于写入日志的槽。然后,您可以在新线程中拥有它的一个实例,并使用 QMetaObject::invokeMethod
和 Qt::QueuedConnection
连接类型在消息处理程序中调用它的槽。这样,来自每个线程的每个调用都会在工作线程中排队和处理,并且它可能会有更好的性能,因为所有工作都在一个单独的线程中完成。
您的方法看起来简单明了。我会一直这样。
有一件事您可以改进:在应用程序启动时只打开文件一次,并在关闭应用程序时将其关闭。打开文件是一项昂贵的操作。
您可以从多个线程写入同一个打开的文件,因为您的互斥体确保只有一个线程同时写入。
我看到没有回复您对迟钝的担忧,我相信这时候您已经想通了。您可能可以通过减少关键区域内的代码量来提高性能。文件操作很慢,所以尽量减少花在这些操作上的时间。这可以包括保持文件打开、缓冲日志并定期写入、在锁定区域之外构建条目以及
void MessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
QDateTime dateTime(QDateTime::currentDateTime());
QString timeStr(dateTime.toString("dd-MM-yyyy HH:mm:ss:zzz"));
QString contextString(QString("(%1, %2)").arg(context.file).arg(context.line));
QString entryString("%1 %2: %3");
entryString = entryString.arg(timeStr).arg(contextString).arg(msg);
QFile outFile("file.log");
mutex.lock();
outFile.open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream stream(&outFile);
stream << entryString << endl;
mutex.unlock();
}