diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index f8c10c78b..15ea2f471 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -31,8 +31,6 @@ App::App(int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) { mLinphoneThread = new Thread(this); init(); - qDebug() << "[App] Starting Thread"; - mLinphoneThread->start(); } App *App::getInstance() { @@ -58,6 +56,11 @@ void App::init() { if (mParser->isSet("verbose")) QtLogger::enableVerbose(true); if (mParser->isSet("qt-logs-only")) QtLogger::enableQtOnly(true); + if (!mLinphoneThread->isRunning()) { + qDebug() << "[App] Starting Thread"; + mLinphoneThread->start(); + } + // QML mEngine = new QQmlApplicationEngine(this); mEngine->addImportPath(":/"); @@ -68,7 +71,10 @@ void App::init() { QObject::connect( mEngine, &QQmlApplicationEngine::objectCreated, this, [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) QCoreApplication::exit(-1); + if (!obj && url == objUrl) { + qCritical() << "[App] Main.qml couldn't be load. The app will exit"; + exit(-1); + } }, Qt::QueuedConnection); mEngine->load(url); @@ -83,6 +89,9 @@ void App::initCppInterfaces() { //------------------------------------------------------------ void App::clean() { + // Wait 500ms to let time for log te be stored. + mLinphoneThread->wait(250); + qApp->processEvents(QEventLoop::AllEvents, 250); mLinphoneThread->exit(); mLinphoneThread->wait(); delete mLinphoneThread; diff --git a/Linphone/core/logger/QtLogger.cpp b/Linphone/core/logger/QtLogger.cpp index 7c3ed9c05..5f7a7f1de 100644 --- a/Linphone/core/logger/QtLogger.cpp +++ b/Linphone/core/logger/QtLogger.cpp @@ -19,21 +19,59 @@ */ #include "QtLogger.hpp" +#include "tool/Utils.hpp" +#include +#include #include +#include +#include // ----------------------------------------------------------------------------- static QtLogger gLogger; +// ============================================================================= + +#if defined(__linux__) || defined(__APPLE__) +#define BLUE "\x1B[1;34m" +#define YELLOW "\x1B[1;33m" +#define GREEN "\x1B[1;32m" +#define PURPLE "\x1B[1;35m" +#define RED "\x1B[1;31m" +#define RESET "\x1B[0m" +#else +#define BLUE "" +#define YELLOW "" +#define GREEN "" +#define PURPLE "" +#define RED "" +#define RESET "" +#endif // if defined(__linux__) || defined(__APPLE__) + +// ----------------------------------------------------------------------------- + +static inline QByteArray getFormattedCurrentTime() { + return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz").toLocal8Bit(); +} + QtLogger::QtLogger(QObject *parent) : QObject(parent) { qInstallMessageHandler(QtLogger::onQtLog); } +QtLogger::~QtLogger() { + qInstallMessageHandler(0); +} + QtLogger *QtLogger::getInstance() { return &gLogger; } void QtLogger::onQtLog(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - emit gLogger.logReceived(type, context.file, context.line, msg); + if (type == QtFatalMsg) { + QString out; // Qt force call abort() that kill the application. So it cannot be used to retrieve + // in other thread. Print the error in the hope that it can be catch somewhere. + gLogger.printLog(&out, Constants::AppDomain, linphone::LogLevel::Fatal, Utils::appStringToCoreString(msg)); + } + emit gLogger.qtLogReceived(type, context.file, context.line, msg); } void QtLogger::enableVerbose(bool verbose) { @@ -43,3 +81,65 @@ void QtLogger::enableVerbose(bool verbose) { void QtLogger::enableQtOnly(bool qtOnly) { emit gLogger.requestQtOnlyEnabled(qtOnly); } + +void QtLogger::onLinphoneLog(const std::string &domain, linphone::LogLevel level, const std::string &message) { + QString qMessage; + printLog(&qMessage, domain, level, message); + + if (level == linphone::LogLevel::Fatal) { + QMetaObject::invokeMethod(qApp, [qMessage]() { + QMessageBox::critical(nullptr, EXECUTABLE_NAME " will crash", qMessage); + std::terminate(); + }); + } +} + +void QtLogger::printLog(QString *qMessage, + const std::string &domain, + linphone::LogLevel level, + const std::string &message) { + bool isAppLog = domain == Constants::AppDomain; + FILE *out = stdout; + // TypeColor Date SourceColor [Domain] TypeColor Type Reset Message + QString format = "%1 %2 %3[%4]%1 %5" RESET " %6\n"; + QString colorType; + QString type; + *qMessage = Utils::coreStringToAppString(message); + switch (level) { + case linphone::LogLevel::Debug: + colorType = GREEN; + type = "DEBUG"; + break; + case linphone::LogLevel::Trace: + colorType = BLUE; + type = "TRACE"; + break; + case linphone::LogLevel::Message: + colorType = BLUE; + type = "MESSAGE"; + break; + case linphone::LogLevel::Warning: + colorType = RED; + type = "WARNING"; + out = stderr; + break; + case linphone::LogLevel::Error: + colorType = RED; + type = "ERROR"; + out = stderr; + break; + case linphone::LogLevel::Fatal: + colorType = RED; + type = "FATAL"; + out = stderr; + break; + } + QString messageToDisplay = format.arg(colorType) + .arg(getFormattedCurrentTime()) + .arg(isAppLog ? PURPLE : YELLOW) + .arg(Utils::coreStringToAppString(domain)) + .arg(type) + .arg(*qMessage); + fprintf(out, "%s", qPrintable(messageToDisplay)); + fflush(out); +} diff --git a/Linphone/core/logger/QtLogger.hpp b/Linphone/core/logger/QtLogger.hpp index a37e10e53..5bf629972 100644 --- a/Linphone/core/logger/QtLogger.hpp +++ b/Linphone/core/logger/QtLogger.hpp @@ -24,22 +24,38 @@ #include #include -// ============================================================================= +#include +// ============================================================================= +// +// Qt SDK +//fatal | | +// -- *----------> | +// | | +// | | <--------- * +// | | | +// -> | | +// V V +// OUT FILE +// // Only one instance. Use getInstance() and logReceived() to bind logs coming from Qt. class QtLogger : public QObject { Q_OBJECT public: QtLogger(QObject *parent = nullptr); - + ~QtLogger(); static QtLogger *getInstance(); - static void onQtLog(QtMsgType type, const QMessageLogContext &context, const QString &msg); static void enableVerbose(bool verbose); static void enableQtOnly(bool qtOnly); + void printLog(QString * qMessage, const std::string &domain, linphone::LogLevel level, const std::string &message); + +// Log Sources + static void onQtLog(QtMsgType type, const QMessageLogContext &context, const QString &msg); + void onLinphoneLog(const std::string &domain, linphone::LogLevel level, const std::string &message); signals: - void logReceived(QtMsgType type, QString contextFile, int contextLine, QString msg); + void qtLogReceived(QtMsgType type, QString contextFile, int contextLine, QString msg); void requestVerboseEnabled(bool verbose); void requestQtOnlyEnabled(bool qtOnly); }; diff --git a/Linphone/main.cpp b/Linphone/main.cpp index 33a415004..2513a215e 100644 --- a/Linphone/main.cpp +++ b/Linphone/main.cpp @@ -19,7 +19,10 @@ int main(int argc, char *argv[]) { } } - int result = app.exec(); + int result = 0; + while (result >= 0) { + result = app.exec(); + } app.clean(); return result; } diff --git a/Linphone/model/logger/LoggerListener.cpp b/Linphone/model/logger/LoggerListener.cpp index 6481fe1fd..c796ecbc2 100644 --- a/Linphone/model/logger/LoggerListener.cpp +++ b/Linphone/model/logger/LoggerListener.cpp @@ -20,96 +20,14 @@ #include "LoggerListener.hpp" -#include -#include -#include -#include - -#include "tool/Constants.hpp" -#include "tool/Utils.hpp" - -// ============================================================================= - -#if defined(__linux__) || defined(__APPLE__) -#define BLUE "\x1B[1;34m" -#define YELLOW "\x1B[1;33m" -#define GREEN "\x1B[1;32m" -#define PURPLE "\x1B[1;35m" -#define RED "\x1B[1;31m" -#define RESET "\x1B[0m" -#else -#define BLUE "" -#define YELLOW "" -#define GREEN "" -#define PURPLE "" -#define RED "" -#define RESET "" -#endif // if defined(__linux__) || defined(__APPLE__) - // ----------------------------------------------------------------------------- -static inline QByteArray getFormattedCurrentTime() { - return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz").toLocal8Bit(); +LoggerListener::LoggerListener() : QObject(nullptr) { } -// ----------------------------------------------------------------------------- - -LoggerListener::LoggerListener() { -} - -void LoggerListener::onLogMessageWritten(const std::shared_ptr &, +void LoggerListener::onLogMessageWritten(const std::shared_ptr &logService, const std::string &domain, linphone::LogLevel level, const std::string &message) { - bool isAppLog = domain == Constants::AppDomain; - if (!mVerboseEnabled || (!isAppLog && mQtOnlyEnabled)) return; - FILE *out = stdout; - // TypeColor Date SourceColor [Domain] TypeColor Type Reset Message - QString format = "%1 %2 %3[%4]%1 %5" RESET " %6\n"; - QString colorType; - QString type; - QString qMessage = Utils::coreStringToAppString(message); - switch (level) { - case linphone::LogLevel::Debug: - colorType = GREEN; - type = "DEBUG"; - break; - case linphone::LogLevel::Trace: - colorType = BLUE; - type = "TRACE"; - break; - case linphone::LogLevel::Message: - colorType = BLUE; - type = "MESSAGE"; - break; - case linphone::LogLevel::Warning: - colorType = RED; - type = "WARNING"; - out = stderr; - break; - case linphone::LogLevel::Error: - colorType = RED; - type = "ERROR"; - out = stderr; - break; - case linphone::LogLevel::Fatal: - colorType = RED; - type = "FATAL"; - out = stderr; - break; - } - QString messageToDisplay = format.arg(colorType) - .arg(getFormattedCurrentTime()) - .arg(isAppLog ? PURPLE : YELLOW) - .arg(Utils::coreStringToAppString(domain)) - .arg(type) - .arg(qMessage); - fprintf(out, "%s", qPrintable(messageToDisplay)); - fflush(out); - if (level == linphone::LogLevel::Fatal) { - QMetaObject::invokeMethod(qApp, [qMessage]() { - QMessageBox::critical(nullptr, EXECUTABLE_NAME " will crash", qMessage); - std::terminate(); - }); - } + emit logReceived(logService, domain, level, message); } diff --git a/Linphone/model/logger/LoggerListener.hpp b/Linphone/model/logger/LoggerListener.hpp index 3b70bd9bf..2386da293 100644 --- a/Linphone/model/logger/LoggerListener.hpp +++ b/Linphone/model/logger/LoggerListener.hpp @@ -21,19 +21,23 @@ #ifndef LOGGER_LISTENER_H_ #define LOGGER_LISTENER_H_ -#include +#include #include #include // ============================================================================= -class LoggerListener : public linphone::LoggingServiceListener { +class LoggerListener : public QObject, public linphone::LoggingServiceListener { +Q_OBJECT public: LoggerListener(); - bool mVerboseEnabled = false; - bool mQtOnlyEnabled = false; - + static QString printLog(bool isAppLog, const std::string &domain, linphone::LogLevel level, const std::string &message); +signals: + void logReceived(const std::shared_ptr &logService, + const std::string &domain, + linphone::LogLevel level, + const std::string &message); private: virtual void onLogMessageWritten(const std::shared_ptr &logService, const std::string &domain, diff --git a/Linphone/model/logger/LoggerModel.cpp b/Linphone/model/logger/LoggerModel.cpp index 1c1c8c9c5..806123eec 100644 --- a/Linphone/model/logger/LoggerModel.cpp +++ b/Linphone/model/logger/LoggerModel.cpp @@ -19,9 +19,9 @@ */ #include - #include #include +#include #include #include "config.h" @@ -35,23 +35,26 @@ // ----------------------------------------------------------------------------- LoggerModel::LoggerModel(QObject *parent) : QObject(parent) { - connect(QtLogger::getInstance(), &QtLogger::logReceived, this, &LoggerModel::onLog, Qt::QueuedConnection); + connect(QtLogger::getInstance(), &QtLogger::qtLogReceived, this, &LoggerModel::onQtLog, Qt::QueuedConnection); connect(QtLogger::getInstance(), &QtLogger::requestVerboseEnabled, this, &LoggerModel::enableVerbose, Qt::QueuedConnection); connect(QtLogger::getInstance(), &QtLogger::requestQtOnlyEnabled, this, &LoggerModel::enableQtOnly, Qt::QueuedConnection); + connect(this, &LoggerModel::linphoneLogReceived, QtLogger::getInstance(), &QtLogger::onLinphoneLog, + Qt::QueuedConnection); } LoggerModel::~LoggerModel() { + linphone::LoggingService::get()->removeListener(mListener); } bool LoggerModel::isVerbose() const { - return mListener ? mListener->mVerboseEnabled : false; + return mVerboseEnabled; } void LoggerModel::enableVerbose(bool verbose) { - if (mListener && mListener->mVerboseEnabled != verbose) { - mListener->mVerboseEnabled = verbose; + if (mVerboseEnabled != verbose) { + mVerboseEnabled = verbose; emit verboseEnabledChanged(); } } @@ -62,24 +65,21 @@ void LoggerModel::enableFullLogs(const bool &full) { } bool LoggerModel::qtOnlyEnabled() const { - return mListener ? mListener->mQtOnlyEnabled : false; + return mQtOnlyEnabled; } void LoggerModel::enableQtOnly(const bool &enable) { - if (mListener) { - if (mListener->mQtOnlyEnabled != enable) { - mListener->mQtOnlyEnabled = enable; - auto service = linphone::LoggingService::get(); - if (service) service->setDomain(enable ? Constants::AppDomain : ""); - emit qtOnlyEnabledChanged(); - } + if (mQtOnlyEnabled != enable) { + mQtOnlyEnabled = enable; + auto service = linphone::LoggingService::get(); + if (service) service->setDomain(enable ? Constants::AppDomain : ""); + emit qtOnlyEnabledChanged(); } } // ----------------------------------------------------------------------------- // Called from Qt -void LoggerModel::onLog(QtMsgType type, QString contextFile, int contextLine, QString msg) { - connect(this, &LoggerModel::logReceived, this, &LoggerModel::onLog, Qt::QueuedConnection); +void LoggerModel::onQtLog(QtMsgType type, QString contextFile, int contextLine, QString msg) { auto service = linphone::LoggingService::get(); QString contextStr = ""; #ifdef QT_MESSAGELOGCONTEXT @@ -116,6 +116,16 @@ void LoggerModel::onLog(QtMsgType type, QString contextFile, int contextLine, QS } } +// Call from Linphone +void LoggerModel::onLinphoneLog(const std::shared_ptr &, + const std::string &domain, + linphone::LogLevel level, + const std::string &message) { + bool isAppLog = domain == Constants::AppDomain; + if (!mVerboseEnabled || (!isAppLog && mQtOnlyEnabled)) return; + emit linphoneLogReceived(domain, level, message); +} + // ----------------------------------------------------------------------------- void LoggerModel::enable(bool status) { @@ -134,7 +144,7 @@ void LoggerModel::init(const std::shared_ptr &config) { void LoggerModel::init() { QLoggingCategory::setFilterRules("qt.qml.connections.warning=false"); mListener = std::make_shared(); - + connect(mListener.get(), &LoggerListener::logReceived, this, &LoggerModel::onLinphoneLog); { std::shared_ptr loggingService = linphone::LoggingService::get(); loggingService->setDomain(Constants::AppDomain); diff --git a/Linphone/model/logger/LoggerModel.hpp b/Linphone/model/logger/LoggerModel.hpp index 03a1b8809..6c4be4154 100644 --- a/Linphone/model/logger/LoggerModel.hpp +++ b/Linphone/model/logger/LoggerModel.hpp @@ -45,17 +45,22 @@ public: void init(); void init(const std::shared_ptr &config); - static void onQtLog(QtMsgType type, const QMessageLogContext &context, const QString &msg); -public slots: - void onLog(QtMsgType type, QString file, int contextLine, QString msg); + + void onQtLog(QtMsgType type, QString file, int contextLine, QString msg);// Received from Qt + void onLinphoneLog(const std::shared_ptr &, + const std::string &domain, + linphone::LogLevel level, + const std::string &message);// Received from SDK signals: - void logReceived(QtMsgType type, QString contextFile, int contextLine, QString msg); + void linphoneLogReceived(const std::string &domain, linphone::LogLevel level, const std::string &message); // Send to Qt void verboseEnabledChanged(); void qtOnlyEnabledChanged(); private: static void log(QtMsgType type, const QMessageLogContext &context, const QString &msg); + bool mVerboseEnabled = false; + bool mQtOnlyEnabled = false; std::shared_ptr mListener; };