Skip to content

Commit cb3877a

Browse files
committed
Enhanced demo with additional configuration examples for the sink.
1 parent 8fa1007 commit cb3877a

File tree

6 files changed

+164
-16
lines changed

6 files changed

+164
-16
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,6 @@ jobs:
5151
$coveragePercent = [math]::Round([double]$coverage * 100, 2)
5252
Write-Host "Current line coverage: $coveragePercent%"
5353
if ($coveragePercent -lt 75) {
54-
Write-Error "Code coverage ($coveragePercent%) is below the required threshold of 70%"
54+
Write-Error "Code coverage ($coveragePercent%) is below the required threshold of 75%"
5555
exit 1
5656
}

CHANGES.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
## Release Notes - Serilog.Sinks.RichTextBox.WinForms.Colored v3.0.1
1+
## Release Notes - Serilog.Sinks.RichTextBox.WinForms.Colored v3.1.0
22

3-
### Minor Release
3+
### Feature Release
44

5-
This minor release focuses on config improvements and performance optimization.
5+
This feature release introduces the ability to clear and restore the RichTextBox sink output.
66

77
### What Changed
88

9-
- Adjusted MaxLogLines limit From 512 to 2048 lines.
10-
- Fixed a bug where the RichTextBox would not persist the zoom factor.
11-
- Optimized the Concurrent Circular Buffer
9+
- Added `Clear()` and `Restore()` operations to the circular buffer/RichTextBox sink.
1210

1311
### Resources
1412

Demo/Form1.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,35 @@ public Form1()
4343

4444
private void Initialize()
4545
{
46+
// This is one way to configure the sink:
4647
_options = new RichTextBoxSinkOptions(
4748
theme: ThemePresets.Literate,
4849
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:l}{NewLine}{Exception}",
4950
formatProvider: new CultureInfo("en-US"));
5051

5152
_sink = new RichTextBoxSink(richTextBox1, _options);
5253
Log.Logger = new LoggerConfiguration()
53-
.MinimumLevel.Verbose()
54-
.WriteTo.Sink(_sink, LogEventLevel.Verbose)
55-
.CreateLogger();
54+
.MinimumLevel.Verbose()
55+
.WriteTo.Sink(_sink, LogEventLevel.Verbose)
56+
.CreateLogger();
57+
58+
// Intentional dead code for demonstration purposes.
59+
if (false)
60+
{
61+
// You can also use fluent syntax to configure the sink like this:
62+
Log.Logger = new LoggerConfiguration()
63+
.MinimumLevel.Verbose()
64+
.WriteTo.RichTextBox(richTextBox1, out _sink, formatProvider: new CultureInfo("en-US"))
65+
.CreateLogger();
66+
67+
// The out _sink is optional, but it allows you to access the sink instance.
68+
// This is useful if you need to access the sink's methods, such as Clear() or Restore().
69+
// If you don't need to access the sink, you can omit the out parameter like this:
70+
Log.Logger = new LoggerConfiguration()
71+
.MinimumLevel.Verbose()
72+
.WriteTo.RichTextBox(richTextBox1, formatProvider: new CultureInfo("en-US"))
73+
.CreateLogger();
74+
}
5675

5776
Log.Debug("Started logger.");
5877
btnDispose.Enabled = true;
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using Serilog.Sinks.RichTextBoxForms.Collections;
2+
using Xunit;
3+
4+
namespace Serilog.Tests.Collections
5+
{
6+
public class ConcurrentCircularBufferTests
7+
{
8+
[Fact]
9+
public void TakeSnapshot_WithItemsLessThanCapacity_ReturnsAllItemsInOrder()
10+
{
11+
// Arrange
12+
var buffer = new ConcurrentCircularBuffer<int>(3);
13+
buffer.Add(1);
14+
buffer.Add(2);
15+
16+
// Act
17+
var snapshot = new List<int>();
18+
buffer.TakeSnapshot(snapshot);
19+
20+
// Assert
21+
Assert.Equal(new[] { 1, 2 }, snapshot);
22+
}
23+
24+
[Fact]
25+
public void AddBeyondCapacity_OverwritesOldestItemsAndMaintainsOrder()
26+
{
27+
// Arrange
28+
var buffer = new ConcurrentCircularBuffer<int>(3);
29+
buffer.Add(1);
30+
buffer.Add(2);
31+
buffer.Add(3);
32+
buffer.Add(4); // Should overwrite the oldest item (1)
33+
34+
// Act
35+
var snapshot = new List<int>();
36+
buffer.TakeSnapshot(snapshot);
37+
38+
// Assert
39+
Assert.Equal(new[] { 2, 3, 4 }, snapshot);
40+
}
41+
42+
[Fact]
43+
public void Clear_FollowedByAdds_SnapshotContainsOnlyNewItems()
44+
{
45+
// Arrange
46+
var buffer = new ConcurrentCircularBuffer<int>(3);
47+
buffer.Add(1);
48+
buffer.Add(2);
49+
buffer.Add(3);
50+
51+
// Act & Assert - After clear, snapshot should be empty
52+
buffer.Clear();
53+
var snapshotAfterClear = new List<int>();
54+
buffer.TakeSnapshot(snapshotAfterClear);
55+
Assert.Empty(snapshotAfterClear);
56+
57+
// Add new item and verify snapshot contains only the new item
58+
buffer.Add(4);
59+
var snapshotAfterOneAdd = new List<int>();
60+
buffer.TakeSnapshot(snapshotAfterOneAdd);
61+
Assert.Equal(new[] { 4 }, snapshotAfterOneAdd);
62+
63+
// Add two more items to fill the buffer again
64+
buffer.Add(5);
65+
buffer.Add(6);
66+
var snapshotAfterMoreAdds = new List<int>();
67+
buffer.TakeSnapshot(snapshotAfterMoreAdds);
68+
Assert.Equal(new[] { 4, 5, 6 }, snapshotAfterMoreAdds);
69+
}
70+
71+
[Fact]
72+
public void Restore_AfterClear_ReturnsAllItemsAgain()
73+
{
74+
// Arrange
75+
var buffer = new ConcurrentCircularBuffer<int>(3);
76+
buffer.Add(1);
77+
buffer.Add(2);
78+
buffer.Add(3);
79+
buffer.Clear();
80+
81+
// Add two new items while buffer is in cleared state
82+
buffer.Add(4);
83+
buffer.Add(5);
84+
85+
// Act - Restore should make all items visible again
86+
buffer.Restore();
87+
var snapshot = new List<int>();
88+
buffer.TakeSnapshot(snapshot);
89+
90+
// Assert
91+
Assert.Equal(new[] { 3, 4, 5 }, snapshot);
92+
}
93+
}
94+
}

