File indexing completed on 2024-05-19 05:49:12

0001 #include <QElapsedTimer>
0002 #include <QRegularExpression>
0003 
0004 #include <KLocalizedString>
0005 
0006 #include "ksystemlogConfig.h"
0007 #include "ksystemlog_debug.h"
0008 
0009 #include "logViewModel.h"
0010 
0011 #include "auditAnalyzer.h"
0012 #include "localLogFileReader.h"
0013 
0014 AuditAnalyzer::AuditAnalyzer(LogMode *logMode)
0015     : FileAnalyzer(logMode)
0016 {
0017 }
0018 
0019 LogViewColumns AuditAnalyzer::initColumns()
0020 {
0021     LogViewColumns columns;
0022     columns.addColumn(LogViewColumn(i18n("Date"), true, false));
0023     columns.addColumn(LogViewColumn(i18n("Message"), true, false));
0024 
0025     return columns;
0026 }
0027 
0028 void AuditAnalyzer::setLogFiles(const QVector<LogFile> &logFiles)
0029 {
0030     // Remove previous files
0031     deleteLogFiles();
0032 
0033     for (const LogFile &logFile : logFiles) {
0034         LogFileReader *logFileReader = createLogFileReader(logFile);
0035         mLogFileReaders.append(logFileReader);
0036 
0037         connect(logFileReader, &LogFileReader::contentChanged, this, &AuditAnalyzer::logFileChanged);
0038         connect(logFileReader, &LogFileReader::statusBarChanged, this, &Analyzer::statusBarChanged);
0039         connect(logFileReader, &LogFileReader::errorOccured, this, &Analyzer::errorOccured);
0040     }
0041 }
0042 
0043 LogFileReader *AuditAnalyzer::createLogFileReader(const LogFile &logFile)
0044 {
0045     return new LocalLogFileReader(logFile);
0046 }
0047 
0048 Analyzer::LogFileSortMode AuditAnalyzer::logFileSortMode()
0049 {
0050     return Analyzer::AscendingSortedLogFile;
0051 }
0052 
0053 LogLine *AuditAnalyzer::parseMessage(const QString &logLine, const LogFile &originalFile)
0054 {
0055     if (logLine.length() < 15) {
0056         return nullptr;
0057     }
0058 
0059     static QRegularExpression re(QStringLiteral("^type=(.*)\\s.*msg=audit\\((\\d*\\.\\d*).*"));
0060     QRegularExpressionMatch match = re.match(logLine);
0061 
0062     if (!match.hasMatch()) {
0063         return nullptr;
0064     }
0065 
0066     const QString messageType = match.captured(1);
0067 
0068     const qint64 msecs = qint64(match.captured(2).toDouble() * 1000.);
0069 
0070     const QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(msecs);
0071 
0072     QString message(logLine);
0073     re.setPattern(QStringLiteral("^type=.*\\):\\s"));
0074     message.remove(re);
0075 
0076     QStringList list;
0077     list.append(messageType);
0078     list.append(message);
0079 
0080     return new LogLine(mLogLineInternalIdGenerator++, dateTime, list, originalFile.url().path(), originalFile.defaultLogLevel(), mLogMode);
0081 }
0082 
0083 void AuditAnalyzer::deleteLogFiles()
0084 {
0085     watchLogFiles(false);
0086 
0087     // Remove the watching on the monitored files
0088     qDeleteAll(mLogFileReaders);
0089     mLogFileReaders.clear();
0090 }
0091 
0092 int AuditAnalyzer::insertLines(const QStringList &bufferedLines, const LogFile &logFile, ReadingMode readingMode)
0093 {
0094     qCDebug(KSYSTEMLOG) << "Inserting lines...";
0095 
0096     // If there is no line
0097     if (bufferedLines.isEmpty()) {
0098         qCWarning(KSYSTEMLOG) << "File is empty : " << logFile.url().path();
0099     }
0100 
0101     int stop = 0;
0102     int currentPosition = 0;
0103 
0104     QList<QStringList> eventLines;
0105     QStringList event;
0106     QString msgField;
0107     QString curMsgField;
0108 
0109     for (const auto &line : bufferedLines) {
0110         msgField = getMsgField(line);
0111 
0112         if (msgField != curMsgField) {
0113             eventLines.append(event);
0114             curMsgField = msgField;
0115             event.clear();
0116         }
0117 
0118         event.push_front(line);
0119     }
0120 
0121     eventLines.append(event);
0122 
0123     QListIterator<QStringList> it(eventLines);
0124     /**
0125      * If the log file is sorted, then we can ignore the first lines
0126      * if there are more lines in the log file than the max lines
0127      *
0128      * TODO Read revertly the file and ignore last lines if we are in Descending mode
0129      */
0130     qCDebug(KSYSTEMLOG) << "Log file Sort mode is " << logFileSortMode();
0131     if (logFileSortMode() == Analyzer::AscendingSortedLogFile) {
0132         // Calculate how many lines we will ignore
0133         if (eventLines.size() > KSystemLogConfig::maxLines()) {
0134             stop = eventLines.size() - KSystemLogConfig::maxLines();
0135         }
0136 
0137         // Ignore those lines
0138         while (currentPosition < stop) {
0139             it.next();
0140             ++currentPosition;
0141         }
0142     }
0143 
0144     int insertedLogLineCount = 0;
0145 
0146     while (currentPosition < eventLines.size()) {
0147         if (insertLine(it.next(), logFile, readingMode)) {
0148             insertedLogLineCount++;
0149         }
0150 
0151         if (readingMode == Analyzer::FullRead) {
0152             informOpeningProgress(currentPosition, (eventLines.size() - 1) - stop);
0153         }
0154 
0155         ++currentPosition;
0156     }
0157 
0158     return insertedLogLineCount;
0159 }
0160 
0161 bool AuditAnalyzer::insertLine(const QStringList &event, const LogFile &originalFile, ReadingMode readingMode)
0162 {
0163     LogLine *line = parseEvent(event, originalFile);
0164 
0165     // Invalid log line
0166     if (!line) {
0167         return false;
0168     }
0169 
0170     // On full reading, it is not needed to display the recent status
0171     if (readingMode == Analyzer::FullRead) {
0172         line->setRecent(false);
0173     }
0174 
0175     return mLogViewModel->insertNewLogLine(line);
0176 }
0177 
0178 void AuditAnalyzer::logFileChanged(LogFileReader *logFileReader, ReadingMode readingMode, const QStringList &content)
0179 {
0180     const QString filePath = logFileReader->logFile().url().path();
0181     if (readingMode == Analyzer::FullRead) {
0182         qCDebug(KSYSTEMLOG) << "File " << filePath << " has been modified on full read.";
0183     } else {
0184         qCDebug(KSYSTEMLOG) << "File " << filePath << " has been modified on partial read";
0185     }
0186 
0187     if (mParsingPaused == true) {
0188         qCDebug(KSYSTEMLOG) << "Pause enabled. Nothing read.";
0189         return;
0190     }
0191 
0192     qCDebug(KSYSTEMLOG) << "Locking file modifications of " << filePath;
0193     mInsertionLocking.lock();
0194     qCDebug(KSYSTEMLOG) << "Unlocking file modifications of " << filePath;
0195 
0196     QElapsedTimer benchmark;
0197     benchmark.start();
0198 
0199     int insertedLogLineCount;
0200 
0201     mLogViewModel->startingMultipleInsertions();
0202 
0203     if (readingMode == Analyzer::UpdatingRead) {
0204         insertedLogLineCount = insertLines(content, logFileReader->logFile(), Analyzer::UpdatingRead);
0205     } else {
0206         qCDebug(KSYSTEMLOG) << "Reading file " << filePath;
0207 
0208         Q_EMIT statusBarChanged(i18n("Opening '%1'...", filePath));
0209 
0210         // Inform that we are now reading the "index" file
0211         Q_EMIT readFileStarted(*mLogMode, logFileReader->logFile(), mLogFileReaders.count() - mLogFileReaders.indexOf(logFileReader), mLogFileReaders.count());
0212 
0213         insertedLogLineCount = insertLines(content, logFileReader->logFile(), Analyzer::FullRead);
0214 
0215         Q_EMIT statusBarChanged(i18n("Log file '%1' loaded successfully.", filePath));
0216     }
0217 
0218     mLogViewModel->endingMultipleInsertions(readingMode, insertedLogLineCount);
0219 
0220     // Inform connected LoadingBar that the reading is now finished
0221     Q_EMIT readEnded();
0222 
0223     // Inform LogManager that new lines have been added
0224     Q_EMIT logUpdated(insertedLogLineCount);
0225 
0226     // Inform MainWindow status bar
0227     Q_EMIT statusBarChanged(i18n("Log file '%1' has changed.", filePath));
0228 
0229     qCDebug(KSYSTEMLOG) << "Updating log files in " << benchmark.elapsed() << " ms";
0230 
0231     mInsertionLocking.unlock();
0232 }
0233 
0234 QString AuditAnalyzer::getMsgField(const QString &logLine)
0235 {
0236     static const QRegularExpression re(QStringLiteral("^.*msg=audit\\((\\d*\\.\\d*:\\d*)\\)"));
0237     const QRegularExpressionMatch match = re.match(logLine);
0238 
0239     if (!match.hasMatch()) {
0240         return QString();
0241     } else {
0242         return match.captured(1);
0243     }
0244 }
0245 
0246 LogLine *AuditAnalyzer::parseEvent(const QStringList &event, const LogFile &originalFile)
0247 {
0248     if (event.isEmpty()) {
0249         return nullptr;
0250     }
0251 
0252     QRegularExpression re(QStringLiteral("^.*msg=audit\\((\\d*\\.\\d*).*"));
0253     QRegularExpressionMatch match = re.match(event.at(0));
0254 
0255     if (!match.hasMatch()) {
0256         return nullptr;
0257     }
0258 
0259     const qint64 msecs = qint64(match.captured(1).toDouble() * 1000.);
0260     const QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(msecs);
0261 
0262     QStringList messages;
0263     QString message;
0264 
0265     for (const auto &msg : event) {
0266         message = m_conv.convertMessage(msg);
0267         re.setPattern(QStringLiteral("^type="));
0268         message.remove(re);
0269         re.setPattern(QStringLiteral("\\smsg=audit\\(\\d*\\.\\d*:\\d*\\)"));
0270         message.remove(re);
0271         messages.append(message);
0272     }
0273 
0274     QStringList list;
0275     list.append(messages.join(QStringLiteral(" \n")));
0276 
0277     return new LogLine(mLogLineInternalIdGenerator++, dateTime, list, originalFile.url().path(), originalFile.defaultLogLevel(), mLogMode);
0278 }
0279 
0280 #include "moc_auditAnalyzer.cpp"