11#include " Pipeline.h"
2- #include " ../core/LogParser.h"
2+ #include " ../core/PatternLogParser.h"
3+ #include " ../core/StandardLogParser.h"
34#include " ../io/MemoryMappedFile.h"
45#include " KeywordHitAnalyzer.h"
56#include " LevelCountAnalyzer.h"
@@ -141,6 +142,18 @@ AnalysisResult Pipeline::run(const std::string &inputPath,
141142 const uint64_t progressReportInterval = 1024 * 1024 ; // 1MB
142143 uint64_t bytesSinceLastReport = 0 ;
143144
145+ // Thread-local timeline aggregator
146+ std::map<uint64_t , std::pair<uint32_t , uint32_t >>
147+ localTimeline; // Key -> {Error, Warning}
148+
149+ // Setup parser
150+ std::unique_ptr<ILogParser> parser;
151+ if (!context.customPattern .empty ()) {
152+ parser = std::make_unique<PatternLogParser>(context.customPattern );
153+ } else {
154+ parser = std::make_unique<StandardLogParser>();
155+ }
156+
144157 while (currentPos < endOffset) {
145158 if (wasCancelled && *wasCancelled)
146159 return localResult;
@@ -161,7 +174,7 @@ AnalysisResult Pipeline::run(const std::string &inputPath,
161174 lineNumber++;
162175
163176 // Parse
164- ParseResult parseResult = LogParser:: parse (line, lineNumber);
177+ ParseResult parseResult = parser-> parse (line, lineNumber);
165178
166179 if (std::holds_alternative<LogEntry>(parseResult)) {
167180 localResult.parsedLines ++; // thread-local count
@@ -173,6 +186,52 @@ AnalysisResult Pipeline::run(const std::string &inputPath,
173186 for (auto &analyzer : analyzers) {
174187 analyzer->process (entry);
175188 }
189+
190+ // --- Populate Heatmap & Timeline ---
191+ if (entry.ts .month > 0 ) { // Valid check heuristic
192+ // Calculate day of week (0=Sunday)
193+ // Zeller's congruence or just std::tm if we reused it?
194+ // Optimization: We manually parsed ts, so we don't have tm
195+ // directly. Let's rely on a helper or basic calculation. For speed,
196+ // C++20 chrono is best but we are C++17 here (mostly). Let's do a
197+ // simple Zeller for Day of Week. Zeller algorithm (0=Saturday,
198+ // 1=Sunday.. for the math, adjusted to 0=Sun):
199+ int y = entry.ts .year ;
200+ int m = entry.ts .month ;
201+ int q = entry.ts .day ;
202+ if (m < 3 ) {
203+ m += 12 ;
204+ y -= 1 ;
205+ }
206+ int K = y % 100 ;
207+ int J = y / 100 ;
208+ int h = (q + 13 * (m + 1 ) / 5 + K + K / 4 + J / 4 + 5 * J) % 7 ;
209+ // h is 0=Saturday, 1=Sunday...6=Friday
210+ // Map to 0=Sunday...6=Saturday
211+ int dayIdx = (h + 1 ) % 7 ; // Now 0=Sun, 1=Mon...6=Sat
212+
213+ int hourIdx = entry.ts .hour ;
214+ if (dayIdx >= 0 && dayIdx < 7 && hourIdx >= 0 && hourIdx < 24 ) {
215+ localResult.heatmap [dayIdx][hourIdx]++;
216+ }
217+
218+ // Timeline: Bucket by minute
219+ // We need a monotonic timestamp.
220+ // Let's assume entry.ts can convert to unix time approx or we just
221+ // use the raw components. Let's use a simplified 64-bit sort key:
222+ // YYYYMMDDHHMM This is sufficient for sorting and buckets.
223+ uint64_t timeKey = (uint64_t )entry.ts .year * 100000000 +
224+ (uint64_t )entry.ts .month * 1000000 +
225+ (uint64_t )entry.ts .day * 10000 +
226+ (uint64_t )entry.ts .hour * 100 +
227+ (uint64_t )entry.ts .minute ;
228+
229+ if (entry.level == LogLevel::ERROR) {
230+ localTimeline[timeKey].first ++;
231+ } else if (entry.level == LogLevel::WARNING) {
232+ localTimeline[timeKey].second ++;
233+ }
234+ }
176235 }
177236 } else {
178237 localResult.invalidLines ++;
@@ -208,6 +267,13 @@ AnalysisResult Pipeline::run(const std::string &inputPath,
208267 for (auto &analyzer : analyzers) {
209268 analyzer->finalize (localResult);
210269 }
270+
271+ // Flatten timeline map to vector
272+ localResult.timeline .reserve (localTimeline.size ());
273+ for (const auto &[timeKey, counts] : localTimeline) {
274+ localResult.timeline .push_back ({timeKey, counts.first , counts.second });
275+ }
276+
211277 return localResult;
212278 };
213279
0 commit comments