Skip to content

Commit 34452fb

Browse files
committed
Refactor RichTextBoxSink and formatting classes to improve performance and memory usage.
1 parent 67b6e61 commit 34452fb

File tree

4 files changed

+43
-50
lines changed

4 files changed

+43
-50
lines changed

Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Formatting/DisplayValueFormatter.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class DisplayValueFormatter : ValueFormatter
3030
{
3131
private readonly IFormatProvider? _formatProvider;
3232
private readonly StringBuilder _scalarBuilder = new();
33-
private readonly StringBuilder _stringBuilder = new();
33+
private readonly StringBuilder _literalBuilder = new(64);
3434
private JsonValueFormatter? _jsonValueFormatter;
3535

3636
public DisplayValueFormatter(Theme theme, IFormatProvider? formatProvider) : base(theme, formatProvider)
@@ -53,9 +53,9 @@ private void FormatLiteralValue(ScalarValue scalar, IRtfCanvas canvas, string? f
5353
return;
5454

5555
case byte[] bytes:
56-
_stringBuilder.Clear();
57-
_stringBuilder.Append('"').Append(Convert.ToBase64String(bytes)).Append('"');
58-
Theme.Render(canvas, StyleToken.String, _stringBuilder.ToString());
56+
_literalBuilder.Clear();
57+
_literalBuilder.Append('"').Append(Convert.ToBase64String(bytes)).Append('"');
58+
Theme.Render(canvas, StyleToken.String, _literalBuilder.ToString());
5959
return;
6060

6161
case bool b:
@@ -90,9 +90,9 @@ private void RenderString(string text, IRtfCanvas canvas, string? format, bool i
9090
}
9191
else
9292
{
93-
_stringBuilder.Clear();
94-
_stringBuilder.Append('"').Append(text.Replace("\"", "\\\"")).Append('"');
95-
Theme.Render(canvas, StyleToken.String, _stringBuilder.ToString());
93+
_literalBuilder.Clear();
94+
_literalBuilder.Append('"').Append(text.Replace("\"", "\\\"")).Append('"');
95+
Theme.Render(canvas, StyleToken.String, _literalBuilder.ToString());
9696
}
9797
}
9898

Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Formatting/JsonValueFormatter.cs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -218,65 +218,55 @@ private void FormatLiteralValue(ScalarValue scalar, IRtfCanvas canvas)
218218
}
219219
}
220220

221-
private static void WriteQuotedJsonString(string str, TextWriter output)
221+
private static void WriteQuotedJsonString(StringBuilder builder, string str)
222222
{
223-
output.Write('\"');
223+
builder.Append('\"');
224224

225225
foreach (var c in str)
226226
{
227227
switch (c)
228228
{
229229
case '"':
230-
output.Write("\\\"");
230+
builder.Append("\\\"");
231231
break;
232232

233233
case '\\':
234-
output.Write(@"\\");
234+
builder.Append(@"\\");
235235
break;
236236

237237
case '\n':
238-
output.Write("\\n");
238+
builder.Append("\\n");
239239
break;
240240

241241
case '\r':
242-
output.Write("\\r");
242+
builder.Append("\\r");
243243
break;
244244

245245
case '\f':
246-
output.Write("\\f");
246+
builder.Append("\\f");
247247
break;
248248

249249
case '\t':
250-
output.Write("\\t");
250+
builder.Append("\\t");
251251
break;
252252

253253
case < (char)32:
254-
output.Write("\\u");
255-
output.Write(((int)c).ToString("X4"));
254+
builder.Append("\\u");
255+
builder.Append(((int)c).ToString("X4"));
256256
break;
257257

258258
default:
259-
output.Write(c);
259+
builder.Append(c);
260260
break;
261261
}
262262
}
263-
output.Write('\"');
263+
builder.Append('\"');
264264
}
265265

