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 }; }