Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4793787
replace fixed regex with regex properties and regex attributes
Hirogen Nov 30, 2025
039b19b
Merge branch 'Development' into performance_optimizations
Dec 2, 2025
ffa5ecc
optimization, added ISpan stuff
Dec 2, 2025
f54bcc4
pipelines docs for implementation
Dec 2, 2025
76a42f2
Pipeline added
Hirogen Dec 2, 2025
02108cd
Merge branch 'Development' into new_reader
Dec 3, 2025
2fbc998
pipelines update
Dec 4, 2025
90ef20b
locks
Dec 4, 2025
5040622
update reader
Dec 6, 2025
ea35028
chore: update plugin hashes [skip ci]
github-actions[bot] Dec 6, 2025
cceaa03
Update src/ColumnizerLib/ILogLineSpanColumnizer.cs
Hirogen Dec 6, 2025
a2e43ff
Update src/LogExpert.Core/Interface/ILogStreamReaderSpan.cs
Hirogen Dec 6, 2025
0765822
Update src/LogExpert.UI/Controls/LogWindow/LogWindow.cs
Hirogen Dec 6, 2025
a76efa0
Update src/LogExpert.Core/Interface/ILogStreamReaderSpan.cs
Hirogen Dec 6, 2025
91f5c30
Update src/LogExpert.Core/Classes/Log/LogBuffer.cs
Hirogen Dec 6, 2025
f846922
chore: update plugin hashes [skip ci]
github-actions[bot] Dec 6, 2025
97808ba
Update src/ColumnizerLib/ITextValue.cs
Hirogen Dec 6, 2025
433ba41
Update src/ColumnizerLib/ILogLine.cs
Hirogen Dec 6, 2025
6a3b31d
review comments
Hirogen Dec 6, 2025
262fab1
chore: update plugin hashes [skip ci]
github-actions[bot] Dec 6, 2025
f163dab
benchmakr summary
Dec 6, 2025
a039252
Merge branch 'new_reader' of https://github.com/LogExperts/LogExpert …
Dec 6, 2025
b28a006
chore: update plugin hashes [skip ci]
github-actions[bot] Dec 6, 2025
3f8dde4
benchmark md updates
Hirogen Dec 6, 2025
0a2f759
Merge branch 'new_reader' of https://github.com/LogExperts/LogExpert …
Hirogen Dec 6, 2025
33fa73e
chore: update plugin hashes [skip ci]
github-actions[bot] Dec 6, 2025
7051b8d
update
Hirogen Dec 7, 2025
316370d
update new line search
Hirogen Dec 7, 2025
3db95b1
more changes
Hirogen Dec 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/ColumnizerLib/IColumnizedLogLine.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ColumnizerLib;

public interface IColumnizedLogLine
Expand All @@ -11,7 +6,6 @@ public interface IColumnizedLogLine

ILogLine LogLine { get; }


IColumn[] ColumnValues { get; }

#endregion
Expand Down
10 changes: 5 additions & 5 deletions src/ColumnizerLib/IColumnizerConfigurator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface IColumnizerConfigurator
/// required settings.
/// </summary>
/// <param name="callback">Callback interface with functions which can be used by the columnizer</param>
/// <param name="configDir">The complete path to the directory where LogExpert stores its settings.
/// <param name="configDir">The complete path to the directory where LogExpert stores its settings.
/// You can use this directory, if you want to. Please don't use the file name "settings.dat", because this
/// name is used by LogExpert.
/// </param>
Expand All @@ -24,21 +24,21 @@ public interface IColumnizerConfigurator
/// It's also your own job to store the configuration in a config file or on the registry.
/// The callback is passed to this function just in case you need the file name of the current log file
/// or the line count etc. You can also use it to store different settings for every log file.
/// You can use the callback to distinguish between different files. Its passed to all important
/// You can use the callback to distinguish between different files. Its passed to all important
/// functions in the Columnizer.
/// </remarks>
void Configure(ILogLineColumnizerCallback callback, string configDir);
void Configure (ILogLineColumnizerCallback callback, string configDir);

/// <summary>
/// This function will be called right after LogExpert has loaded your Columnizer class. Use this
/// to load the configuration which was saved in the Configure() function.
/// You have to hold the loaded config data in your Columnizer object.
/// </summary>
/// <param name="configDir">The complete path to the directory where LogExpert stores its settings.
/// <param name="configDir">The complete path to the directory where LogExpert stores its settings.
/// You can use this directory, if you want to. Please don't use the file name "settings.dat", because this
/// name is used by LogExpert.
/// </param>
void LoadConfig(string configDir);
void LoadConfig (string configDir);

