diff --git a/CHANGES.md b/CHANGES.md
index 7f3da4f..8e3baf1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,81 +1,14 @@
-## Release Notes - Serilog.Sinks.RichTextBox.WinForms.Colored v3.0.0
+## Release Notes - Serilog.Sinks.RichTextBox.WinForms.Colored v3.0.1
-### Major Release - Breaking Changes
+### Minor Release
-This major release focuses on performance optimization, UI stability, and streamlined configuration while simplifying the configuration API.
+This minor release focuses on config improvements and performance optimization.
-### Breaking Changes
+### What Changed
-- **Simplified Configuration Options**: Reduced configurable options to focus only on the most relevant and commonly used settings:
- - Removed `messageBatchSize` parameter (no longer needed)
- - Removed `messagePendingInterval` parameter (no longer needed)
- - Renamed `appliedTheme` parameter to `theme` for consistency
-
-- **Theme System Overhaul**: Complete redesign of the theme system to align with the Serilog WPF sink. Previous theme names (`Dark`, `Light`, `DarkClassic`, `LightClassic`) have been replaced with new theme presets. All themes now include WCAG compliance with proper contrast ratio.
-
-- **Enhanced Memory Management**: The `maxLogLines` parameter now has improved validation (1-512 range) with a default of 256 lines to ensure optimal performance. While not mandatory, proper configuration is recommended to prevent performance degradation from excessive log entries in the WinForms control.
-
-### New Theme System
-
-Available built-in themes:
-
-| Theme | Description |
-|-----------------------------|------------------------------------------------------------------------------|
-| `ThemePresets.Literate` | Styled to replicate the default theme of Serilog.Sinks.Console (default) |
-| `ThemePresets.Grayscale` | A theme using only shades of gray, white, and black |
-| `ThemePresets.Colored` | A theme based on the original Serilog.Sinks.ColoredConsole sink |
-| `ThemePresets.Luminous` | A new light theme with high contrast for accessibility |
-
-The themes based on the original sinks are slightly adjusted to be WCAG compliant, ensuring that the contrast ratio between text and background colors is at least 4.5:1. `Luminous` is a new theme specifically created for this sink.
-
-### Bug Fixes
-
-- **Fixed UI Freezing**: Resolved critical UI freeze issues caused by SystemEvents when using RichTextBox controls on background threads.
-
-- **Fixed Auto-scroll on .NET Framework**: Corrected auto-scroll behavior that wasn't working properly on .NET Framework applications.
-
-### Performance Improvements
-
-- **Optimized Rendering Logic**: Removed the off-screen RichTextBox dependency and improved the rendering pipeline for better performance and reduced memory usage.
-
-- **Streamlined Processing**: Removed unnecessary batching parameters to simplify internal processing logic.
-
-### Migration Guide
-
-Due to breaking changes, please update your existing configurations:
-
-1. **Update Theme Names**: Replace old theme names with new equivalents:
- - `Dark` or `DarkClassic` → `ThemePresets.Colored` or `ThemePresets.Grayscale` or `ThemePresets.Literate`
- - `Light` or `LightClassic` → `ThemePresets.Luminous`
-
-2. **Update Parameter Names**:
- - `appliedTheme` → `theme`
- - Remove `messageBatchSize` and `messagePendingInterval` parameters (no longer supported)
-
-I recommend pairing this sink with a file sink for persistent logging storage, as it's not practical to have thousands of log entries displayed in a RichTextBox control.
-
-### Recommended Configuration
-
-```csharp
-Log.Logger = new LoggerConfiguration()
- .WriteTo.RichTextBox(richTextBox1,
- theme: ThemePresets.Literate,
- maxLogLines: 64) // Optional, defaults to 256
- .WriteTo.File("logs/app-.txt", rollingInterval: RollingInterval.Day) // Recommended for persistence
- .CreateLogger();
-```
-
-### Full Changelog
-
-- Reduced configurable options to only the most relevant ones (breaking change)
-- Renamed `appliedTheme` parameter to `theme` (breaking change)
-- Removed `messageBatchSize` and `messagePendingInterval` parameters (breaking change)
-- Completely redesigned theme system with new theme names and WCAG compliance (breaking change)
-- Added new `Luminous` theme for high contrast accessibility
-- Enhanced `maxLogLines` validation with 1-512 range limit
-- Fixed UI freeze caused by SystemEvents on background-thread RichTextBox
-- Optimized performance by removing the off-screen RichTextBox and improving rendering logic
-- Fixed auto-scroll issue on .NET Framework
+- Adjusted MaxLogLines limit From 512 to 2048 lines.
+- Fixed a bug where the RichTextBox would not persist the zoom factor.
+- Optimized the Concurrent Circular Buffer
### Resources
diff --git a/Serilog.Sinks.RichTextBox.WinForms.Colored/Serilog.Sinks.RichTextBox.WinForms.Colored.csproj b/Serilog.Sinks.RichTextBox.WinForms.Colored/Serilog.Sinks.RichTextBox.WinForms.Colored.csproj
index 3b11dff..4ed5008 100644
--- a/Serilog.Sinks.RichTextBox.WinForms.Colored/Serilog.Sinks.RichTextBox.WinForms.Colored.csproj
+++ b/Serilog.Sinks.RichTextBox.WinForms.Colored/Serilog.Sinks.RichTextBox.WinForms.Colored.csproj
@@ -22,13 +22,11 @@
net462;net471;net6.0-windows;net8.0-windows;net9.0-windows;netcoreapp3.0-windows;netcoreapp3.1-windows
true
true
- 3.0.0
+ 3.0.1
- - Reduced configurable options to only the most relevant ones (breaking change).
- - Aligned theme colors with Serilog WPF sink including WCAG compliance (breaking change).
- - Fixed UI freeze caused by SystemEvents on background-thread RichTextBox.
- - Optimized performance by removing the off-screen RichTextBox and improving rendering logic.
- - Fixed auto-scroll issue on .NET Framework.
+ - Adjusted MaxLogLines constraint from 512 to 2048 lines.
+ - Fixed bug where the RTB control would not persist the zoom factor.
+ - Minor improvements to the concurrent circular buffer.
See repository for more information:
https://github.com/vonhoff/Serilog.Sinks.RichTextBox.WinForms.Colored
diff --git a/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Collections/ConcurrentCircularBuffer.cs b/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Collections/ConcurrentCircularBuffer.cs
index 10cdc15..3dc9924 100644
--- a/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Collections/ConcurrentCircularBuffer.cs
+++ b/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Collections/ConcurrentCircularBuffer.cs
@@ -4,35 +4,39 @@ namespace Serilog.Sinks.RichTextBoxForms.Collections
{
internal sealed class ConcurrentCircularBuffer
{
+ private readonly object _sync = new();
private readonly T[] _buffer;
private readonly int _capacity;
private int _head;
private int _count;
- private readonly object _sync = new();
-
public ConcurrentCircularBuffer(int capacity)
{
_capacity = capacity > 0 ? capacity : 1;
_buffer = new T[_capacity];
- _head = 0;
- _count = 0;
}
public void Add(T item)
{
lock (_sync)
{
- var tail = (_head + _count) % _capacity;
- _buffer[tail] = item;
+ var tail = _head + _count;
+ if (tail >= _capacity)
+ {
+ tail -= _capacity;
+ }
+ _buffer[tail] = item;
if (_count == _capacity)
{
- _head = (_head + 1) % _capacity;
+ if (++_head == _capacity)
+ {
+ _head = 0;
+ }
}
else
{
- _count++;
+ ++_count;
}
}
}
@@ -42,13 +46,18 @@ public void TakeSnapshot(List target)
lock (_sync)
{
target.Clear();
- target.Capacity = _count;
- for (var i = 0; i < _count; i++)
+ for (var i = 0; i < _count; ++i)
{
- target.Add(_buffer[(_head + i) % _capacity]);
+ var index = _head + i;
+ if (index >= _capacity)
+ {
+ index -= _capacity;
+ }
+
+ target.Add(_buffer[index]);
}
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Extensions/RichTextBoxExtensions.cs b/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Extensions/RichTextBoxExtensions.cs
index 150f615..7d8d8fe 100644
--- a/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Extensions/RichTextBoxExtensions.cs
+++ b/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Extensions/RichTextBoxExtensions.cs
@@ -68,7 +68,7 @@ public static void SetRtf(this RichTextBox richTextBox, string rtf, bool autoScr
private static void SetRtfInternal(RichTextBox richTextBox, string rtf, bool autoScroll)
{
richTextBox.Suspend();
-
+ var originalZoomFactor = richTextBox.ZoomFactor;
var scrollPoint = new Point();
if (!autoScroll)
@@ -78,6 +78,12 @@ private static void SetRtfInternal(RichTextBox richTextBox, string rtf, bool aut
richTextBox.Rtf = rtf;
+ // Re-apply the zoom level, as assigning to the Rtf property resets it back to 1.0.
+ if (Math.Abs(richTextBox.ZoomFactor - originalZoomFactor) > float.Epsilon)
+ {
+ richTextBox.ZoomFactor = originalZoomFactor;
+ }
+
if (!autoScroll)
{
SendMessage(richTextBox.Handle, EM_SETSCROLLPOS, 0, ref scrollPoint);
diff --git a/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/RichTextBoxSinkOptions.cs b/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/RichTextBoxSinkOptions.cs
index 578df05..ba5cd07 100644
--- a/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/RichTextBoxSinkOptions.cs
+++ b/Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/RichTextBoxSinkOptions.cs
@@ -32,7 +32,7 @@ public class RichTextBoxSinkOptions
///
/// The colour theme applied when rendering individual message tokens.
/// When true (default) the target control scrolls automatically to the most recent log line.
- /// Maximum number of log events retained in the in-memory circular buffer and rendered in the control. Must be between 1 and 10,000 (default: 256).
+ /// Maximum number of log events retained in the in-memory circular buffer and rendered in the control. Must be between 1 and 2048 (default: 256).
/// Timeout, in milliseconds, after which buffered events are flushed to the control if a batch has not yet been triggered. Must be between 250ms and 30 seconds (default: 500ms).
/// Serilog output template that controls textual formatting of each log event.
/// Optional culture-specific or custom formatting provider used when rendering scalar values; null for the invariant culture.
@@ -60,7 +60,7 @@ public int MaxLogLines
private set => _maxLogLines = value switch
{
< 1 => 1,
- > 512 => 512,
+ > 2048 => 2048,
_ => value
};
}