266266
private string GetQuotedJsonString(string str)
267267
{
268268
_jsonStringBuilder.Clear();
269-
var estimatedCapacity = str.Length + 2;
270-
if (_jsonStringBuilder.Capacity < estimatedCapacity)
271-
{
272-
_jsonStringBuilder.Capacity = estimatedCapacity;
273-
}
274-
275-
using (var writer = new StringWriter(_jsonStringBuilder))
276-
{
277-
WriteQuotedJsonString(str, writer);
278-
}
279-
269+
WriteQuotedJsonString(_jsonStringBuilder, str);
280270
return _jsonStringBuilder.ToString();
281271
}
282272
}

Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/RichTextBoxSink.cs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ public class RichTextBoxSink : ILogEventSink, IDisposable
4040
private readonly CancellationTokenSource _tokenSource;
4141
private readonly Task _processingTask;
4242
private bool _disposed;
43-
private int _hasNewMessages;
4443

4544
public RichTextBoxSink(RichTextBox richTextBox, RichTextBoxSinkOptions options, ITokenRenderer? renderer = null)
4645
{
@@ -51,7 +50,6 @@ public RichTextBoxSink(RichTextBox richTextBox, RichTextBoxSinkOptions options,
5150

5251
_buffer = new ConcurrentCircularBuffer<LogEvent>(options.MaxLogLines);
5352
_signal = new AutoResetEvent(false);
54-
_hasNewMessages = 0;
5553

5654
richTextBox.Clear();
5755
richTextBox.ReadOnly = true;
@@ -78,24 +76,18 @@ public void Dispose()
7876
public void Emit(LogEvent logEvent)
7977
{
8078
_buffer.Add(logEvent);
81-
Update();
79+
_signal.Set();
8280
}
8381

8482
public void Clear()
8583
{
8684
_buffer.Clear();
87-
Update();
85+
_signal.Set();
8886
}
8987

9088
public void Restore()
9189
{
9290
_buffer.Restore();
93-
Update();
94-
}
95-
96-
private void Update()
97-
{
98-
Interlocked.Exchange(ref _hasNewMessages, 1);
9991
_signal.Set();
10092
}
10193

@@ -104,38 +96,48 @@ private void ProcessMessages(CancellationToken token)
10496
var builder = new RtfBuilder(_options.Theme);
10597
var snapshot = new System.Collections.Generic.List<LogEvent>(_options.MaxLogLines);
10698
var flushInterval = TimeSpan.FromMilliseconds(FlushIntervalMs);
107-
var lastFlush = DateTime.UtcNow;
99+
var lastFlush = DateTime.MinValue;
108100

109101
while (!token.IsCancellationRequested)
110102
{
111103
_signal.WaitOne();
112104

113-
if (Interlocked.CompareExchange(ref _hasNewMessages, 0, 1) != 1)
105+
if (token.IsCancellationRequested)
114106
{
115-
continue;
107+
break;
116108
}
117109

118110
var now = DateTime.UtcNow;
119-
var elapsed = now - lastFlush;
120-
if (elapsed < flushInterval)
111+
var elapsedSinceLastFlush = now - lastFlush;
112+
if (elapsedSinceLastFlush < flushInterval)
121113
{
122-
var remaining = flushInterval - elapsed;
123-
if (remaining > TimeSpan.Zero)
114+
var remainingTime = flushInterval - elapsedSinceLastFlush;
115+
if (token.WaitHandle.WaitOne(remainingTime))
124116
{
125-
Thread.Sleep(remaining);
117+
break;
126118
}
127119
}
128120

129-
Interlocked.Exchange(ref _hasNewMessages, 0);
121+
while (_signal.WaitOne(0))
122+
{
123+
}
124+
130125
_buffer.TakeSnapshot(snapshot);
126+
131127
builder.Clear();
132128
foreach (var evt in snapshot)
133129
{
134130
_renderer.Render(evt, builder);
135131
}
132+
133+
if (_richTextBox.IsDisposed || _richTextBox.Disposing)
134+
{
135+
continue;
136+
}
137+
136138
_richTextBox.SetRtf(builder.Rtf, _options.AutoScroll, token);
137139
lastFlush = DateTime.UtcNow;
138140
}
139141
}
140142
}
141-
}
143+
}

Serilog.Sinks.RichTextBox.WinForms.Colored/Sinks/RichTextBoxForms/Rtf/RtfBuilder.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ public void Dispose()
179179
public void Clear()
180180
{
181181
_body.Clear();
182+
_documentBuilder.Clear();
182183

183184
const int maxRetainedBuilderSize = 64 * 1024;
184185
if (_body.Capacity > maxRetainedBuilderSize)

0 commit comments

Comments
 (0)