Search code examples
c++http-redirectstdoutstderr

How to redirect stdout to stderr


I am working with a C++ QT application that also uses JavaScript. In C++ I use the qDebug function and capture all data with qInstallMessageHandler.

This captures everything that is directed to stderr. In JavaScript I am using console.info which writes data to stdout.

What I want to do is redirect stdout to stderr so that all the messages written by console.info find they're way into the same message handler.

void qDebugMsgHandler(QtMsgType type, const QMessageLogContext& context, const QString& strMsg) {
    QString strOutput;

    if ( context.file ) {
        strOutput += QString(context.file);

        if ( context.function ) {
            if ( context.line > 0 ) {
                strOutput += QString(" L%1").arg(context.line, 8, 10, QChar('0')) + QString(":");
           }
            strOutput += QString(context.function);
        }
    }
    if ( strMsg.length() > 0 ) {
        if ( strOutput.length() > 0 ) {
            strOutput += ": ";
        }
        strOutput += strMsg;
    }
    switch( type ) {
    case QtDebugMsg:
        fprintf(stderr, "   Debug:%s\n", strOutput.toLatin1().data());
        break;
    case QtInfoMsg:
        fprintf(stderr, "    Info:%s\n", strOutput.toLatin1().data());
        break;
    case QtWarningMsg:
        fprintf(stderr, " Warning:%s\n", strOutput.toLatin1().data());
        break;
    case QtCriticalMsg:
        fprintf(stderr, "Critical:%s\n", strOutput.toLatin1().data());
        break;
    case QtFatalMsg:
        fprintf(stderr, "   Fatal:%s\n", strOutput.toLatin1().data());
        break;
    }
    fflush(stderr);
}

Solution

  • For my purpose, I ended up created an invokable routine in C++ which could be called from the JavaScript, C++ prototype:

    Q_INVOKABLE void log(QString strMsg, QString strFile, long ulngLine);
    

    C++ implementation:

    void clsScriptHelper::log(QString strMsg, QString strFile, long ulngLine) {
        QJsonObject json;
        json["file"] = strFile;
        json["line"] = QString("%1").arg(ulngLine);
        json["msg"] = strMsg;
        json["type"] = QString("%1").arg(QtDebugMsg);
        qDebug() << json;
    }
    

    In the JavaScript I expose my C++ layer through the object reference "api", then call the log routine:

    api.log("Testing", "SomeFile.js", 99);
    

    The message handler now looks like this:

    void qDebugMsgHandler(QtMsgType type, const QMessageLogContext& context, const QString& strMsg) {
        static const QString scstrQJSONObject("QJsonObject(");
    
        QString strFile, strFunction, strInfo, strOutput;
        long lngLine = 0;
    
        switch( type ) {
        case QtDebugMsg:
            strOutput = "   Debug:";
            break;
        case QtInfoMsg:
            strOutput = "    Info:";
            break;
        case QtWarningMsg:
            strOutput = " Warning:";
            break;
        case QtCriticalMsg:
            strOutput = "Critical:";
            break;
        case QtFatalMsg:
            strOutput = "   Fatal:";
            break;
        }
        if ( strMsg.startsWith(scstrQJSONObject) ) {
    //Message contains a JSON object, extract the details
            int intLength = strMsg.length() - (scstrQJSONObject.length() + 1);
            QString strJSON = strMsg.mid(scstrQJSONObject.length(), intLength);
            QJsonDocument objDoc = QJsonDocument::fromJson(strJSON.toUtf8());
    
            if ( objDoc.isNull() ) {
                return;
            }
            QJsonObject objJSON = objDoc.object();
            strFile = objJSON.take("file").toString();
            lngLine = static_cast<long>(objJSON.take("line").toDouble());
            strInfo = objJSON.take("msg").toString();
            type = static_cast<QtMsgType>(objJSON.take("type").toInt());
        } else {
            strFile = QString(context.file);
    
            if ( context.function ) {
                strFunction = QString(context.function);
            }
            if ( context.line > 0 ) {
                lngLine = context.line;
            }
            strInfo = strMsg;
        }
        if ( strFile.length() > 0 ) {
            strOutput += strFile;
        }
        if ( lngLine > 0 ) {
            strOutput += QString(" L%1").arg(lngLine, 8, 10, QChar('0')) + QString(":");
        }
        if ( strFunction.length() > 0 ) {
            strOutput += strFunction;
        }
        if ( strInfo.length() > 0 ) {
            if ( strOutput.length() > 0 ) {
                strOutput += ": ";
            }
            strOutput += strInfo;
        }
        std::cout << strOutput.toLatin1().data() << std::endl << std::flush;
    }