qDebug, qWarning, qCritical 등 출력을 리디렉션하는 방법은 무엇입니까?


84

qDebug() <<디버그 출력을 위해 많은 문을 사용하고 있습니다 . 셸 스크립트에 의존하지 않고 디버그 출력을 파일로 리디렉션 할 수있는 크로스 플랫폼 방법이 있습니까? 나는 open ()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(물론 qWarning, qCritical등) 메시지는 핸들러에 파일 당신이있는 거 쓰기로 리디렉션 될 것이다.


3
고마워요. 디버그 출력을 파일로 리디렉션 할 수있을뿐만 아니라 타임 스탬프와 같은 더 유용한 정보를 인쇄 할 수도 있습니다. :)
Septagram

2
@Septagram : 맞습니다. 처리기 자체에 유용한 메시지를 추가 할 수 있습니다. 그리고 qDebug, qWarning등 을 사용하는 것에 따라 다른 파일에 다른 메시지를 출력 할 수도 있습니다 qCritical!
Nawaz

1
그건 그렇고, 실제 출력을 수행하는 콜백-void myMessageOutput (QtMsgType type, const char * msg)-어떤 인코딩으로 메시지를 수신합니까?
Septagram

8
문서 링크와 API가 약간 변경되었습니다. Qt5에서 qInstallMsgHandler더 이상 사용되지 않고 qInstallMessageHandler(동일한 아이디어) 로 대체되었습니다 . 5.0의 qInstallMsgHandler경우 qt-project.org/doc/qt-5.0/qtcore/…에 있으며 qInstallMessageHandler거기에도 있습니다. 5.1의 경우 qInstallMsgHandler완전히 제거되었습니다.
Jason C

1
@Aditya : Qt4에서 콜백은 두 개의 인수 만 사용합니다. 당신이 사용할 수 있도록 :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();
}

case QtFatalMsg : ... abort (); // 로그를 작성하기 전에 종료됩니다
raidsan

QT 5부터 시작하여 메시지 처리기를 변경 qInstallMessageHandler하는 대신 사용해야 qInstallMsgHandler합니다.
SuB

이 메시지 핸들러는 스레드로부터 안전하지 않습니다. 두 개의 스레드가 동시에 전송하면 로그 메시지를 잃게됩니다 (outFile.open ()은 스레드 중 하나에 대해 false를 반환합니다). 파일을 열기 전에 QMutex를 잠근 다음 파일을 닫은 후 뮤텍스를 잠금 해제 할 수 있습니다. 이것은 가장 간단한 접근 방식이지만 스레드 경합이 발생합니다. 그렇지 않으면 오버 헤드가 낮은 스레드로부터 안전한 메시지 큐잉을 살펴 봐야하며 프레임 워크를 사용하는 것이 더 나을 수 있습니다.
Anthony Hayward

9

다음은 기본 메시지 처리기를 연결하는 작업 예제입니다.

@Ross Rogers 감사합니다!

// -- 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 ()"를 호출하는 것을 확인했습니다. 생략해도 될까요?
diverger

매번 로그 파일을 열고 닫아야하므로이 설정에서는 권장하지 않습니다. 그러나 어떤 방식 으로든 알고리즘을 변경할 수 있습니다. 해당 로그 파일은 앱의 초기화시 한 번만 열립니다. 이렇게하면 앱이 종료 될 때 한 번만 닫으면됩니다.
신경 전달 물질

1
감사! 매우 도움이됩니다.
아론

이 메시지 핸들러는 스레드로부터 안전하지 않습니다. 두 개의 스레드가 동시에 전송하면 로그 메시지가 손실됩니다 (outFile.open ()은 스레드 중 하나에 대해 false를 리턴합니다). 파일을 열기 전에 QMutex를 잠근 다음 파일을 닫은 후 뮤텍스를 잠금 해제 할 수 있습니다. 이것은 가장 간단한 접근 방식이지만 스레드 경합이 발생합니다. 그렇지 않으면 오버 헤드가 낮은 스레드로부터 안전한 메시지 큐잉을 살펴 봐야합니다. 프레임 워크를 사용하는 것이 더 나을 수 있습니다!
Anthony Hayward

나는 당신의 의견에 동의합니다 – 그것은 완벽하지 않습니다. 그러나 그것은 대부분의 시간에 제 역할을합니다. 어쨌든 모든 수정을 환영합니다!
신경 전달 물질

6

음, 디버그 출력을 stderr와 다른 것으로 리디렉션해야하는 순간은 로깅 도구에 대해 생각할 수있는 순간이라고 말하고 싶습니다. 필요한 경우 라이브러리 에서 사용하는 것이 좋습니다 QxtLogger( "QxtLogger 클래스는 사용하기 쉽고 확장하기 쉬운 로깅 도구입니다." ) Qxt.


0

다음은 stderr및 파일에 모두 로깅하는 간단한 스레드 안전 관용적 Qt 예제입니다 .

void messageHandler (QtMsgType 유형, const QMessageLogContext & 컨텍스트, const QString & 메시지)
{
    정적 QMutex 뮤텍스;
    QMutexLocker 잠금 (& mutex);

    정적 QFile logFile (LOGFILE_LOCATION);
    static bool logFileIsOpen = logFile.open (QIODevice :: Append | QIODevice :: Text);

    std :: cerr << qPrintable (qFormatLogMessage (유형, 컨텍스트, 메시지)) << std :: endl;

    if (logFileIsOpen) {
        logFile.write (qFormatLogMessage (유형, 컨텍스트, 메시지) .toUtf8 () + '\ n');
        logFile.flush ();
    }
}

qInstallMessageHandler(messageHandler)다른 답변에 설명 된대로 설치하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.