From 006961df5e273dd916833c666b1a86731bb00006 Mon Sep 17 00:00:00 2001 From: BRUNER Patrick Date: Fri, 19 Jun 2026 12:17:01 +0200 Subject: [PATCH 1/2] Fix for Issue #460: a bracketed field containing nested brackets (e.g. "[ProgramName/MethodName[41]]") must stay in a single column instead of being split at the first inner ']'. --- .gitattributes | 7 +++++ .../Columnizer/SquareBracketColumnizer.cs | 19 +++++++++++- .../SquareBracketColumnizerTest.cs | 31 +++++++++++++++++++ src/LogExpert.Tests/LogExpert.Tests.csproj | 3 ++ .../SquareBracketColumnizerTest_06.txt | 5 +++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 .gitattributes create mode 100644 src/LogExpert.Tests/TestData/SquareBracketColumnizerTest_06.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..56b578ee --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# Generated at release time by `nuget-license | Set-Content` on a Windows runner, +# which writes CRLF. Pin to LF so the regenerated file matches the committed blob +# regardless of the runner's core.autocrlf setting — otherwise releases would +# commit pure line-ending churn (or treat the file as binary). See release.yml. +# (Pattern has no slash so it matches the file regardless of path — its directory +# "Solution Items" contains a space, which .gitattributes cannot express in a path.) +usedComponents.json text eol=lf diff --git a/src/LogExpert.Core/Classes/Columnizer/SquareBracketColumnizer.cs b/src/LogExpert.Core/Classes/Columnizer/SquareBracketColumnizer.cs index 5ac9f6da..fffd3d3d 100644 --- a/src/LogExpert.Core/Classes/Columnizer/SquareBracketColumnizer.cs +++ b/src/LogExpert.Core/Classes/Columnizer/SquareBracketColumnizer.cs @@ -333,7 +333,24 @@ private Column[] SquareSplit (ReadOnlyMemory line, int dateLen, int timeLe break; } - var closingBracketIndex = trimmed.Span.IndexOf(']'); + // Find the ']' that matches the opening '[', accounting for nested brackets + // (e.g. "[ProgramName/MethodName[41]]" is a single field). + var closeSpan = trimmed.Span; + var depth = 0; + var closingBracketIndex = -1; + for (var j = 0; j < closeSpan.Length; j++) + { + if (closeSpan[j] == '[') + { + depth++; + } + else if (closeSpan[j] == ']' && --depth == 0) + { + closingBracketIndex = j; + break; + } + } + if (closingBracketIndex < 0) { columnList.Add(new Column { FullValue = rest, Parent = clogLine }); diff --git a/src/LogExpert.Tests/ColumnizerTests/SquareBracketColumnizerTest.cs b/src/LogExpert.Tests/ColumnizerTests/SquareBracketColumnizerTest.cs index 01f99022..d1112a93 100644 --- a/src/LogExpert.Tests/ColumnizerTests/SquareBracketColumnizerTest.cs +++ b/src/LogExpert.Tests/ColumnizerTests/SquareBracketColumnizerTest.cs @@ -76,4 +76,35 @@ public void GetPriority_HappyFile_ColumnCountMatches (string fileName, int count _ = squareBracketColumnizer.GetPriority(path, loglines); Assert.That(count, Is.EqualTo(squareBracketColumnizer.GetColumnCount())); } + + // Issue #460: a bracketed field containing nested brackets (e.g. "[ProgramName/MethodName[41]]") + // must stay in a single column instead of being split at the first inner ']'. + [Test] + public void SplitLine_NestedBrackets_KeepsInnerBracketsInOneColumn () + { + SquareBracketColumnizer squareBracketColumnizer = new(); + var path = Path.Join(AppDomain.CurrentDomain.BaseDirectory, @".\TestData\SquareBracketColumnizerTest_06.txt"); + + LogfileReader logFileReader = new(path, new EncodingOptions(), true, 40, 50, new MultiFileOptions(), ReaderType.System, PluginRegistry.PluginRegistry.Instance, 500); + logFileReader.ReadFiles(); + + List loglines = + [ + logFileReader.GetLogLineMemory(0), + logFileReader.GetLogLineMemory(1) + ]; + + _ = squareBracketColumnizer.GetPriority(path, loglines); + + var line = logFileReader.GetLogLineMemory(0); + var columns = squareBracketColumnizer.SplitLine(null, line); + + Assert.Multiple(() => + { + Assert.That(columns.ColumnValues[0].FullValue.ToString(), Is.EqualTo("[WARN ]")); + Assert.That(columns.ColumnValues[1].FullValue.ToString(), Is.EqualTo("[18/08-06:06:18.436]")); + Assert.That(columns.ColumnValues[2].FullValue.ToString(), Is.EqualTo("[ProgramName/MethodName[41]]")); + Assert.That(columns.ColumnValues[^1].FullValue.ToString().Trim(), Is.EqualTo("About to run.")); + }); + } } \ No newline at end of file diff --git a/src/LogExpert.Tests/LogExpert.Tests.csproj b/src/LogExpert.Tests/LogExpert.Tests.csproj index 271b8f26..1ce9e53f 100644 --- a/src/LogExpert.Tests/LogExpert.Tests.csproj +++ b/src/LogExpert.Tests/LogExpert.Tests.csproj @@ -48,6 +48,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/LogExpert.Tests/TestData/SquareBracketColumnizerTest_06.txt b/src/LogExpert.Tests/TestData/SquareBracketColumnizerTest_06.txt new file mode 100644 index 00000000..8c4399e6 --- /dev/null +++ b/src/LogExpert.Tests/TestData/SquareBracketColumnizerTest_06.txt @@ -0,0 +1,5 @@ +[WARN ][18/08-06:06:18.436][ProgramName/MethodName[41]] About to run. +[WARN ][18/08-06:07:15.436][ProgramName/MethodName[771]] Finishing Application. +[WARN ][18/08-06:07:15.436][ProgramName/MethodName[771]]] Finishing Application. +[WARN ][18/08-06:07:15.436][ProgramName/MethodName[771]]]] Finishing Application. +[WARN ][18/08-06:07:15.436][ProgramName/MethodName[771]]]]] Finishing Application. From 1fa60b66009c4fcc9f3a056b48b3db397f83870e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 19 Jun 2026 10:20:05 +0000 Subject: [PATCH 2/2] chore: update plugin hashes [skip ci] --- .../PluginHashGenerator.Generated.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index 12141533..62ae9c24 100644 --- a/src/PluginRegistry/PluginHashGenerator.Generated.cs +++ b/src/PluginRegistry/PluginHashGenerator.Generated.cs @@ -10,7 +10,7 @@ public static partial class PluginValidator { /// /// Gets pre-calculated SHA256 hashes for built-in plugins. - /// Generated: 2026-06-17 19:50:55 UTC + /// Generated: 2026-06-19 10:20:03 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "EF107873A2547134F2E0D1546125F266D0E7B06DF3AD5F69937C3E4B9D0A1EF5", + ["AutoColumnizer.dll"] = "FE5B41F852198756D6E7F565DFF531DB842260FC0AA8AC9E0FFBE39204FE5D15", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "83A307BF2B861420AEF66DB1F17AE2A7F643AD5A9962EA14134E728964FE408D", - ["CsvColumnizer.dll (x86)"] = "83A307BF2B861420AEF66DB1F17AE2A7F643AD5A9962EA14134E728964FE408D", - ["DefaultPlugins.dll"] = "6DAE9C7AD97EE112F516688A5F4448CF167424B048594AAFC5B699CEACDAB610", - ["FlashIconHighlighter.dll"] = "961ABD5874FBA5752B46B545A27C24D3A52C7DF395502DC562D8856D9F016A1F", - ["GlassfishColumnizer.dll"] = "B40D6A5688DBC4706470BB18715A83524A3552EA5440C2A74C74C8D7C3D9ED25", - ["JsonColumnizer.dll"] = "6CA9BFD2EACDB619A556A50ED9D8B6BD833FEB27ABED5F28608555F1C58150FD", - ["JsonCompactColumnizer.dll"] = "9B244A519517B3BD511FBC344A108F62E166421992A151458235E843D2AA4EDC", - ["Log4jXmlColumnizer.dll"] = "73DA4158CD609E75D1A645E3B34E3ABF6277E8904FC7E9762F8224DC199CCF60", - ["LogExpert.Resources.dll"] = "96BD00F6A38F728EBD6315D3192DBE2BD1DEAD5E7823AB2AE129DCC3F338617C", + ["CsvColumnizer.dll"] = "10B5DC2422CFC4FA65D880DE0DE800138C9283AB28734A2D0F18431B40515D30", + ["CsvColumnizer.dll (x86)"] = "10B5DC2422CFC4FA65D880DE0DE800138C9283AB28734A2D0F18431B40515D30", + ["DefaultPlugins.dll"] = "F78E00FED77F20EA408C7D818045F7B37D2CA8BDAA3AB2BE4E15DF162615FE84", + ["FlashIconHighlighter.dll"] = "B8BAC28BA72540CD2FA5AC189E7DF2616EB0F51B0D15DDA96688D11D2BB426C1", + ["GlassfishColumnizer.dll"] = "90095DFB7B30B88D7477687A9BDCA1B119038AFBFF0216D0B7A852C4F41D66C6", + ["JsonColumnizer.dll"] = "9E4F52BB6724451808B43719546CFA520B6FD17C65C231D172099D81DA8E2D0B", + ["JsonCompactColumnizer.dll"] = "3FD84C6EB8BB3589F347C33C2B5B272A7BC13995AA32BEC0C67E596BC630B33F", + ["Log4jXmlColumnizer.dll"] = "8EE85F16DE33AEA8BEA53B2EEEC8D032C572C5F3885CA07584DCB248D838F441", + ["LogExpert.Resources.dll"] = "A4E16709FB669C2617AB6BC910737118B96F484E920FF06357FC85BF8A665A99", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll (x86)"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.Logging.Abstractions.dll"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", ["Microsoft.Extensions.Logging.Abstractions.dll (x86)"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", - ["RegexColumnizer.dll"] = "8D9D58E2659E0A29306BC6614DEEC33314EA6293F62D861F6794587B7FC78D68", - ["SftpFileSystem.dll"] = "22A52842AD7B2FB727EC9F6F1FB3010028F55538839A8E1899D98AF74A522438", - ["SftpFileSystem.dll (x86)"] = "C37E1E8B463FB9373CDD437FD4366DDE9E15D19DED2420D59057E21F7F203DC5", - ["SftpFileSystem.Resources.dll"] = "41DF1B52E15926627AB8E178F5BF5692C325082749E8FE1959E1BC1DDF1E90FC", - ["SftpFileSystem.Resources.dll (x86)"] = "41DF1B52E15926627AB8E178F5BF5692C325082749E8FE1959E1BC1DDF1E90FC", + ["RegexColumnizer.dll"] = "64EC13C21963405A42BBB41F42A886791B8E97910E660D0C8D244231454A8871", + ["SftpFileSystem.dll"] = "962DBA38A676D7DA93B069F13765F9AD7C5E955F50C22BA46A356DE4B9556F88", + ["SftpFileSystem.dll (x86)"] = "B872D63BA65261B943411098B46115A4940CC66F03E4807B561F6D20DB1F5F17", + ["SftpFileSystem.Resources.dll"] = "E8039F5F595F9D30731ECD3A67116CEC41963CA4AA51BF4E0368CC6BE9FFE9D3", + ["SftpFileSystem.Resources.dll (x86)"] = "E8039F5F595F9D30731ECD3A67116CEC41963CA4AA51BF4E0368CC6BE9FFE9D3", }; }