| 1 | #include "QLoggerWriter.h" | 
|---|
| 2 |  | 
|---|
| 3 | #include <QDateTime> | 
|---|
| 4 | #include <QFile> | 
|---|
| 5 | #include <QTextStream> | 
|---|
| 6 | #include <QDir> | 
|---|
| 7 | #include <QDebug> | 
|---|
| 8 |  | 
|---|
| 9 | namespace | 
|---|
| 10 | { | 
|---|
| 11 | /** | 
|---|
| 12 | * @brief Converts the given level in a QString. | 
|---|
| 13 | * @param level The log level in LogLevel format. | 
|---|
| 14 | * @return The string with the name of the log level. | 
|---|
| 15 | */ | 
|---|
| 16 | QString levelToText(const QLogger::LogLevel &level) | 
|---|
| 17 | { | 
|---|
| 18 | switch (level) | 
|---|
| 19 | { | 
|---|
| 20 | case QLogger::LogLevel::Trace: | 
|---|
| 21 | return "Trace"; | 
|---|
| 22 | case QLogger::LogLevel::Debug: | 
|---|
| 23 | return "Debug"; | 
|---|
| 24 | case QLogger::LogLevel::Info: | 
|---|
| 25 | return "Info"; | 
|---|
| 26 | case QLogger::LogLevel::Warning: | 
|---|
| 27 | return "Warning"; | 
|---|
| 28 | case QLogger::LogLevel::Error: | 
|---|
| 29 | return "Error"; | 
|---|
| 30 | case QLogger::LogLevel::Fatal: | 
|---|
| 31 | return "Fatal"; | 
|---|
| 32 | } | 
|---|
| 33 |  | 
|---|
| 34 | return QString(); | 
|---|
| 35 | } | 
|---|
| 36 | } | 
|---|
| 37 |  | 
|---|
| 38 | namespace QLogger | 
|---|
| 39 | { | 
|---|
| 40 |  | 
|---|
| 41 | QLoggerWriter::QLoggerWriter(const QString &fileDestination, LogLevel level, const QString &fileFolderDestination, | 
|---|
| 42 | LogMode mode, LogFileDisplay fileSuffixIfFull, LogMessageDisplays messageOptions) | 
|---|
| 43 | : mFileSuffixIfFull(fileSuffixIfFull) | 
|---|
| 44 | , mMode(mode) | 
|---|
| 45 | , mLevel(level) | 
|---|
| 46 | , mMessageOptions(messageOptions) | 
|---|
| 47 | { | 
|---|
| 48 | mFileDestinationFolder = (fileFolderDestination.isEmpty() ? QDir::currentPath() : fileFolderDestination) + "/logs/"; | 
|---|
| 49 | mFileDestination = mFileDestinationFolder + fileDestination; | 
|---|
| 50 |  | 
|---|
| 51 | QDir dir(mFileDestinationFolder); | 
|---|
| 52 | if (fileDestination.isEmpty()) | 
|---|
| 53 | { | 
|---|
| 54 | mFileDestination = dir.filePath(QString::fromLatin1( "%1.log").arg( | 
|---|
| 55 | QDateTime::currentDateTime().date().toString(QString::fromLatin1( "yyyy-MM-dd")))); | 
|---|
| 56 | } | 
|---|
| 57 | else if (!fileDestination.contains(QLatin1Char('.'))) | 
|---|
| 58 | mFileDestination.append(QString::fromLatin1( ".log")); | 
|---|
| 59 |  | 
|---|
| 60 | if (mMode == LogMode::Full || mMode == LogMode::OnlyFile) | 
|---|
| 61 | dir.mkpath(QStringLiteral( ".")); | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 | void QLoggerWriter::setLogMode(LogMode mode) | 
|---|
| 65 | { | 
|---|
| 66 | mMode = mode; | 
|---|
| 67 |  | 
|---|
| 68 | if (mMode == LogMode::Full || mMode == LogMode::OnlyFile) | 
|---|
| 69 | { | 
|---|
| 70 | QDir dir(mFileDestinationFolder); | 
|---|
| 71 | dir.mkpath(QStringLiteral( ".")); | 
|---|
| 72 | } | 
|---|
| 73 |  | 
|---|
| 74 | if (mode != LogMode::Disabled && !this->isRunning()) | 
|---|
| 75 | start(); | 
|---|
| 76 | } | 
|---|
| 77 |  | 
|---|
| 78 | QString QLoggerWriter::renameFileIfFull() | 
|---|
| 79 | { | 
|---|
| 80 | QFile file(mFileDestination); | 
|---|
| 81 |  | 
|---|
| 82 | // Rename file if it's full | 
|---|
| 83 | if (file.size() >= mMaxFileSize) | 
|---|
| 84 | { | 
|---|
| 85 | QString newName; | 
|---|
| 86 |  | 
|---|
| 87 | const auto fileDestination = mFileDestination.left(mFileDestination.lastIndexOf('.')); | 
|---|
| 88 | const auto fileExtension = mFileDestination.mid(mFileDestination.lastIndexOf('.') + 1); | 
|---|
| 89 |  | 
|---|
| 90 | if (mFileSuffixIfFull == LogFileDisplay::DateTime) | 
|---|
| 91 | { | 
|---|
| 92 | newName | 
|---|
| 93 | = QString( "%1_%2.%3") | 
|---|
| 94 | .arg(fileDestination, QDateTime::currentDateTime().toString( "dd_MM_yy__hh_mm_ss"), fileExtension); | 
|---|
| 95 | } | 
|---|
| 96 | else | 
|---|
| 97 | newName = generateDuplicateFilename(fileDestination, fileExtension); | 
|---|
| 98 |  | 
|---|
| 99 | const auto renamed = file.rename(mFileDestination, newName); | 
|---|
| 100 |  | 
|---|
| 101 | return renamed ? newName : QString(); | 
|---|
| 102 | } | 
|---|
| 103 |  | 
|---|
| 104 | return QString(); | 
|---|
| 105 | } | 
|---|
| 106 |  | 
|---|
| 107 | QString QLoggerWriter::generateDuplicateFilename(const QString &fileDestination, const QString &fileExtension, | 
|---|
| 108 | int fileSuffixNumber) | 
|---|
| 109 | { | 
|---|
| 110 | QString path(fileDestination); | 
|---|
| 111 | if (fileSuffixNumber > 1) | 
|---|
| 112 | path = QString( "%1(%2).%3").arg(fileDestination, QString::number(fileSuffixNumber), fileExtension); | 
|---|
| 113 | else | 
|---|
| 114 | path.append(QString( ".%1").arg(fileExtension)); | 
|---|
| 115 |  | 
|---|
| 116 | // A name already exists, increment the number and check again | 
|---|
| 117 | if (QFileInfo::exists(path)) | 
|---|
| 118 | return generateDuplicateFilename(fileDestination, fileExtension, fileSuffixNumber + 1); | 
|---|
| 119 |  | 
|---|
| 120 | // No file exists at the given location, so no need to continue | 
|---|
| 121 | return path; | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | void QLoggerWriter::write(QVector<QString> messages) | 
|---|
| 125 | { | 
|---|
| 126 | // Write data to console | 
|---|
| 127 | if (mMode == LogMode::OnlyConsole) | 
|---|
| 128 | { | 
|---|
| 129 | for (const auto &message : messages) | 
|---|
| 130 | qInfo() << message; | 
|---|
| 131 |  | 
|---|
| 132 | return; | 
|---|
| 133 | } | 
|---|
| 134 |  | 
|---|
| 135 | // Write data to file | 
|---|
| 136 | QFile file(mFileDestination); | 
|---|
| 137 |  | 
|---|
| 138 | const auto prevFilename = renameFileIfFull(); | 
|---|
| 139 |  | 
|---|
| 140 | if (file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Append)) | 
|---|
| 141 | { | 
|---|
| 142 | QTextStream out(&file); | 
|---|
| 143 |  | 
|---|
| 144 | if (!prevFilename.isEmpty()) | 
|---|
| 145 | out << QString( "Previous log %1\n").arg(prevFilename); | 
|---|
| 146 |  | 
|---|
| 147 | for (const auto &message : messages) | 
|---|
| 148 | { | 
|---|
| 149 | out << message; | 
|---|
| 150 |  | 
|---|
| 151 | if (mMode == LogMode::Full) | 
|---|
| 152 | qInfo() << message; | 
|---|
| 153 | } | 
|---|
| 154 |  | 
|---|
| 155 | file.close(); | 
|---|
| 156 | } | 
|---|
| 157 | } | 
|---|
| 158 |  | 
|---|
| 159 | void QLoggerWriter::enqueue(const QDateTime &date, const QString &threadId, const QString &module, LogLevel level, | 
|---|
| 160 | const QString &function, const QString &fileName, int line, const QString &message) | 
|---|
| 161 | { | 
|---|
| 162 | QMutexLocker locker(&mutex); | 
|---|
| 163 |  | 
|---|
| 164 | if (mMode == LogMode::Disabled) | 
|---|
| 165 | return; | 
|---|
| 166 |  | 
|---|
| 167 | QString fileLine; | 
|---|
| 168 | if (mMessageOptions.testFlag(LogMessageDisplay::File) && mMessageOptions.testFlag(LogMessageDisplay::Line) | 
|---|
| 169 | && !fileName.isEmpty() && line > 0 && mLevel <= LogLevel::Debug) | 
|---|
| 170 | { | 
|---|
| 171 | fileLine = QString( "{%1:%2}").arg(fileName, QString::number(line)); | 
|---|
| 172 | } | 
|---|
| 173 | else if (mMessageOptions.testFlag(LogMessageDisplay::File) && mMessageOptions.testFlag(LogMessageDisplay::Function) | 
|---|
| 174 | && !fileName.isEmpty() && !function.isEmpty() && mLevel <= LogLevel::Debug) | 
|---|
| 175 | { | 
|---|
| 176 | fileLine = QString( "{%1}{%2}").arg(fileName, function); | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | QString text; | 
|---|
| 180 | if (mMessageOptions.testFlag(LogMessageDisplay::Default)) | 
|---|
| 181 | { | 
|---|
| 182 | text = QString( "[%1][%2][%3][%4]%5 %6") | 
|---|
| 183 | .arg(levelToText(level), module) | 
|---|
| 184 | .arg(date.toSecsSinceEpoch()) | 
|---|
| 185 | .arg(threadId, fileLine, message); | 
|---|
| 186 | } | 
|---|
| 187 | else | 
|---|
| 188 | { | 
|---|
| 189 | if (mMessageOptions.testFlag(LogMessageDisplay::LogLevel)) | 
|---|
| 190 | text.append(QString( "[%1]").arg(levelToText(level))); | 
|---|
| 191 |  | 
|---|
| 192 | if (mMessageOptions.testFlag(LogMessageDisplay::ModuleName)) | 
|---|
| 193 | text.append(QString( "[%1]").arg(module)); | 
|---|
| 194 |  | 
|---|
| 195 | if (mMessageOptions.testFlag(LogMessageDisplay::DateTime)) | 
|---|
| 196 | text.append(QString( "[%1]").arg(date.toSecsSinceEpoch())); | 
|---|
| 197 |  | 
|---|
| 198 | if (mMessageOptions.testFlag(LogMessageDisplay::ThreadId)) | 
|---|
| 199 | text.append(QString( "[%1]").arg(threadId)); | 
|---|
| 200 |  | 
|---|
| 201 | if (!fileLine.isEmpty()) | 
|---|
| 202 | { | 
|---|
| 203 | if (fileLine.startsWith(QChar::Space)) | 
|---|
| 204 | fileLine = fileLine.right(1); | 
|---|
| 205 |  | 
|---|
| 206 | text.append(fileLine); | 
|---|
| 207 | } | 
|---|
| 208 | if (mMessageOptions.testFlag(LogMessageDisplay::Message)) | 
|---|
| 209 | { | 
|---|
| 210 | if (text.isEmpty() || text.endsWith(QChar::Space)) | 
|---|
| 211 | text.append(QString( "%1").arg(message)); | 
|---|
| 212 | else | 
|---|
| 213 | text.append(QString( " %1").arg(message)); | 
|---|
| 214 | } | 
|---|
| 215 | } | 
|---|
| 216 |  | 
|---|
| 217 | text.append(QString::fromLatin1( "\n")); | 
|---|
| 218 |  | 
|---|
| 219 | mMessages.append({ threadId, text }); | 
|---|
| 220 |  | 
|---|
| 221 | if (!mIsStop) | 
|---|
| 222 | mQueueNotEmpty.wakeAll(); | 
|---|
| 223 | } | 
|---|
| 224 |  | 
|---|
| 225 | void QLoggerWriter::run() | 
|---|
| 226 | { | 
|---|
| 227 | if (!mQuit) | 
|---|
| 228 | { | 
|---|
| 229 | QMutexLocker locker(&mutex); | 
|---|
| 230 | mQueueNotEmpty.wait(&mutex); | 
|---|
| 231 | } | 
|---|
| 232 |  | 
|---|
| 233 | while (!mQuit) | 
|---|
| 234 | { | 
|---|
| 235 | decltype(mMessages) copy; | 
|---|
| 236 |  | 
|---|
| 237 | { | 
|---|
| 238 | QMutexLocker locker(&mutex); | 
|---|
| 239 | std::swap(copy, mMessages); | 
|---|
| 240 | } | 
|---|
| 241 |  | 
|---|
| 242 | write(std::move(copy)); | 
|---|
| 243 |  | 
|---|
| 244 | if (!mQuit) | 
|---|
| 245 | { | 
|---|
| 246 | QMutexLocker locker(&mutex); | 
|---|
| 247 | mQueueNotEmpty.wait(&mutex); | 
|---|
| 248 | } | 
|---|
| 249 | } | 
|---|
| 250 | } | 
|---|
| 251 |  | 
|---|
| 252 | void QLoggerWriter::closeDestination() | 
|---|
| 253 | { | 
|---|
| 254 | QMutexLocker locker(&mutex); | 
|---|
| 255 | mQuit = true; | 
|---|
| 256 | mQueueNotEmpty.wakeAll(); | 
|---|
| 257 | } | 
|---|
| 258 |  | 
|---|
| 259 | } | 
|---|
| 260 |  | 
|---|