Skip to content

Commit 4481d87

Browse files
author
Timothy Dodd
committed
Refactor log level handling for consistency and efficiency
Updated log level parsing to return LogLevel.Any for undetected or empty levels across multiple files. Introduced compiled regex patterns for improved detection performance and refactored existing logic for clarity. Ensured consistent behavior in log level defaults across the application.
1 parent d33256f commit 4481d87

3 files changed

Lines changed: 43 additions & 38 deletions

File tree

src/LogMkAgent/Services/LogWatcher.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,8 +501,8 @@ private LogLevel GetLogLevel(string logLine)
501501
return LogLevel.Any;
502502

503503
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;
504+
// Parser now returns Any for undetected levels, Information only for explicit INFO logs
505+
return parsedLevel;
506506
}
507507

508508
private LogLine ParseLogLine(string originalLine, string cleanLine, string podName, string deploymentName, LogLevel logLevel)

src/LogMkApi/Controllers/LogController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ public async Task<ActionResult<LogResponse>> CreateSingle(
489489
var timestamp = logEntry.Timestamp ?? ParseTimestampFromLine(logEntry.Line) ?? receivedAt;
490490

491491
// Parse log level from line if not provided
492-
var logLevel = logEntry.LogLevel ?? ParseLogLevelFromLine(logEntry.Line) ?? LogMkCommon.LogLevel.Information;
492+
var logLevel = logEntry.LogLevel ?? ParseLogLevelFromLine(logEntry.Line) ?? LogMkCommon.LogLevel.Any;
493493

494494
// Validate timestamp
495495
if (timestamp > DateTimeOffset.UtcNow.AddMinutes(5))

src/LogMkCommon/LogParser.cs

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public static class LogParser
6565
public static LogLevel ParseLogLevel(string line)
6666
{
6767
if (string.IsNullOrWhiteSpace(line))
68-
return LogLevel.Information;
68+
return LogLevel.Any;
6969

7070
// Remove ANSI escape sequences first
7171
var cleanLine = RemoveANSIEscapeRegex.Replace(line, string.Empty);
@@ -76,51 +76,56 @@ public static LogLevel ParseLogLevel(string line)
7676
return GetLogLevel(processedLine);
7777
}
7878

79+
// Compiled regex patterns for efficient log level detection
80+
private static readonly Regex ErrorPattern = new(@"\b(ERROR|ERR|FAIL)\b|ERROR:|FAIL:|\[ERROR\]",
81+
RegexOptions.Compiled | RegexOptions.IgnoreCase);
82+
private static readonly Regex WarningPattern = new(@"\b(WARN|WARNING|WRN)\b|WARN:|WARNING:|\[WARN\]|\[WARNING\]",
83+
RegexOptions.Compiled | RegexOptions.IgnoreCase);
84+
private static readonly Regex InfoPattern = new(@"\b(INFO|INFORMATION|INF)\b|INFO:|INFORMATION:|\[INFO\]|\[INFORMATION\]",
85+
RegexOptions.Compiled | RegexOptions.IgnoreCase);
86+
private static readonly Regex DebugPattern = new(@"\b(DEBUG|DBG|DBUG)\b|DEBUG:|DBUG:|\[DEBUG\]",
87+
RegexOptions.Compiled | RegexOptions.IgnoreCase);
88+
private static readonly Regex TracePattern = new(@"\b(TRACE|TRC)\b|TRACE:|\[TRACE\]",
89+
RegexOptions.Compiled | RegexOptions.IgnoreCase);
90+
7991
private static LogLevel GetLogLevel(string logLine)
8092
{
8193
if (string.IsNullOrEmpty(logLine))
82-
return LogLevel.Information;
94+
return LogLevel.Any;
8395

84-
// Check if line is JSON and try to parse level from it
85-
if (logLine.TrimStart().StartsWith('{') && logLine.TrimEnd().EndsWith('}'))
96+
// Quick check for JSON (avoid trim operations unless needed)
97+
int firstNonWhitespace = 0;
98+
while (firstNonWhitespace < logLine.Length && char.IsWhiteSpace(logLine[firstNonWhitespace]))
99+
firstNonWhitespace++;
100+
101+
if (firstNonWhitespace < logLine.Length && logLine[firstNonWhitespace] == '{')
86102
{
87-
var jsonLevel = ParseJsonLogLevel(logLine);
88-
if (jsonLevel != LogLevel.Information) // If we found a specific level in JSON
89-
return jsonLevel;
103+
int lastNonWhitespace = logLine.Length - 1;
104+
while (lastNonWhitespace >= 0 && char.IsWhiteSpace(logLine[lastNonWhitespace]))
105+
lastNonWhitespace--;
106+
107+
if (lastNonWhitespace >= 0 && logLine[lastNonWhitespace] == '}')
108+
{
109+
var jsonLevel = ParseJsonLogLevel(logLine);
110+
if (jsonLevel != LogLevel.Any)
111+
return jsonLevel;
112+
}
90113
}
91114

92-
var upperLine = logLine.ToUpperInvariant();
93-
94-
// Check for common log level patterns with boundaries
95-
if (ContainsLogLevel(upperLine, "[ERROR]", "ERROR:", " ERR ", "ERROR ", "FAIL:"))
96-
return LogLevel.Error;
97-
if (ContainsLogLevel(upperLine, "[WARN]", "[WARNING]", "WARNING:", " WARN ", " WRN ", "WARN:"))
98-
return LogLevel.Warning;
99-
if (ContainsLogLevel(upperLine, "[INFO]", "[INFORMATION]", "INFORMATION:", " INFO ", " INF ", "INFO:"))
115+
// Use compiled regex patterns for efficient matching
116+
// Check in order of frequency (errors and warnings are typically less common)
117+
if (InfoPattern.IsMatch(logLine))
100118
return LogLevel.Information;
101-
if (ContainsLogLevel(upperLine, "[DEBUG]", "DEBUG:", " DBG ", "DBUG:", " DEBUG "))
102-
return LogLevel.Debug;
103-
if (ContainsLogLevel(upperLine, "[TRACE]", "TRACE:", " TRC ", " TRACE "))
104-
return LogLevel.Trace;
105-
106-
// Check for log level at the beginning of the line
107-
if (upperLine.StartsWith("ERROR") || upperLine.StartsWith("ERR"))
119+
if (ErrorPattern.IsMatch(logLine))
108120
return LogLevel.Error;
109-
if (upperLine.StartsWith("WARN") || upperLine.StartsWith("WARNING"))
121+
if (WarningPattern.IsMatch(logLine))
110122
return LogLevel.Warning;
111-
if (upperLine.StartsWith("INFO") || upperLine.StartsWith("INFORMATION"))
112-
return LogLevel.Information;
113-
if (upperLine.StartsWith("DEBUG") || upperLine.StartsWith("DBG"))
123+
if (DebugPattern.IsMatch(logLine))
114124
return LogLevel.Debug;
115-
if (upperLine.StartsWith("TRACE") || upperLine.StartsWith("TRC"))
125+
if (TracePattern.IsMatch(logLine))
116126
return LogLevel.Trace;
117127

118-
return LogLevel.Information; // Default
119-
}
120-
121-
private static bool ContainsLogLevel(string upperLine, params string[] patterns)
122-
{
123-
return patterns.Any(pattern => upperLine.Contains(pattern));
128+
return LogLevel.Any; // Default when no specific level detected
124129
}
125130

126131
public static string ParseContainerLogFormat(string originalLine, string cleanLine)
@@ -283,7 +288,7 @@ private static LogLevel ParseJsonLogLevel(string line)
283288
"INFO" or "INFORMATION" => LogLevel.Information,
284289
"DEBUG" or "DBG" => LogLevel.Debug,
285290
"TRACE" or "TRC" or "VERBOSE" => LogLevel.Trace,
286-
_ => LogLevel.Information
291+
_ => LogLevel.Any
287292
};
288293
}
289294
}
@@ -294,6 +299,6 @@ private static LogLevel ParseJsonLogLevel(string line)
294299
// Not valid JSON or parsing error, ignore
295300
}
296301

297-
return LogLevel.Information;
302+
return LogLevel.Any;
298303
}
299304
}

0 commit comments

Comments
 (0)