diff --git a/ProjectPlugins/CodexPlugin/CodexLogLine.cs b/ProjectPlugins/CodexPlugin/CodexLogLine.cs new file mode 100644 index 0000000..d275814 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/CodexLogLine.cs @@ -0,0 +1,105 @@ +using System.Globalization; + +namespace CodexPlugin +{ + public class CodexLogLine + { + public static CodexLogLine? Parse(string line) + { + try + { + if (string.IsNullOrEmpty(line) || + line.Length < 34 || + line[3] != ' ' || + line[33] != ' ') return null; + + line = line.Replace(Environment.NewLine, string.Empty); + + var level = line.Substring(0, 3); + var dtLine = line.Substring(4, 23); + + var firstEqualSign = line.IndexOf('='); + var msgStart = 34; + var msgEnd = line.Substring(0, firstEqualSign).LastIndexOf(' '); + var msg = line.Substring(msgStart, msgEnd - msgStart).Trim(); + var attrsLine = line.Substring(msgEnd); + + var attrs = SplitAttrs(attrsLine); + + var format = "yyyy-MM-dd HH:mm:ss.fff"; + var dt = DateTime.ParseExact(dtLine, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToUniversalTime(); + + return new CodexLogLine() + { + LogLevel = level, + TimestampUtc = dt, + Message = msg, + Attributes = attrs + }; + } + catch + { + return null; + } + } + + public string LogLevel { get; set; } = string.Empty; + public DateTime TimestampUtc { get; set; } + public string Message { get; set; } = string.Empty; + public Dictionary Attributes { get; private set; } = new Dictionary(); + + /// + /// After too much time spent cursing at regexes, here's what I got: + /// Parses input string into 'key=value' pair, considerate of quoted (") values. + /// + private static Dictionary SplitAttrs(string input) + { + input += " "; + var result = new Dictionary(); + + var key = string.Empty; + var value = string.Empty; + var mode = 1; + var inQuote = false; + + foreach (var c in input) + { + if (mode == 1) + { + if (c == '=') mode = 2; + else if (c == ' ') + { + if (string.IsNullOrEmpty(key)) continue; + else + { + result.Add(key, string.Empty); + key = string.Empty; + value = string.Empty; + } + } + else key += c; + } + else if (mode == 2) + { + if (c == ' ' && !inQuote) + { + result.Add(key, value); + key = string.Empty; + value = string.Empty; + mode = 1; + } + else if (c == '\"') + { + inQuote = !inQuote; + } + else + { + value += c; + } + } + } + + return result; + } + } +} diff --git a/Tests/CodexTests/BasicTests/LogHelperTests.cs b/Tests/CodexTests/BasicTests/LogHelperTests.cs index 33c509c..8b6ba5e 100644 --- a/Tests/CodexTests/BasicTests/LogHelperTests.cs +++ b/Tests/CodexTests/BasicTests/LogHelperTests.cs @@ -40,38 +40,16 @@ namespace CodexTests.BasicTests var map = new Dictionary(); log.IterateLines(line => { - if (string.IsNullOrEmpty(line) || - !line.Contains(" ") || - !line.Contains("=") || - line.Length < 34 || - line[33] != ' ' - ) return; + var log = CodexLogLine.Parse(line); + if (log == null) return; if (startUtc.HasValue) { - var timestampLine = line.Substring(4, 23); - var timestamp = DateTime.Parse(timestampLine); - if (timestamp < startUtc) return; + if (log.TimestampUtc < startUtc) return; } - // "INF 2024-04-14 10:40:50.042+00:00 Creating a private key and saving it tid=1 count=2" - var start = 34; - var msg = line.Substring(start); - - // "Creating a private key and saving it tid=1 count=2" - var firstEqualSign = msg.IndexOf("="); - msg = msg.Substring(0, firstEqualSign); - - // "Creating a private key and saving it tid" - var lastSpace = msg.LastIndexOf(" "); - msg = msg.Substring(0, lastSpace); - - // "Creating a private key and saving it " - msg = msg.Trim(); - - // "Creating a private key and saving it" - if (map.ContainsKey(msg)) map[msg] += 1; - else map.Add(msg, 1); + if (map.ContainsKey(log.Message)) map[log.Message] += 1; + else map.Add(log.Message, 1); }); return map; }