qDebug、qWarning、qCriticalなどの出力をリダイレクトする方法は?


84

qDebug() <<デバッグ出力に多くのステートメントを使用しています。シェルスクリプトに頼らずに、そのデバッグ出力をファイルにリダイレクトできるクロスプラットフォームの方法はありますか?私は推測しているオープン()dup2の()は、 Linuxの中で仕事をするだろうが、それはWindowsでのMinGWでコンパイルした動作しますか?

そして多分それをするQtの方法がありますか?

回答:


120

qInstallMsgHandler関数を使用してメッセージハンドラーをインストールする必要があります。その後、を使用QTextStreamしてデバッグメッセージをファイルに書き込むことができます。サンプル例を次に示します。

#include <QtGlobal>
#include <stdio.h>
#include <stdlib.h>

void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    QByteArray localMsg = msg.toLocal8Bit();
    switch (type) {
    case QtDebugMsg:
        fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
        break;
    case QtInfoMsg:
        fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
        break;
    case QtWarningMsg:
        fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
        break;
    case QtCriticalMsg:
        fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
        break;
    case QtFatalMsg:
        fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
        abort();
    }
}

int main(int argc, char **argv)
{
    qInstallMessageHandler(myMessageOutput); // Install the handler
    QApplication app(argc, argv);
    ...
    return app.exec();
}

qInstallMsgHandler(コメントを追加しただけです)のドキュメントから引用:

上記の例では、関数myMessageOutputstderrこれを使用して、他のファイルストリームに置き換えるか、関数を完全に書き直します。

あなたが書くと、この機能をインストールすると、すべてのqDebug(だけでなく、などqWarningqCriticalなど)のメッセージは、ハンドラ内に、あなたしている書き込みファイルにリダイレクトされます。


3
ねえ、どうもありがとう。デバッグ出力をファイルにリダイレクトできるだけでなく、タイムスタンプなどのより有用な情報を印刷することもできます:)
セプタグラム2011

2
@Septagram:その通りです。ハンドル自体にいくつかの便利なメッセージを追加できます。そして、あなたが使用しているものに基づいて異なるファイルに出力しても異なるメッセージは、かもしれないqDebugqWarningqCriticalおよびにそう!
nawaz 2011

1
ちなみに、実際の出力を行うコールバック(void myMessageOutput(QtMsgType type、const char * msg))は、どのエンコーディングでメッセージを受信しますか?
セプタグラム2011

8
ドキュメントのリンクとAPIが少し変更されました。Qt5でqInstallMsgHandler非推奨になり、qInstallMessageHandler(同じアイデア)に置き換えられました。5.0の場合qInstallMsgHandlerであるqt-project.org/doc/qt-5.0/qtcore/...qInstallMessageHandlerそこにもあります。5.1では、qInstallMsgHandler完全に削除されました。
ジェイソンC

1
@Aditya:Qt4では、コールバックは2つの引数のみを取ります。あなたはこれを使用することができますので、:void myMessageOutput(QtMsgType type, const char *msg) { ... }
ナワズ

19

ここからすべての信用は精神に行きます。

#include <QApplication>
#include <QtDebug>
#include <QFile>
#include <QTextStream>

void myMessageHandler(QtMsgType type, const QMessageLogContext &, const QString & msg)
{
    QString txt;
    switch (type) {
    case QtDebugMsg:
        txt = QString("Debug: %1").arg(msg);
        break;
    case QtWarningMsg:
        txt = QString("Warning: %1").arg(msg);
    break;
    case QtCriticalMsg:
        txt = QString("Critical: %1").arg(msg);
    break;
    case QtFatalMsg:
        txt = QString("Fatal: %1").arg(msg);
    break;
    }
    QFile outFile("log");
    outFile.open(QIODevice::WriteOnly | QIODevice::Append);
    QTextStream ts(&outFile);
    ts << txt << endl;
}

int main( int argc, char * argv[] )
{
    QApplication app( argc, argv );
    qInstallMessageHandler(myMessageHandler);   
    ...
    return app.exec();
}

ケースQtFatalMsg:... abort(); //ログを書き込む前に終了します
raidsan 2012年

メッセージハンドラを変更するqInstallMessageHandler代わりに、QT5から開始する必要がありqInstallMsgHandlerます。
SuB 2016年

このメッセージハンドラはスレッドセーフではありません。2つのスレッドから同時に送信された場合、ログメッセージは失われます(outFile.open()はスレッドの1つに対してfalseを返します)。ファイルを開こうとする前にQMutexをロックし、ファイルを閉じた後にミューテックスのロックを解除することができます。これは最も単純なアプローチですが、スレッドの競合が発生します。それ以外の場合は、オーバーヘッドの少ないスレッドセーフなメッセージキューを確認する必要があります...フレームワークを使用した方がよい場合があります。
アンソニーヘイワード

9

これは、デフォルトのメッセージハンドラーをフックする実際の例です。

ありがとう@ロスロジャース!

// -- main.cpp

// Get the default Qt message handler.
static const QtMessageHandler QT_DEFAULT_MESSAGE_HANDLER = qInstallMessageHandler(0);

void myCustomMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    // Handle the messages!

    // Call the default handler.
    (*QT_DEFAULT_MESSAGE_HANDLER)(type, context, msg);
}

int main(int argc, char *argv[])
{
    qInstallMessageHandler(myCustomMessageHandler);

    QApplication a(argc, argv);

    qDebug() << "Wello Horld!";

    return 0;
}

8

これは、アプリがQt Creatorから実行された場合はコンソールに、debug.logコンパイルされてスタンドアロンアプリとして実行された場合はファイルにログを記録するためのクロスプラットフォームソリューションです。

main.cpp

#include <QApplication>
#include <QtGlobal>
#include <QtDebug>
#include <QTextStream>
#include <QTextCodec>
#include <QLocale>
#include <QTime>
#include <QFile>   

const QString logFilePath = "debug.log";
bool logToFile = false;
    
void customMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    QHash<QtMsgType, QString> msgLevelHash({{QtDebugMsg, "Debug"}, {QtInfoMsg, "Info"}, {QtWarningMsg, "Warning"}, {QtCriticalMsg, "Critical"}, {QtFatalMsg, "Fatal"}});
    QByteArray localMsg = msg.toLocal8Bit();
    QTime time = QTime::currentTime();
    QString formattedTime = time.toString("hh:mm:ss.zzz");
    QByteArray formattedTimeMsg = formattedTime.toLocal8Bit();
    QString logLevelName = msgLevelHash[type];
    QByteArray logLevelMsg = logLevelName.toLocal8Bit();

    if (logToFile) {
        QString txt = QString("%1 %2: %3 (%4)").arg(formattedTime, logLevelName, msg,  context.file);
        QFile outFile(logFilePath);
        outFile.open(QIODevice::WriteOnly | QIODevice::Append);
        QTextStream ts(&outFile);
        ts << txt << endl;
        outFile.close();
    } else {
        fprintf(stderr, "%s %s: %s (%s:%u, %s)\n", formattedTimeMsg.constData(), logLevelMsg.constData(), localMsg.constData(), context.file, context.line, context.function);
        fflush(stderr);
    }

    if (type == QtFatalMsg)
        abort();
}

int main(int argc, char *argv[])
{
    QByteArray envVar = qgetenv("QTDIR");       //  check if the app is ran in Qt Creator

    if (envVar.isEmpty())
        logToFile = true;

    qInstallMessageHandler(customMessageOutput); // custom message handler for debugging

    QApplication a(argc, argv);
    // ...and the rest of 'main' follows

ログのフォーマットは、QString("%1 %2: %3 (%4)").arg...(ファイルの場合)およびfprintf(stderr, "%s %s: %s (%s:%u, %s)\n"...(コンソールの場合)によって処理されます。

インスピレーション:https//gist.github.com/polovik/10714049


すべてのログイベントで「outFile.close()」を呼び出しているようです。省略してもいいですか?
発散

毎回ログファイルを開いているため、閉じる必要があるため、この設定ではお勧めしません。ただし、アルゴリズムを変更することはできます。そのログファイルは、アプリの初期化時に1回だけ開かれます。このように、アプリが終了するときに一度だけ閉じる必要があります。
神経伝達物質2018年

1
ありがとう!とても助かりました。
アーロン

このメッセージハンドラはスレッドセーフではありません。2つのスレッドから同時に送信された場合、ログメッセージは失われます(outFile.open()はスレッドの1つに対してfalseを返します)。ファイルを開こうとする前にQMutexをロックし、ファイルを閉じた後にミューテックスのロックを解除することができます。これは最も単純なアプローチですが、スレッドの競合が発生します。それ以外の場合は、オーバーヘッドの少ないスレッドセーフなメッセージキューを確認する必要があります...フレームワークを使用した方がよい場合があります。
アンソニーヘイワード

私はあなたに同意します—それは完璧にはほど遠いです。しかし、それはほとんどの場合その仕事をします。とにかく、どんな変更でも大歓迎です!
神経伝達物質2018

6

さて、デバッグ出力をstderrとは異なるものにリダイレクトする必要があるのは、ロギングツールについて考えることができるときです。必要だと思われる場合は、ライブラリからQxtLogger「QxtLoggerクラスは使いやすく、拡張しやすいロギングツールです。」を使用することをお勧めしQxtます。


0

これは、ログstderrとファイルの両方にログインするための、単純でスレッドセーフな慣用的なQtの例です。

void messageHandler(QtMsgType type、const QMessageLogContext&context、const QString&message)
{{
    静的QMutexミューテックス;
    QMutexLockerロック(&mutex);

    静的QFilelogFile(LOGFILE_LOCATION);
    static bool logFileIsOpen = logFile.open(QIODevice :: Append | QIODevice :: Text);

    std :: cerr << qPrintable(qFormatLogMessage(type、context、message))<< std :: endl;

    if(logFileIsOpen){
        logFile.write(qFormatLogMessage(type、context、message).toUtf8()+ '\ n');
        logFile.flush();
    }
}

qInstallMessageHandler(messageHandler)他の回答の説明に従ってインストールしてください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.