#endregion
}
7 changes: 1 addition & 6 deletions src/ColumnizerLib/IColumnizerPriority.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ColumnizerLib;

public interface IColumnizerPriority
Expand All @@ -13,5 +8,5 @@ public interface IColumnizerPriority
/// <param name="samples"></param>
/// <param name="fileName"></param>
/// <returns></returns>
Priority GetPriority(string fileName, IEnumerable<ILogLine> samples);
Priority GetPriority (string fileName, IEnumerable<ILogLine> samples);
}
6 changes: 1 addition & 5 deletions src/ColumnizerLib/IFileSystemCallback.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace ColumnizerLib;

/// <summary>
Expand All @@ -15,7 +11,7 @@ public interface IFileSystemCallback
/// Retrieve a logger. The plugin can use the logger to write log messages into LogExpert's log file.
/// </summary>
/// <returns></returns>
ILogExpertLogger GetLogger();
ILogExpertLogger GetLogger ();

#endregion
}
12 changes: 4 additions & 8 deletions src/ColumnizerLib/IInitColumnizer.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace ColumnizerLib;

/// <summary>
/// Implement this interface in your columnizer if you need to do some initialization work
/// Implement this interface in your columnizer if you need to do some initialization work
/// every time the columnizer is selected.
/// </summary>
/// <remarks>
Expand All @@ -14,7 +10,7 @@ namespace ColumnizerLib;
/// heavyweight work to do in your implementations.</para>
/// <para>
/// If a file is reloaded, the current Columnizer is set again. That means that the methods of this
/// interface will be called again. Generally you should do no assumptions about how often the
/// interface will be called again. Generally you should do no assumptions about how often the
/// methods will be called. The file is already loaded when the columnizer is set. So
/// you can use the methods in the given callbacks to get informations about the file or to
/// retrieve specific lines.
Expand All @@ -28,14 +24,14 @@ public interface IInitColumnizer
/// This method is called when the Columnizer is selected as the current columnizer.
/// </summary>
/// <param name="callback">Callback that can be used to retrieve some informations, if needed.</param>
void Selected(ILogLineColumnizerCallback callback);
void Selected (ILogLineColumnizerCallback callback);

/// <summary>
/// This method is called when the Columnizer is de-selected (i.e. when another Columnizer is
/// selected).
/// </summary>
/// <param name="callback">Callback that can be used to retrieve some informations, if needed.</param>
void DeSelected(ILogLineColumnizerCallback callback);
void DeSelected (ILogLineColumnizerCallback callback);

#endregion
}
63 changes: 63 additions & 0 deletions src/ColumnizerLib/ILogLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,67 @@ public interface ILogLine : ITextValue
int LineNumber { get; }

#endregion
}

/// <summary>
/// Represents a single log line, including its full text and line number.
/// </summary>
/// <remarks>
/// <para>
/// <b>Purpose:</b> <br/>
/// The <c>LogLine</c> struct encapsulates the content and line number of a log entry. It is used throughout the
/// columnizer and log processing infrastructure to provide a strongly-typed, immutable representation of a log line.
/// </para>
/// <para>
/// <b>Usage:</b> <br/>
/// This struct implements the <see cref="ILogLine"/> interface, allowing it to be used wherever an <c>ILogLine</c>
/// is expected. It provides value semantics and is intended to be lightweight and efficiently passed by value.
/// </para>
/// <para>
/// <b>Relationship to ILogLine:</b> <br/>
/// <c>LogLine</c> is a concrete, immutable implementation of the <see cref="ILogLine"/> interface, providing
/// properties for the full line text and its line number.
/// </para>
/// <para>
/// <b>Why struct instead of record:</b> <br/>
/// A <c>struct</c> is preferred over a <c>record</c> here to avoid heap allocations and to provide value-type
/// semantics, which are beneficial for performance when processing large numbers of log lines. The struct is
/// immutable (readonly), ensuring thread safety and predictability. The previous <c>record</c> implementation
/// was replaced to better align with these performance and semantic requirements.
/// </para>
/// </remarks>
public class LogLine (string fullLine, int lineNumber) : ILogLineMemory
{
private readonly ReadOnlyMemory<char> _lineMemory;
private string _cachedString;

public LogLine (ReadOnlyMemory<char> lineMemory, int lineNumber) : this(lineMemory.ToString(), lineNumber)
{
FullLineMemory = lineMemory;
LineNumber = lineNumber;
}

public string FullLine => _cachedString ??= _lineMemory.ToString();

public int LineNumber { get; } = lineNumber;

public string Text => FullLine;

public ReadOnlyMemory<char> FullLineMemory { get; }

public override bool Equals (object obj)
{
return obj is LogLine other &&
FullLine == other.FullLine &&
LineNumber == other.LineNumber;
}

public override int GetHashCode ()
{
return HashCode.Combine(FullLine, LineNumber);
}

public static bool operator == (LogLine left, LogLine right) => left.Equals(right);

public static bool operator != (LogLine left, LogLine right) => !(left == right);
}
6 changes: 6 additions & 0 deletions src/ColumnizerLib/ILogLineMemory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ColumnizerLib;

