/*! * * Copyright (c) 2019-2020 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.h * \author (last) Behrouz NematiPour * \date (last) 12-Sep-2020 * \author (original) Behrouz NematiPour * \date (original) 26-Aug-2020 * */ #pragma once // Qt #include #include #include // Project #include "main.h" // Doxygen : do not remove #include "StorageGlobals.h" // Define #define _Logger Storage::Logger::I() #define LOG_EXPORT _Logger.concurrentExportLogs() #define LOG_DEBUG(vCONTENT) emit Storage::Logger::I().didLog(vCONTENT, Storage::Logger::LogType::eLogDebug, true ) #define ADD_EVENT_HEADER emit Storage::Logger::I().didLog(_headerE, Storage::Logger::LogType::eLogEvent, false) #define LOG_EVENT(vCONTENT) emit Storage::Logger::I().didLog(vCONTENT, Storage::Logger::LogType::eLogEvent, true ) #define LOG_DATUM(vCONTENT) emit Storage::Logger::I().didLog(vCONTENT, Storage::Logger::LogType::eLogDatum, true ) #define MIXED_EVENT_DATUM #undef DISABLE_ACKNOW_CHECKIN_MESSAGE_LOG // forward declarations class tst_logging; namespace Storage { /*! * \brief The Logger class * \details Main logger class that has all the required implementation for logging. * The provided interface is the LOG_DATUM, LOG_EVENT, LOG_DEBUG, LOG_EXPORT defines * and no other methods. * * This object is logging all the registered Denali Messages transactions over the CANBus in CSV format text file. * Logger class currently has 3 types which are Event, Error, Datum. * The Event and Data messages are logged in the csv file with "log" extension * and the UI Application specific errors are logged in a csv file with the "err" extension. * Logs file names format start with current system date by "" format and "_denali.". * The content of the file is depending on each log type and has ",,,,". * Currently, the file name and content formatting can be modified by changing the formatting values in Logger class. * * This class has its own thread for logging. * Also for exporting the log files it uses asynchronous thread calling by QtConcurrent. * It communicated with other classes only with Signal/Slots for thread safety and will notify other classes when the export is done by signals. * * \note * PLEASE BE CAREFUL THIS CLASS IS USING QtConcurrent::run FOR THE EXPORT LOG FILES. * AND ONLY PRIVATE VOID LOG (,) IS CALLING IN POOLED THREAD * PLEASE BE VERY CAREFUL. * ALL THE OTHER CLASSES TO USE THIS CLASS SHOULD ONLY USE LOG_DATUM, LOG_EVENT, LOG_DEBUG * TO DO THE LOGGING */ class Logger : public QObject { Q_OBJECT // Singleton SINGLETON(Logger) // friends friend class ::tst_logging; public : enum LogType { eLogDebug, ///< Application Error : CANBus messages does not have error it's Event/Data only eLogEvent, ///< Massages on the CANBus : Error is an event type eLogDatum, ///< Massages on the CANBus : Broadcast data type eLogTrtmt, ///< Treatment Log Files eLogType_Count, }; Q_ENUM(LogType) private: const char *_headerE = "TimeStamp,Type,SubSys,Name,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40"; QDir _dir; QString _logFileNamePrefix; QHash _logPathNames; const QHash _logBasePathNames { { LogType::eLogEvent, "log/" }, { LogType::eLogDatum, "log/" }, { LogType::eLogDebug, "service/" }, { LogType::eLogTrtmt, Storage::Treatment_Log_Folder }, }; const QHash _logPrefix { // Will be used for the logging in the file { LogType::eLogEvent, "E" }, { LogType::eLogDatum, "D" }, { LogType::eLogDebug, "" }, // it has its own file and all the content is Error }; const QHash _logFileNameExt { { LogType::eLogEvent, ".log" }, #ifdef MIXED_EVENT_DATUM { LogType::eLogDatum, ".log" }, #else { LogType::eLogDatum, ".dat" }, #endif { LogType::eLogDebug, ".err" }, { LogType::eLogTrtmt, ".log" }, }; // be careful when defining these percentages // 1 - Since now both the Log and Datum are in the same place and file they have same percentage. // if separated then the percentage has to be separated // 2 - the total in _logTypeExpiryDay is not 100 and it has to be summed up with Storage::Available_Space_Percent. // so it is 70% for Event/Datum + 15% Service + 15% free = 100% total const QHash _logTypeMaxUsageLimit { #ifdef MIXED_EVENT_DATUM { LogType::eLogEvent, 70 }, // in percent { LogType::eLogDatum, 70 }, // in percent #else { LogType::eLogEvent, 35 }, // in percent { LogType::eLogDatum, 35 }, // in percent #endif { LogType::eLogDebug, 15 }, // in percent // Not Sure yet so commented out in the remove. { LogType::eLogTrtmt, 100}, // in percent // No Remove for now }; const char *_dateFormat = "yyyy_MM_dd" ; // date used in the file name const char *_timeFormat = "HH:mm:ss.zzz"; // timestamp in the file const char *_dateSeparator = "_"; // used in filename const char *_separator = ","; bool _enableConsoleOut = false; QString _logFileName = ""; QMutex _logRemoveRunning; QFutureWatcher _exportLogsWatcher; QFutureWatcher _removeLogsWatcher; QThread *_thread = nullptr; bool _init = false; public: void enableConsoleOut(bool vEnabled); void postInit(); signals: void didLogPathSet(Logger::LogType vLogType, const QString &vLogPath); public slots: bool init(); bool init(QThread &vThread); private slots: void quit(); private: void initConnections(); void initThread(QThread &vThread); void quitThread(); bool checkThread(); private: // ----- setting up void checkLogPath (); void setLogBasePath (bool vUseApplicationDirPath = false); bool setLogPath (); bool setLogPath (LogType vLogType); public: const QString &logPath(Logger::LogType vLogType); // ----- Export structure private : bool exportLogs(); public slots: // this slot is thread safe and can be called from outside by LOG_EXPORT. bool concurrentExportLogs(); void onExportLogs(); signals: void didExportLogs(); // ----- Remove Old Logs structure private: int removeLogs(); private slots: // this slot is thread safe and can be called from outside but preferred not to. bool concurrentRemoveLogs(); void onRemoveLogs(); signals: /*! * \brief didRemoveLogs * \details This signal will be emitted mainly for DeviceController to not to emit the signal that Logger is connected to * , while the logging cleanup is in progress, otherwise if the log cleanup takes more that 1sec (current interval) * in the DeviceController then the cleanup will be called again for no good reason. * \param vInProgress - true if the log cleanup is in progress, false otherwise. */ void didRemoveLogs(bool vInProgress); // ----- Available space is low private slots: void onSDCardSpaceChange(bool vReady, qint64 vTotal, qint64 vAvailable, quint8 vPercent); // ----- logging structure private slots: void onLog (const QString &vContent, LogType vLogType, bool vTimestamp); private: void log (const QString &vContent, LogType vLogType, bool vTimestamp); signals: /*! * \brief didLog * \details Notifies the logger on a request for log * \param vContent - content as type of string to be logged * \param vLogType - the type of logging of type Storage::Logger::LogType */ void didLog (const QString &vContent, LogType vLogType, bool vTimestamp); }; }