Serilog.Sinks.RichTextBox.WinForms.Colored/RichTextBoxSinkLoggerConfigurationExtensions.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public static class RichTextBoxSinkLoggerConfigurationExtensions
4848
public static LoggerConfiguration RichTextBox(
4949
this LoggerSinkConfiguration sinkConfiguration,
5050
RichTextBox richTextBoxControl,
51+
out RichTextBoxSink richTextBoxSink,
5152
Theme? theme = null,
5253
bool autoScroll = true,
5354
int maxLogLines = 256,
@@ -59,8 +60,46 @@ public static LoggerConfiguration RichTextBox(
5960
var appliedTheme = theme ?? ThemePresets.Literate;
6061
var renderer = new TemplateRenderer(appliedTheme, outputTemplate, formatProvider);
6162
var options = new RichTextBoxSinkOptions(appliedTheme, autoScroll, maxLogLines, outputTemplate, formatProvider);
62-
var sink = new RichTextBoxSink(richTextBoxControl, options, renderer);
63-
return sinkConfiguration.Sink(sink, minimumLogEventLevel, levelSwitch);
63+
richTextBoxSink = new RichTextBoxSink(richTextBoxControl, options, renderer);
64+
return sinkConfiguration.Sink(richTextBoxSink, minimumLogEventLevel, levelSwitch);
65+
}
66+
67+
/// <summary>
68+
/// Adds a sink that writes log events to the specified Windows Forms <see cref="RichTextBox"/>
69+
/// using colour-coded rich-text formatting.
70+
/// </summary>
71+
/// <param name="sinkConfiguration">The logger sink configuration this extension method operates on.</param>
72+
/// <param name="richTextBoxControl">The target <see cref="RichTextBox"/> instance that will display the log output.</param>
73+
/// <param name="theme">Optional theme controlling colours of individual message tokens. When <c>null</c>, <see cref="Serilog.Sinks.RichTextBoxForms.Themes.ThemePresets.Literate"/> is used.</param>
74+
/// <param name="autoScroll">When <c>true</c> (default) the control automatically scrolls to the newest log entry.</param>
75+
/// <param name="maxLogLines">Maximum number of log events retained in the circular buffer and rendered in the control.</param>
76+
/// <param name="outputTemplate">Message template that controls the textual representation of each log event.</param>
77+
/// <param name="formatProvider">Culture-specific or custom formatting provider, or <c>null</c> to use the invariant culture.</param>
78+
/// <param name="minimumLogEventLevel">Minimum level below which events are ignored by this sink.</param>
79+
/// <param name="levelSwitch">Optional switch allowing the minimum log level to be changed at runtime.</param>
80+
/// <returns>A <see cref="LoggerConfiguration"/> object that can be further configured.</returns>
81+
public static LoggerConfiguration RichTextBox(
82+
this LoggerSinkConfiguration sinkConfiguration,
83+
RichTextBox richTextBoxControl,
84+
Theme? theme = null,
85+
bool autoScroll = true,
86+
int maxLogLines = 256,
87+
string outputTemplate = OutputTemplate,
88+
IFormatProvider? formatProvider = null,
89+
LogEventLevel minimumLogEventLevel = LogEventLevel.Verbose,
90+
LoggingLevelSwitch? levelSwitch = null)
91+
{
92+
return RichTextBox(
93+
sinkConfiguration,
94+
richTextBoxControl,
95+
out _,
96+
theme,
97+
autoScroll,
98+
maxLogLines,
99+
outputTemplate,
100+
formatProvider,
101+
minimumLogEventLevel,
102+
levelSwitch);
64103
}
65104
}
66105
}

Serilog.Sinks.RichTextBox.WinForms.Colored/Serilog.Sinks.RichTextBox.WinForms.Colored.csproj

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,9 @@
2222
<TargetFrameworks>net462;net471;net6.0-windows;net8.0-windows;net9.0-windows;netcoreapp3.0-windows;netcoreapp3.1-windows</TargetFrameworks>
2323
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
2424
<UseWindowsForms>true</UseWindowsForms>
25-
<Version>3.0.1</Version>
25+
<Version>3.1.0</Version>
2626
<PackageReleaseNotes>
27-
- Adjusted MaxLogLines constraint from 512 to 2048 lines.
28-
- Fixed bug where the RTB control would not persist the zoom factor.
29-
- Minor improvements to the concurrent circular buffer.
27+
- Added ability to clear and restore the RichTextBox sink output.
3028

3129
See repository for more information:
3230
https://github.com/vonhoff/Serilog.Sinks.RichTextBox.WinForms.Colored

0 commit comments

Comments
 (0)