public interface ILogLineMemory : ILogLine
{
ReadOnlyMemory<char> FullLineMemory { get; }
}
23 changes: 23 additions & 0 deletions src/ColumnizerLib/ILogLineSpan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
public interface ILogLineSpan
{
ReadOnlySpan<char> GetFullLineSpan ();

int LineNumber { get; }
}

public readonly ref struct LogLineSpan : ILogLineSpan
{
private readonly ReadOnlyMemory<char> _lineMemory;

public LogLineSpan (ReadOnlyMemory<char> lineMemory, int lineNumber)
{
_lineMemory = lineMemory;
LineNumber = lineNumber;
}

public static LogLineSpan Create (ReadOnlyMemory<char> lineMemory, int lineNumber) => new LogLineSpan(lineMemory, lineNumber);

public ReadOnlySpan<char> GetFullLineSpan () => _lineMemory.Span;

public int LineNumber { get; }
}
19 changes: 19 additions & 0 deletions src/ColumnizerLib/ILogLineSpanColumnizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace ColumnizerLib;

public interface ILogLineSpanColumnizer : ILogLineColumnizer
{
/// <summary>
/// Span-based version of SplitLine that avoids string allocations
/// </summary>
IColumnizedLogLine SplitLine (ILogLineColumnizerCallback callback, ReadOnlySpan<char> lineSpan, int lineNumber);

/// <summary>
/// Span-based timestamp extraction
/// </summary>
DateTime GetTimestamp (ILogLineColumnizerCallback callback, ReadOnlySpan<char> lineSpan, int lineNumber);

/// <summary>
/// Indicates if this columnizer supports span-based operations
/// </summary>
bool IsSpanSupported { get; }
}
12 changes: 12 additions & 0 deletions src/ColumnizerLib/ITextValue.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
namespace ColumnizerLib;

// DEPRECATED: This interface adds no value and causes performance overhead.
// Keep for backward compatibility but mark as obsolete.
[Obsolete("ITextValue is deprecated. Access FullLine or FullValue directly instead.", false)]
public interface ITextValue
{
#region Properties

string Text { get; }

#endregion
}

public static class TextValueExtensions
{
[Obsolete("Use ILogLine.FullLine property directly instead of this extension method")]
public static string GetText (this ILogLine logLine) => logLine.FullLine;

[Obsolete("Use DisplayValue property directly")]
public static string GetText (this IColumn column) => column.DisplayValue;
}
1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<NoWarn>$(NoWarn);NU1507</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="chocolatey" Version="2.4.3" />
<PackageVersion Include="CsvHelper" Version="33.1.0" />
<PackageVersion Include="DockPanelSuite.ThemeVS2015" Version="3.1.1" />
Expand Down
16 changes: 16 additions & 0 deletions src/LogExpert.Benchmarks/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project>
<!-- This file prevents the parent Directory.Build.props from affecting BenchmarkDotNet's generated projects -->
<PropertyGroup>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<SignAssembly>false</SignAssembly>
</PropertyGroup>

<!-- Do NOT include the shared AssemblyInfo.cs -->
<ItemGroup>
<Compile Remove="$(MSBuildThisFileDirectory)..\Solution Items\AssemblyInfo.cs" />
<Compile Remove="..\Solution Items\AssemblyInfo.cs" />
</ItemGroup>

<!-- Prevent parent Directory.Build.props from being applied -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" Condition="false" />
</Project>
3 changes: 3 additions & 0 deletions src/LogExpert.Benchmarks/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Project>
<!-- Prevent parent Directory.Build.targets from being imported -->
</Project>
26 changes: 26 additions & 0 deletions src/LogExpert.Benchmarks/LogExpert.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Override Directory.Build.props settings for BenchmarkDotNet compatibility -->
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<SignAssembly>false</SignAssembly>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\LogExpert.Core\LogExpert.Core.csproj" />
</ItemGroup>

<!-- Exclude the shared AssemblyInfo.cs that Directory.Build.props tries to add -->
<ItemGroup>
<Compile Remove="..\Solution Items\AssemblyInfo.cs" />
</ItemGroup>

</Project>
Loading