/*! * * Copyright (c) 2020-2024 Diality Inc. - All Rights Reserved. * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file Logger.cpp * \author (last) Behrouz NematiPour * \date (last) 08-Apr-2024 * \author (original) Behrouz NematiPour * \date (original) 26-Aug-2020 * */ #include "Logger.h" // Qt #include #include #include #include #include #include #include // Project #include "ApplicationController.h" #include "DeviceController.h" #include "Threads.h" #include "StorageGlobals.h" #include "MainTimer.h" #include "FileHandler.h" #include "format.h" using namespace Storage; /*! * \brief Logger::Logger * \details Constructor * \param parent - QObject parent owner object. * Qt handles the children destruction by their parent objects life-cycle. */ Logger::Logger(QObject *parent) : QObject(parent) { if ( ! ( gLogLongName || gLogSerialName ) ) { _fileDateFormat = "yyyy_MM_dd"; // date used in the file name } } /*! * \brief Logger::init * \details Initializes the Class. * \return False if it has been called before. */ bool Logger::init() { if ( _init ) return false; _init = true; // runs in thread checkLogPath(); initConnections(); return true; } /*! * \brief Logger::init * \details Initialized the Class by calling the init() method first * And initializes the thread vThread by calling initThread * on success init(). * \param vThread - the thread * \return returns the return value of the init() method */ bool Logger::init(QThread &vThread) { // disabled coco begin validated: Application is not running in multi-threaded mode for testing // it has been tested and works perfectly fine in normal run. if ( ! init() ) return false; initThread(vThread); return true; } // disabled coco end /*! * \brief Logger::postInit * \details these are the actions that should be executed after the logger has been initialized * but also cannot be done in the Logger class since it is not immediately moving to the thread. */ void Logger::postInit() { if ( ! checkThread() ) return; ADD_APPED_HEADER; ADD_DEBUG_HEADER; LOG_DEBUG(tr("%1 Initialized").arg(metaObject()->className())); LOG_DEBUG(tr("Application %1 Started").arg(qApp->applicationName())); LOG_APPED_UI(qApp-> applicationVersion()); } /*! * \brief Logger::checkThread * \details Checks the current thread to be the logger thread * otherwise sends out a message to the console and returns false. * \return false if not logger thread */ bool Logger::checkThread() { bool ok = true; if ( this->thread() != &Threads::_Logger_Thread ) { qDebug() << " ----- " << "The main Log function rejection: The Logger is not initialized for proper use"; ok = false; } return ok; } /*! * \brief Logger quit * \details quits the class * Calls quitThread */ void Logger::quit() { // disabled coco begin validated: Application termination is not correctly done in coco!!! // it has been tested and works perfectly fine in normal run. quitThread(); // validated } // disabled coco end /*! * \brief Logger::initConnections * \details Initializes the required signal/slot connection between this class and other objects * to be able to communicate. * \note No connection has been defined yet. */ void Logger::initConnections() { connect(&_ApplicationController , SIGNAL(didActionReceive (GuiActionType , const QVariantList &)), this , SLOT( onActionReceive (GuiActionType , const QVariantList &))); connect(&_exportLogsWatcher, SIGNAL(finished ()), this , SLOT(onExportLogs())); connect(this, SIGNAL(didLog(QString,LogType,bool)), this, SLOT( onLog(QString,LogType,bool))); connect(&_MainTimer, SIGNAL( didDateChange ()), this , SLOT( concurrentRemoveLogs())); connect(&_removeLogsWatcher, SIGNAL(finished ()), this , SLOT(onRemoveLogs())); connect(&_DeviceController, SIGNAL( didSDCardStateChange(bool, bool)), this , SLOT( onSDCardStateChange(bool, bool))); connect(&_DeviceController, SIGNAL( didSDCardSpaceChange(bool, qint64, qint64, quint8)), this , SLOT( onSDCardSpaceChange(bool, qint64, qint64, quint8))); connect(&_DeviceController, SIGNAL( didSettingsPartitionStateChange(bool, bool)), this , SLOT( onSettingsPartitionStateChange(bool, bool))); connect(&_DeviceController, SIGNAL( didSettingsPartitionSpaceChange(bool, qint64, qint64, quint8)), this , SLOT( onSettingsPartitionSpaceChange(bool, qint64, qint64, quint8))); connect(&_DeviceController, SIGNAL(didCryptSetupMount(bool)), this , SLOT( onCryptSetupMount(bool))); } /*! * \brief Logger::initThread * \details Moves this object into the thread vThread. * And checks that this method is called from main thread. * Also connects quitThread to application aboutToQuit. * \param vThread - the thread */ void Logger::initThread(QThread &vThread) { // disabled coco begin validated: Application is not running in multi-threaded mode for testing // it has been tested and works perfectly fine in normal run. // runs in main thread Q_ASSERT_X(QThread::currentThread() == qApp->thread() , __func__, "The Class initialization must be done in Main Thread" ); _thread = &vThread; _thread->setObjectName(QString("%1_Thread").arg(metaObject()->className())); connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit())); _thread->start(); moveToThread(_thread); } // disabled coco end /*! * \brief Logger::quitThread * \details Moves this object to main thread to be handled by QApplication * And to be destroyed there. */ void Logger::quitThread() { if (! _thread) return; // runs in thread moveToThread(qApp->thread()); // validated } void Logger::onLog(const QString &vContent, LogType vLogType, bool vTimestamp) { static bool notified = false; if ( ! _logStorageReady && ! gDisableSDCFailLogStop ) { if ( ! notified ) { notified = true; qDebug() << "Log storage not ready, logging rejected"; } return; } log(vContent, vLogType, vTimestamp); } /*! * \brief Logger::checkLogPath * \details Sets the log paths and creates them if didn't exist. */ void Logger::checkLogPath() { // The POST detects the SD card mount state, but logging needs it before // POST is complete, need to determine via code whether the SD card is mounted QStorageInfo baseSDCard; baseSDCard.setPath(SDCard_Base_Path_Name); bool isSDMounted = !baseSDCard.isRoot(); // When the SD card is not mounted, the path is set to the mount-point's root, ie: '/' setLogBasePath(!isSDMounted); // if the SD is not mounted, use tmp folder; else use /media/sd-card setLogPath ( ); // check and create log folders // Note: it may require to check for write access regarding device setup if using tmp } /*! * \brief Logger::setLogBasePath * \details Tries to the set the log path to the default log path (Log_Base_Path_Name) * Will set the application folder as the base log path if cannot set the log path to the default. * Will log the event in that case. * \param vUseTempPath */ void Logger::setLogBasePath(bool vUseTempPath) { #ifndef BUILD_FOR_TARGET // Not building for target, not using tmp location vUseTempPath = false; #endif if (vUseTempPath) { _dir.setPath(gStandard_tmp); // NOTE: Do not use LOG_XXXXX, At this moment Logger has not been initialized yet QString msg = QString("temp location used for events logging (%1)").arg(_dir.path()); //DEBUG qDebug() << msg; FileHandler::errOut(msg); } else { _dir.setPath(SDCard_Base_Path_Name); } } /*! * \brief Logger::setLogPath * \details set the log path for each of the Datum, Event, Error log types * \return False if can not st the log paths. */ bool Logger::setLogPath() { bool ok = true; if ( ok && ! setLogPath(LogType::eLogAppED) ) ok = false; if ( ok && ! setLogPath(LogType::eLogDebug) ) ok = false; return ok; } /*! * \brief Logger::onCryptSetupMount * \details the handler for the _DeviceController::didCryptSetupMount * to set the treatment folder when the encrypted partition is ready * and successfully decrypted and mounted. * It checks to make sure the folder exist and is able to be written and read. */ void Logger::onCryptSetupMount(bool /*vPass*/) { LogType vLogType = LogType::eLogTrtmt; QString basePath = Storage::Settings_Path(); // use the Settings path first (/var/configurations (Encrypted Partition)) if ( ! QDir (basePath ).exists( )) { basePath = gStandard_tmp; goto lOut; } if ( ! FileHandler::makeFolder (basePath + Storage::Txr_Folder_Treatment )) { basePath = gStandard_tmp; goto lOut; } lOut: _logPathNames[vLogType] = basePath + Storage::Txr_Folder_Treatment; emit didLogPathSet(vLogType, _logPathNames[vLogType]); FileHandler::errOut(tr("The '%1' folder selected for the treatment reports").arg(_logPathNames[vLogType])); } /*! * \brief Logger::setLogPath * \details Sets the log path for the log type vLogType * Creates the folder if not exists. * \param vLogType - log type * \return returns false if the path does not exist and folder cannot be created. */ bool Logger::setLogPath(LogType vLogType) { bool ok = false; // treatment logs moved to the encrypted partition/configurations. // it handled in onCryptSetupMount if (vLogType == LogType::eLogTrtmt ) return true; _logPathNames[vLogType] = _dir.path() + "/" + _logBasePathNames[vLogType]; ok = FileHandler::makeFolder(_logPathNames[vLogType]); if ( ok ) emit didLogPathSet(vLogType, _logPathNames[vLogType]); return ok; } /*! * \brief Logger::log * \details Logs the content vContent in log type of vLogType. * \param vContent - Log content * \param vLogType - Log type * \note This method is not thread-safe so is private and needs to be called by concurrentLog * Which uses QtConcurrent::run to run in thread and thread-safe. */ void Logger::log(const QString &vContent, LogType vLogType, bool vTimestamp) { if ( ! checkThread() ) return; QString mContent; // - Add header QString currentDate = QDate::currentDate().toString(_fileDateFormat); QString currentTime = QTime::currentTime().toString(_fileTimeFormat); if ( _logFileNameDate != currentDate ) { if ( ! _logFileNameDate.isEmpty() ) { switch ( vLogType ) { case eLogAppED : mContent = _headerA; break; case eLogDebug : mContent = _headerD; break; // case LogType::eLogTrtmt: // this type of log will never happen here. Only put here to make sure it is intentional. default : mContent = _headerD; break; } mContent += "\r\n"; } _logFileNameDate = currentDate; _logFileNameTime = currentTime; } // - Make log file name if (_logFileNameMode.isEmpty()) { _logFileNameMode = _logFileNameMode_init; } QString fileName; if ( gLogLongName ) { // -x fileName = _logFileNameHDSN + _fileSeparator + _logFileNameDate + _fileSeparator + _logFileNameTime + _fileSeparator + _logFileNameMode ; } else if ( gLogSerialName ) { // -X fileName = _logFileNameHDSN + _fileSeparator + _logFileNameDate ; } else { fileName = _logFileNameDate + _fileSeparator + Storage::systemSetupPostfix; } // case LogType::eLogTrtmt: // this type of log will never happen here. Only put here to make sure it is intentional. switch (vLogType) { case LogType::eLogAppED: { // ------------------------------------------------------------------------ TODO: Improve : function declaration fileName += _logFileNameExt[vLogType]; static QString oFileName; if( oFileName != fileName ) { if( oFileName.trimmed().isEmpty() ) { oFileName = fileName; } else { emit didLogBackup( _logPathNames[vLogType] + oFileName ); oFileName = fileName; } } } break; case LogType::eLogDebug: { // ------------------------------------------------------------------------ TODO: Improve : function declaration fileName += _logFileNameExt[vLogType]; static QString oFileName; if( oFileName != fileName ) { if( oFileName.trimmed().isEmpty() ) { oFileName = fileName; } else { emit didLogBackup( _logPathNames[vLogType] + oFileName ); oFileName = fileName; } } } break; default: fileName += _logFileNameExt[eLogDebug]; LOG_DEBUG(QString("Incorrect type of logging %1").arg(vLogType)); } // - Add timestamp if ( vTimestamp ) mContent = QTime::currentTime().toString(_timeFormat) + _separator; // - Add the content mContent += vContent; // some messages like the version having the U08(uchar) parameters which converts to '\0' and causes problems in reading the log file. mContent.replace('\0', "0"); // - Make the log path and write to log. QString logPathName = _logPathNames[vLogType]; if (logPathName.isEmpty()) logPathName = _logPathNames[eLogDebug]; _logFileName = logPathName + fileName; bool isWritten = FileHandler::write(_logFileName, mContent + "\r\n", true); if(!isWritten) emit didLogIOFail(); // console out the log if enabled. if (_enableConsoleOut) { qDebug().noquote() << mContent; } } /*! * \brief Logger::exportLogs * \details Exports the log files from log folder (Storage::Log_Base_Path_Name_Location) * into USB drive folder (Storage::USB_Mount_Point) * \return true if at least one file has been exported */ bool Logger::exportLogs(const GuiStringIndexMap &vExportList) { return exportList(vExportList, eLogAppED); } /*! * \brief Logger::exportService * \details Exports the service files from service folder (Storage::Log_Base_Path_Name_Location) * into USB drive folder (Storage::USB_Mount_Point) * \return true if at least one file has been exported */ bool Logger::exportErrs(const GuiStringIndexMap &vExportList) { return exportList(vExportList, eLogDebug); } /*! * \brief Logger::exportTreatment * \details Exports the treatment files from treatment folder (Storage::Log_Base_Path_Name_Location) * into USB drive folder (Storage::USB_Mount_Point) * \return true if at least one file has been exported */ bool Logger::exportTrts(const GuiStringIndexMap &vExportList) { return exportList(vExportList, eLogTrtmt); } /*! * \brief Logger::concurrentExportTest * \details Tests if a log is running * \return if running return false */ bool Logger::concurrentExportIsOk() { if ( _exportLogsWatcher.isRunning() ) { LOG_DEBUG(QString("Export type of %1 is running").arg(_logNames[_exportLogsType])); return false; } return true; } /*! * \brief Logger::concurrentExportLogs * \details Export logs scheduler. * \return always returns true for now. * \note This method uses QtConcurrent run to execute the FileHandler copyFolder method. */ bool Logger::concurrentExportLogs(const GuiStringIndexMap &vExportList) { if ( ! concurrentExportIsOk() ) return false; _exportLogsType = eLogAppED; LOG_DEBUG(QString("Export %1 start").arg(_logNames[_exportLogsType])); QFuture future = QtConcurrent::run(this, &Logger::exportLogs, vExportList); _exportLogsWatcher.setFuture(future); return true; } /*! * \brief Logger::concurrentExportLogs * \details Export logs scheduler. * \return always returns true for now. * \note This method uses QtConcurrent run to execute the FileHandler copyFolder method. */ bool Logger::concurrentExportErrs(const GuiStringIndexMap &vExportList) { if ( ! concurrentExportIsOk() ) return false; _exportLogsType = eLogDebug; LOG_DEBUG(QString("Export %1 start").arg(_logNames[_exportLogsType])); QFuture future = QtConcurrent::run(this, &Logger::exportErrs, vExportList); _exportLogsWatcher.setFuture(future); return true; } /*! * \brief Logger::concurrentExportLogs * \details Export logs scheduler. * \return always returns true for now. * \note This method uses QtConcurrent run to execute the FileHandler copyFolder method. */ bool Logger::concurrentExportTrts(const GuiStringIndexMap &vExportList) { if ( ! concurrentExportIsOk() ) return false; _exportLogsType = eLogTrtmt; LOG_DEBUG(QString("Export %1 start").arg(_logNames[_exportLogsType])); QFuture future = QtConcurrent::run(this, &Logger::exportTrts, vExportList); _exportLogsWatcher.setFuture(future); return true; } /*! * \brief Logger::onExportLogs * \details Export log notification slot which logs result of export. */ void Logger::onExportLogs() { // disabled coco begin validated: This needs user interaction to export to USB device // has been tested manually LOG_DEBUG(QString("Export %1 ended: %2").arg(_logNames[_exportLogsType]).arg(_exportLogsWatcher.result())); //DEBUG: qDebug()<< "_exportLogsWatcher.result()" << _exportLogsWatcher.result(); if(!_exportLogsWatcher.result()){ emit didLogIOFail(); return; } emit didExportLogs(); } // disabled coco end /*! * \brief CloudSyncController::onActionReceive * \details The slot which will be called when a CANBus message is received, and will be sent to CloudSync if related. * \param vAction - The message action * \param vData - The message data */ void Logger::onActionReceive(GuiActionType vAction, const QVariantList &vData) { switch (vAction) { case GuiActionType::ID_HDOperationModeData : { if ( vData.length() >= 2 ) { bool ok = true; quint32 opMode = vData[0].toUInt(&ok); if ( ! ok ) return; quint32 subMode = vData[1].toUInt(&ok); if ( ! ok ) return; QString mode; if ( opMode == GuiHDOpModes::MODE_STAN && subMode >= GuiHDStandbyStates::STANDBY_WAIT_FOR_DISINFECT_STATE ) { mode = "Disinfect"; if( _logFileNameMode != mode ) { _logFileNameMode = mode; _logFileNameTime = QDateTime::currentDateTime().time().toString(_fileTimeFormat); } } else { switch ( opMode ) { case GuiHDOpModes::MODE_NLEG: case GuiHDOpModes::MODE_FAUL: case GuiHDOpModes::MODE_SERV: case GuiHDOpModes::MODE_INIT: case GuiHDOpModes::MODE_STAN: mode = _logFileNameMode_init; if( _logFileNameMode != mode ) { _logFileNameMode = mode; _logFileNameTime = QDateTime::currentDateTime().time().toString(_fileTimeFormat); } break; case GuiHDOpModes::MODE_TPAR: case GuiHDOpModes::MODE_PRET: case GuiHDOpModes::MODE_TREA: case GuiHDOpModes::MODE_POST: mode = "Treatment"; if( _logFileNameMode != mode ) { _logFileNameMode = mode; _logFileNameTime = QDateTime::currentDateTime().time().toString(_fileTimeFormat); } break; default: break; } break; } } } break; case GuiActionType::ID_AdjustSerialHDRsp : { if ( vData.length() >= 1 ) { QString hdSerial= vData[0].toString(); if ( hdSerial.trimmed().isEmpty() ) hdSerial = "Unknown"; _logFileNameHDSN = hdSerial; } } break; default: break; } } /*! * \brief Logger::removeLogs * \details Remove old logs by iterating in the log/service folders and look for expired logs. * \return count file(s) have been removed. */ int Logger::removeLogs(LogType vLogType) { // Storage::FileHandler::find("/media/denali/0CAA-40C1/log/", {"*.err"}, 15); return 0; // has been tested manually LOG_DEBUG(tr("Initializing log clean up")); int removeCount = 0; auto logFiles = { eLogAppED , eLogDebug , eLogCloud }; // TODO: UI (Linux denali user has no access to the CloudSync log folders to delete) auto logFile = { vLogType }; // Is mostly used for the txr files which are in separate partition. for ( const auto &iType : ( vLogType == eLogFiles ? logFiles : logFile ) ) { // The Application does not have access to remove the CloudSync logs, // therefore sends CloudSync a message with expected used percent of the log files, // to remove the logs. if ( iType == eLogCloud ) { emit didRetentionLogCS(_logTypeMaxUsageLimit[iType]); continue; } QString mCSource = _logPathNames [iType]; QString mCExtension = _logFileNameExt[iType]; // DEBUG: qDebug() << "@" << mCSource << mLogFileFilter << mCExtension << iType << _logTypeMaxUsageLimit[iType]; QFileInfoList fileInfoList = FileHandler::find( mCSource , // where to look {"*.*"} , // what to delete // it means the removal will be for all the files in that location and won't look at the mCExtension _logTypeMaxUsageLimit[iType]); // how many/much removeCount = fileInfoList.count(); // qDebug() << "@" << removeCount << fileInfoList; if ( ! removeCount ) { LOG_DEBUG(QString("No log file to delete for type(%1) more than %2% limit from folder %3") .arg(mCExtension) .arg(_logTypeMaxUsageLimit[iType]) .arg(mCSource)); continue; } LOG_DEBUG(QString("Removing %1 logs of type (%2) more than %3% limit from folder %4") .arg(removeCount) .arg(mCExtension) .arg(_logTypeMaxUsageLimit[iType]) .arg(mCSource)); for (const auto &info: fileInfoList) { bool isWritable = info.isWritable(); bool isOldLog = info.lastModified().date() != QDate().currentDate(); if ( ! isWritable ) { LOG_DEBUG(QString("Log %1 cannot be deleted" ).arg(info.fileName())); continue; } if ( ! isOldLog ) { LOG_DEBUG(QString("Current day log %1 cannot be deleted" ).arg(info.fileName())); continue; } QString mFileName = mCSource + info.fileName(); // DEBUG: qDebug() << "#" << mFileName; bool ok = QFile::remove(mFileName); if (ok) { LOG_DEBUG(QString("Removing %1 succeeded").arg(mFileName)); } else { LOG_DEBUG(QString("Removing %1 failed" ).arg(mFileName)); } } } return removeCount; } /*! * \brief Logger::concurrentRemoveLogs * \details remove logs scheduler. * \return always returns true for now. * \note This method uses QtConcurrent run to execute the FileHandler copyFolder method. */ bool Logger::concurrentRemoveLogs(LogType vLogType) { // disabled coco begin validated: This needs user interaction to check the old files deleted // has been tested manually LOG_DEBUG("Remove Logs Starting"); emit didRemoveLogs(true); QFuture mFuture = QtConcurrent::run(this, &Logger::removeLogs, vLogType); _removeLogsWatcher.setFuture(mFuture); return true; } // disabled coco end /*! * \brief Logger::onRemoveLogs * \details Remove old logs notification slot which logs result of remove. */ void Logger::onRemoveLogs() { LOG_DEBUG(tr("Remove Logs Ended: %1").arg(_removeLogsWatcher.result())); emit didRemoveLogs(false); } void Logger::onSDCardStateChange(bool vReady, bool vReadonly) { #if BUILD_FOR_DESKTOP Q_UNUSED(vReady ) Q_UNUSED(vReadonly ) _logStorageReady = true; #else _logStorageReady = vReady && !vReadonly; #endif } /*! * \brief Logger::onSDCardSpaceChange * \details SD Card storage space parameter change slot. * This slot when called is calling the function concurrentRemoveLogs, * if percent of available space vPercent is less than Storage::Available_Space_Percent, * if the SD Card is ready (vReady is true) * \param vReady - The SD Card is Ready * \param vTotal - Total storage space on the SD Card * \param vAvailable - Available storage space on the SD Card * \param vPercent - Percent of available storage space on the SD Card */ void Logger::onSDCardSpaceChange(bool vReady, qint64 vTotal, qint64 vAvailable, quint8 vPercent) { // disabled coco begin validated: This needs user interaction to change the SD card files system. // has been tested manually Q_UNUSED(vTotal ) Q_UNUSED(vAvailable ) if ( ! vReady ) return; // DEBUG: qDebug() << vPercent << Storage::Available_Space_Percent; if ( Storage::Log_Min_Available_Total_Space_IsLow(vPercent) ) { concurrentRemoveLogs(); } } // disabled coco end /*! * \brief Logger::onSettingsPartitionStateChange * \details handle the state change of the settings partition * \param vReady - The Settings Partition is Ready * \param vReadonly - The Settings Partition is readonly */ void Logger::onSettingsPartitionStateChange(bool vReady, bool vReadonly) { Q_UNUSED(vReadonly) LOG_DEBUG(QString("Settings Partition State Changed | vReady: %1 ").arg(vReady)); } /*! * \brief Logger::onSettingsPartitionSpaceChange * \details Settings Partition storage space parameter change slot. * This slot when called is calling the function concurrentRemoveLogs, * if percent of available space vPercent is less than Storage::Available_Space_Percent, * if the Settings Partition is ready (vReady is true) * \param vReady - The Settings Partition is Ready * \param vTotal - Total storage space on the Settings Partition * \param vAvailable - Available storage space on the Settings Partition * \param vPercent - Percent of available storage space on the Settings Partition */ void Logger::onSettingsPartitionSpaceChange(bool vReady, qint64 vTotal, qint64 vAvailable, quint8 vPercent) { // disabled coco begin validated: This needs user interaction to change the SD card files system. // has been tested manually Q_UNUSED(vTotal ) Q_UNUSED(vAvailable ) if ( ! vReady ) return; // DEBUG: qDebug() << vPercent << Storage::Available_Space_Percent; if ( Storage::Txr_Min_Available_Total_Space_IsLow(vPercent) ) { concurrentRemoveLogs(eLogTrtmt); } } // disabled coco end /*! * \brief Logger::enableConsoleOut * \details Enables or Disables the console output and logs the status * \param vEnabled - Enable console output if true */ void Logger::enableConsoleOut(bool vEnabled) { // disabled coco begin validated: This code meant to be used only for debugging and tested manually if (_enableConsoleOut == vEnabled) return; _enableConsoleOut = vEnabled; if (_enableConsoleOut) { LOG_DEBUG("Console out Logging enabled"); } else { LOG_DEBUG("Console out Logging disabled"); } } // disabled coco end /*! * \brief Logger::logPath * \details The accessor of the log path for each log type. * \param vLogType - type of the log * \return the log path of the log type vLogType as string * \sa Logger::LogType */ const QString &Logger::logPath(Logger::LogType vLogType) { return _logPathNames[vLogType]; } /*! * \brief Logger::exportList * \details Exports files from the list if the vExportList has any item, otherwise exports all the log files of type vLogType. * \param vExportList - List of files to export * \param vLogType - type of the log files to get the correct location for export. * \return true if the export is successful. */ bool Logger::exportList(const GuiStringIndexMap &vExportList, Logger::LogType vLogType) { // DEBUG: qDebug() << __FUNCTION__ << vExportList; // qDebug() << " ~~~~~~~~~~ " << QThread::currentThread()->objectName(); auto notifier = [this] (quint32 vIndex, const QString &vFileName, quint8 vPercent) { emit didExportStat(vIndex, vFileName, vPercent); // qDebug() << "0" << vIndex << vFileName << vPercent; }; int result = 0; QString mSource = _logPathNames[vLogType]; QString mDestination = USB_Mount_Point; if ( vExportList.isEmpty() ) { // Copy Folder result = FileHandler::copyFolder( mSource, mDestination); } else { GuiStringIndexMapIterator it(vExportList); while ( it.hasNext() ) { it.next(); // qDebug() << it.key() << it.value() << mSource << mDestination; auto index = it.key (); auto filename = it.value(); result = FileHandler::copyFile(mSource, mDestination + _logBasePathNames[vLogType], filename, ¬ifier, index); } } return result >= 0; // refer to QProcess::execute(hit F1 on execute) doc. }