QDebug, qWarning, qCritical आदि आउटपुट को रीडायरेक्ट कैसे करें?


84

मैं qDebug() <<डिबग आउटपुट के लिए बहुत सारे बयानों का उपयोग कर रहा हूं । क्या कोई क्रॉस-प्लेटफ़ॉर्म तरीका है जो मैं शेल स्क्रिप्ट्स का सहारा लिए बिना उस डिबग आउटपुट को किसी फ़ाइल में रीडायरेक्ट कर सकता हूं? मैं अनुमान लगा रहा हूं कि ओपन () और डुप 2 () लिनक्स में काम करेगा, लेकिन क्या यह विंडोज में मिनगव के साथ संकलित होगा?

और शायद यह करने के लिए एक क्यूटी तरीका है?

जवाबों:


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(मैंने केवल टिप्पणियाँ जोड़ी):

उपरोक्त उदाहरण में, फ़ंक्शन myMessageOutputका उपयोग करता है stderrजिसे आप किसी अन्य फाइल स्ट्रीम के साथ बदलना चाहते हैं, या फ़ंक्शन को पूरी तरह से फिर से लिखना चाहते हैं!

एक बार जब आप इस फ़ंक्शन को लिखते हैं और इंस्टॉल करते हैं, तो आपके सभी qDebug(साथ ही qWarning, qCriticalआदि) संदेश उस फ़ाइल में रीडायरेक्ट हो जाएंगे जो आप हैंडलर में लिख रहे हैं।


3
अरे, बहुत बहुत धन्यवाद। न केवल यह मुझे एक फ़ाइल पर डिबग आउटपुट को पुनर्निर्देशित करने देगा, यह मुझे टाइमस्टैम्प की तरह अधिक उपयोगी जानकारी प्रिंट करने में भी सक्षम बनाता है :)
सेप्टाग्राम

2
@ समर्थन: बिल्कुल। आप हैंगर में ही कुछ उपयोगी संदेश जोड़ सकते हैं; और आप अलग अलग फ़ाइलों, क्या आप का उपयोग के आधार पर करने के लिए भी उत्पादन अलग संदेश हो सकता है qDebug, qWarning, qCriticalऔर इतने पर!
नवाज

1
वैसे, कॉलबैक जो वास्तविक आउटपुट करता है - void myMessageOutput (QtMsgType type, const char * msg) - यह कौन सा एन्कोडिंग एक संदेश प्राप्त करता है?
सेप्टाग्राम

8
प्रलेखन लिंक और एपीआई थोड़ा बदल गया है। Qt5 में qInstallMsgHandlerपदावनत और प्रतिस्थापित किया गया qInstallMessageHandler(एक ही विचार)। 5.0 के qInstallMsgHandlerलिए qt-project.org/doc/qt-5.0/qtcore/… पर है और qInstallMessageHandlerवहां भी है। 5.1 के लिए, qInstallMsgHandlerपूरी तरह से हटा दिया गया था।
जेसन सी

1
@ आदित्य: 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();
}

मामला QtFatalMsg: ... abort (); // यह लॉग लिखने से पहले छोड़ देगा
raidsan

संदेश हैंडलर को बदलने के qInstallMessageHandlerबजाय , क्यूटी 5 से शुरू करें qInstallMsgHandler
SuB

यह संदेश हैंडलर थ्रेड-सुरक्षित नहीं है। यदि आप एक ही समय में दो थ्रेड द्वारा भेजे गए लॉग संदेश खो देंगे (outFile.open () थ्रेड्स में से एक के लिए गलत वापस आ जाएगा)। फ़ाइल खोलने का प्रयास करने से पहले आप QMutex को लॉक कर सकते हैं, फिर फ़ाइल को बंद करने के बाद म्यूटेक्स को अनलॉक करें। यह सबसे सरल तरीका है लेकिन यह थ्रेड कंटेस्टेंट को पेश करेगा। आपको कम-ओवरहेड थ्रेड-सुरक्षित संदेश कतारबद्ध रूप से देखने की आवश्यकता होगी ... और आप एक रूपरेखा का उपयोग करके बेहतर हो सकते हैं।
एंथनी हेवर्ड

9

यहां डिफ़ॉल्ट संदेश हैंडलर को हुक करने का एक कार्यशील उदाहरण दिया गया है।

शुक्रिया @Ross रोजर्स!

// -- 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

यहाँ कंसोल पर लॉग इन करने के लिए एक क्रॉस-प्लेटफ़ॉर्म समाधान है, अगर ऐप को क्यूटी क्रिएटर से और 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
धन्यवाद! यह बहुत मददगार है।
एरोन

यह संदेश हैंडलर थ्रेड-सुरक्षित नहीं है। यदि आप एक ही समय में दो थ्रेड द्वारा भेजे गए लॉग संदेश खो देंगे (outFile.open () थ्रेड्स में से एक के लिए गलत वापस आ जाएगा)। फ़ाइल खोलने का प्रयास करने से पहले आप QMutex को लॉक कर सकते हैं, फिर फ़ाइल को बंद करने के बाद म्यूटेक्स को अनलॉक करें। यह सबसे सरल तरीका है लेकिन यह थ्रेड कंटेस्टेंट को पेश करेगा। आपको कम-ओवरहेड थ्रेड-सुरक्षित संदेश कतारबद्ध रूप से देखने की आवश्यकता होगी ... और आप एक रूपरेखा का उपयोग करके बेहतर हो सकते हैं!
एंथनी हेवर्ड

मैं आपसे सहमत हूँ - यह बिल्कुल सही है। लेकिन यह ज्यादातर समय अपना काम करता है। वैसे भी, किसी भी संशोधन का स्वागत है!
न्यूरोट्रांसमीटर

6

खैर, मैं कहूंगा कि जब आपको अपने डिबग आउटपुट को stderr से भिन्न कुछ पर पुनर्निर्देशित करने की आवश्यकता होती है, जब आप कुछ लॉगिंग टूल के बारे में सोच सकते हैं। यदि आपको लगता है कि आपको एक की आवश्यकता है तो मैं इसका उपयोग करने की सलाह दूंगाQxtLogger ( "QxtLogger वर्ग का उपयोग करना आसान है, लॉगिंग टूल का विस्तार करना आसान है।" ) Qxtलाइब्रेरी से।


0

यहाँ एक सरल, धागा सुरक्षित मुहावरेदार Qt उदाहरण है stderrजिसे लॉग इन करना और फाइल करना:

शून्य संदेशहैंडलर (QtMsgType प्रकार, const QMessageLogContext और संदर्भ, const QString और संदेश)
{
    स्थिर क्यूम्यूटेक्स म्यूटेक्स;
    QMutexLocker लॉक (और म्यूटेक्स);

    स्थिर QFile logFile (LOGFILE_LOCATION);
    स्टेटिक बूल logFileIsOpen = logFile.open (QIODevice :: Append | QIODevice :: Text);

    std :: cerr << qPrintable (qFormatLogMessage (प्रकार, संदर्भ, संदेश)) << std :: endl;

    अगर (logFileIsOpen) {
        logFile.write (qFormatLogMessage (प्रकार, संदर्भ, संदेश) .toUtf8 () + '\ n');
        logFile.flush ();
    }
}

इसे qInstallMessageHandler(messageHandler)अन्य उत्तरों में वर्णित अनुसार स्थापित करें ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.