防止qDebug()写入std输出

时间:2018-06-15 12:12:14

标签: c++ qt qdebug

我正在使用qDebug(),qInfo()等结合qInstallMessageHandler来编写我的日志文件。在执行我的应用程序时,我还在批处理屏幕上获得输出。

我只找到了QT_NO_DEBUG_OUTPUT,但我希望在运行时切换此配置。有没有办法阻止Qt写入std输出?

格尔茨

丹尼尔

1 个答案:

答案 0 :(得分:2)

  

遗憾的是,您只能访问这些消息,但无法防止将其写入标准输出。

至少在Qt 5中这是错误的。消息打印代码looks as follows:您可以清楚地看到只使用消息处理程序:

if (grabMessageHandler()) {
    // prefer new message handler over the old one
    if (msgHandler.load() == qDefaultMsgHandler
            || messageHandler.load() != qDefaultMessageHandler) {
        (*messageHandler.load())(msgType, context, message);
    } else {
        (*msgHandler.load())(msgType, message.toLocal8Bit().constData());
    }
    ungrabMessageHandler();
} else {
    fprintf(stderr, "%s\n", message.toLocal8Bit().constData());
}

正如您所看到的,fprintf(stderr, ...)只有在messageHandler本身内检测到递归时才会回退。

因此,您需要阻止任何调试输出的是实现和设置自己的messageHandler

要完全关闭Qt中的所有调试输出,以下操作:

#include <QtCore>
int main() {
  qDebug() << "I'm not quiet at all yet";
  qInstallMessageHandler(+[](QtMsgType, const QMessageLogContext &, const QString &){});
  qDebug() << "I'm very, very quiet";
}

在任何情况下,应用程序范围的文件记录器的合理实现可能如下所示 - 它不会不必要地重新创建QTextStream;它使用QString::toUtf8()代替,并明确写出行结尾。

#include <QtCore>

class Logger {
   static struct Data {
      Logger *instance;
      QtMessageHandler chainedHandler;
   } d;
   bool m_isOpen;
   QFile m_logFile;
   QtMessageHandler m_oldHandler = {};
   static void handler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
      if (d.instance)
         d.instance->log(msg);
      if (d.chainedHandler)
         d.chainedHandler(type, context, msg);
   }
public:
   enum ChainMode { DontChain, Chain };
   Logger() {
      Q_ASSERT(!instance());
      m_logFile.setFileName("myLog.txt");
      m_isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
      d.instance = this;
   }
   ~Logger() { uninstallHandler(); }
   bool isOpen() const { return m_isOpen; }
   void installHandler(ChainMode mode) {
      Q_ASSERT(!m_oldHandler);
      m_oldHandler = qInstallMessageHandler(handler);
      if (mode == Chain)
         d.chainedHandler = m_oldHandler;
   }
   void uninstallHandler() {
      if (m_oldHandler) {
         m_oldHandler = nullptr;
         d.chainedHandler = nullptr;
         qInstallMessageHandler(m_oldHandler);
      }
   }
   /// This method is *not* thread-safe. Use with a thread-safe wrapper such as `qDebug`.
   void log(const QString & msg) {
      if (isOpen()) {
         m_logFile.write(msg.toUtf8());
         m_logFile.write("\n", 1);
      }
   }
   /// Closes the log file early - this is mostly used for testing.
   void endLog() {
      uninstallHandler();
      m_logFile.close();
   }
   static Logger *instance() { return d.instance; }
};

Logger::Data Logger::d;

template <typename T> QByteArray streamOutputFor(const T &data) {
   QBuffer buf;
   buf.open(QIODevice::ReadWrite | QIODevice::Text);
   QTextStream s(&buf);
   s << data << endl;
   buf.close();
   return buf.data();
}

QByteArray readEnd(const QString &fileName, int count) {
   QFile file(fileName);
   if (file.open(QIODevice::ReadOnly | QIODevice::Text) && file.size() >= count) {
      file.seek(file.size() - count);
      return file.readAll();
   }
   return {};
}

void test() {
   auto const entry = QDateTime::currentDateTime().toString().toUtf8();
   Q_ASSERT(Logger::instance()->isOpen());
   qDebug() << entry.data();
   Logger::instance()->endLog();

   auto reference = streamOutputFor(entry.data());
   auto readback = readEnd("myLog.txt", reference.size());
   Q_ASSERT(!reference.isEmpty());
   Q_ASSERT(readback == reference);
}

int main() {
   Logger logger;
   logger.installHandler(Logger::DontChain);
   qDebug() << "Something or else";
   test();
}