Skip to content

Commit 20daee9

Browse files
author
Timothy Dodd
committed
Refactor log handling and implement work queue system
- Refactored `LogWatcher` to move ANSI escape sequence handling and log parsing to `LogParser`. - Updated `LogController` to add an endpoint for creating single log entries using a work queue for asynchronous log purges. - Introduced a new `WorkQueue` table in the database schema to manage log purge operations with status tracking. - Implemented `WorkQueueProcessorService` as a background service for processing queued log purges with real-time updates via SignalR. - Added `WorkQueueComponent` in the frontend to display the status of queued operations and allow cancellation of pending jobs. - Created new models and DTOs for work queue items for API communication. - Developed Angular service for managing work queue operations, including queuing purges and retrieving status. - Enhanced UI for settings page and work queue component, improving user feedback and handling of disabled states. - Improved error handling and notifications for better user experience. - Overall architecture enhancements for scalability and maintainability with a focus on asynchronous processing.
1 parent f873200 commit 20daee9

23 files changed

Lines changed: 1853 additions & 161 deletions

src/LogMkAgent/Services/LogWatcher.cs

Lines changed: 10 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ public class LogWatcher : BackgroundService
3333
private readonly SemaphoreSlim _fileSemaphore = new(Environment.ProcessorCount);
3434

3535
// Static readonly for better performance
36-
private static readonly string RemoveANSIEscapePattern = @"\x1B\[[0-9;]*[A-Za-z]";
37-
private static readonly Regex RemoveANSIEscapeRegex = new(RemoveANSIEscapePattern, RegexOptions.Compiled);
36+
// Moved to LogParser class in LogMkCommon
3837

