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::invokeMethodQt::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();
}