3938
// Retry configuration
4039
private const int MaxRetryAttempts = 3;
@@ -439,7 +438,7 @@ private async Task ReadNewLinesAsync(PodInfo info, CancellationToken stoppingTok
439438
private LogLine? ProcessLogLine(string line, PodInfo info, PodSettings podSettings, DeploymentSettings deploymentSettings, ref bool foundRecent)
440439
{
441440
// Remove ANSI escape sequences
442-
var cleanLine = RemoveANSIEscapeRegex.Replace(line, string.Empty);
441+
var cleanLine = LogParser.RemoveANSIEscapeSequences(line);
443442

444443
// Parse container format (timestamp stdout/stderr message)
445444
var processedLine = ParseContainerLogFormat(line, cleanLine);
@@ -472,25 +471,7 @@ private async Task ReadNewLinesAsync(PodInfo info, CancellationToken stoppingTok
472471

473472
private string ParseContainerLogFormat(string originalLine, string cleanLine)
474473
{
475-
var firstSpace = originalLine.IndexOf(' ');
476-
if (firstSpace <= 0)
477-
return cleanLine;
478-
479-
var secondSpace = originalLine.IndexOf(' ', firstSpace + 1);
480-
if (secondSpace <= 0)
481-
return cleanLine;
482-
483-
var thirdSpace = originalLine.IndexOf(' ', secondSpace + 1);
484-
if (thirdSpace <= 0)
485-
return cleanLine;
486-
487-
var outType = originalLine.Substring(firstSpace + 1, secondSpace - firstSpace - 1);
488-
if (outType == "stdout" || outType == "stderr")
489-
{
490-
return cleanLine.Substring(thirdSpace + 1 - (originalLine.Length - cleanLine.Length));
491-
}
492-
493-
return cleanLine;
474+
return LogParser.ParseContainerLogFormat(originalLine, cleanLine);
494475
}
495476

496477
private LogLevel GetLogLevelCached(string logLine)
@@ -519,28 +500,9 @@ private LogLevel GetLogLevel(string logLine)
519500
if (string.IsNullOrEmpty(logLine))
520501
return LogLevel.Any;
521502

522-
var lowerLogLine = logLine.ToLowerInvariant();
523-
524-
var errorIndex = FindFirst(lowerLogLine, "error", "err");
525-
var warningIndex = FindFirst(lowerLogLine, "warning", "warn", "wrn");
526-
var infoIndex = FindFirst(lowerLogLine, "information", "info", "inf");
527-
var debugIndex = FindFirst(lowerLogLine, "debug", "dbg");
528-
529-
var firstIndex = MinNonNegative(errorIndex, warningIndex, infoIndex, debugIndex);
530-
531-
if (firstIndex < 0)
532-
return LogLevel.Any; // No recognizable log level found
533-
if (firstIndex == errorIndex)
534-
return LogLevel.Error;
535-
if (firstIndex == warningIndex)
536-
return LogLevel.Warning;
537-
if (firstIndex == infoIndex)
538-
return LogLevel.Information;
539-
if (firstIndex == debugIndex)
540-
return LogLevel.Debug;
541-
542-
543-
return LogLevel.Trace; // Default to Trace if no specific level found
503+
var parsedLevel = LogParser.ParseLogLevel(logLine);
504+
// Return Any if it's Information (default) to maintain existing behavior
505+
return parsedLevel == LogLevel.Information ? LogLevel.Any : parsedLevel;
544506
}
545507

546508
private LogLine ParseLogLine(string originalLine, string cleanLine, string podName, string deploymentName, LogLevel logLevel)
@@ -559,65 +521,20 @@ private LogLine ParseLogLine(string originalLine, string cleanLine, string podNa
559521

560522
private DateTimeOffset? ParseTimestamp(string line)
561523
{
562-
var firstSpace = line.IndexOf(' ');
563-
if (firstSpace < 0)
564-
return null;
565-
566-
var timestampStr = line.Substring(0, firstSpace);
567-
568-
try
569-
{
570-
timestampStr = TruncateFractionalSeconds(timestampStr, 7);
571-
572-
if (DateTimeOffset.TryParseExact(timestampStr, "yyyy-MM-ddTHH:mm:ss.fffffffZ",
573-
CultureInfo.InvariantCulture, DateTimeStyles.None, out var timestamp))
574-
{
575-
return timestamp;
576-
}
577-
}
578-
catch (Exception ex)
579-
{
580-
_logger.LogDebug(ex, "Failed to parse timestamp: {Timestamp}", timestampStr);
581-
}
582-
583-
return null;
584-
}
585-
586-
private static string TruncateFractionalSeconds(string timestamp, int maxFractionalDigits)
587-
{
588-
var dotIndex = timestamp.IndexOf('.');
589-
if (dotIndex == -1)
590-
return timestamp;
591-
592-
var endIndex = dotIndex + maxFractionalDigits + 1;
593-
if (endIndex >= timestamp.Length - 1)
594-
return timestamp;
595-
596-
return timestamp.Substring(0, endIndex) + "Z";
524+
return LogParser.ParseTimestamp(line);
597525
}
598526

599-
private static int FindFirst(string line, params string[] keywords)
600-
{
601-
return keywords
602-
.Select(keyword => line.IndexOf(keyword, StringComparison.Ordinal))
603-
.Where(index => index >= 0)
604-
.DefaultIfEmpty(-1)
605-
.Min();
606-
}
527+
// TruncateFractionalSeconds moved to LogParser class
607528

608-
private static int MinNonNegative(params int[] values)
609-
{
610-
var validValues = values.Where(v => v >= 0);
611-
return validValues.Any() ? validValues.Min() : -1;
612-
}
529+
// Helper methods moved to LogParser class
613530

614531
private bool IsLineContinuation(string line, PodInfo info, DateTimeOffset lastTimestamp)
615532
{
616533
if (string.IsNullOrWhiteSpace(line))
617534
return false;
618535

619536
// Parse container format to get the actual log content
620-
var cleanLine = RemoveANSIEscapeRegex.Replace(line, string.Empty);
537+
var cleanLine = LogParser.RemoveANSIEscapeSequences(line);
621538
var processedLine = ParseContainerLogFormat(line, cleanLine);
622539

623540
// Check if line has a timestamp at the beginning

0 commit comments

Comments
 (0)