Skip to content

Commit ab91578

Browse files
committed
⛲ log sink: refactor to improve efficiency
- Store formatted log messages instead of raw log events in queue. - Avoid allocating strings when formatting timestamp and log level.
1 parent a4edaf6 commit ab91578

File tree

1 file changed

+42
-24
lines changed

1 file changed

+42
-24
lines changed

YoutubeDl.Wpf/Models/QueuedTextBoxSink.cs

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using Serilog.Events;
55
using System;
66
using System.Collections.Generic;
7-
using System.Linq;
87
using System.Reactive.Concurrency;
98

109
namespace YoutubeDl.Wpf.Models;
@@ -13,61 +12,80 @@ public class QueuedTextBoxSink : ReactiveObject, ILogEventSink
1312
{
1413
private readonly object _locker = new();
1514
private readonly ObservableSettings _settings;
16-
private readonly Queue<LogEvent> _queuedLogEvents;
15+
private readonly Queue<string> _queuedLogMessages;
1716
private readonly IFormatProvider? _formatProvider;
17+
private int _contentLength;
1818

1919
[Reactive]
2020
public string Content { get; set; } = "";
2121

2222
public QueuedTextBoxSink(ObservableSettings settings, IFormatProvider? formatProvider = null)
2323
{
2424
_settings = settings;
25-
_queuedLogEvents = new(settings.LoggingMaxEntries);
25+
_queuedLogMessages = new(settings.LoggingMaxEntries);
2626
_formatProvider = formatProvider;
2727
}
2828

2929
public void Emit(LogEvent logEvent)
3030
{
3131
// Workaround for https://github.com/reactiveui/ReactiveUI/issues/3415 before upstream has a fix.
32-
if (logEvent.MessageTemplate.Text.EndsWith(" is a POCO type and won't send change notifications, WhenAny will only return a single value!"))
32+
if (logEvent.MessageTemplate.Text.EndsWith(" is a POCO type and won't send change notifications, WhenAny will only return a single value!", StringComparison.Ordinal))
3333
{
3434
return;
3535
}
3636

37+
var renderedMessage = logEvent.RenderMessage(_formatProvider);
38+
39+
// 2023-04-24T10:24:00.000+00:00 [I] Hi!
40+
var length = 29 + 1 + 3 + 1 + renderedMessage.Length + Environment.NewLine.Length;
41+
var message = string.Create(length, logEvent, (buf, logEvent) =>
42+
{
43+
if (!logEvent.Timestamp.TryFormat(buf, out var charsWritten, "yyyy-MM-ddTHH:mm:ss.fffzzz"))
44+
throw new Exception("Failed to format timestamp for log message.");
45+
if (charsWritten != 29)
46+
throw new Exception($"Unexpected formatted timestamp length {charsWritten}.");
47+
48+
buf[29] = ' ';
49+
buf[30] = '[';
50+
buf[31] = logEvent.Level switch
51+
{
52+
LogEventLevel.Verbose => 'V',
53+
LogEventLevel.Debug => 'D',
54+
LogEventLevel.Information => 'I',
55+
LogEventLevel.Warning => 'W',
56+
LogEventLevel.Error => 'E',
57+
LogEventLevel.Fatal => 'F',
58+
_ => '?',
59+
};
60+
buf[32] = ']';
61+
buf[33] = ' ';
62+
renderedMessage.CopyTo(buf[34..]);
63+
Environment.NewLine.CopyTo(buf[(34 + renderedMessage.Length)..]);
64+
});
65+
3766
lock (_locker)
3867
{
39-
if (_queuedLogEvents.Count >= _settings.LoggingMaxEntries)
68+
while (_queuedLogMessages.Count >= _settings.LoggingMaxEntries)
4069
{
41-
_queuedLogEvents.Dequeue();
70+
var dequeuedMessage = _queuedLogMessages.Dequeue();
71+
_contentLength -= dequeuedMessage.Length;
4272
}
4373

44-
_queuedLogEvents.Enqueue(logEvent);
74+
_queuedLogMessages.Enqueue(message);
75+
_contentLength += message.Length;
4576

46-
var messages = _queuedLogEvents.Select(x => (x.Timestamp.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz"), x.Level, x.RenderMessage(_formatProvider)));
47-
var length = messages.Sum(x => x.Item1.Length + 1 + 3 + 1 + x.Item3.Length + Environment.NewLine.Length);
48-
var text = string.Create(length, messages, (buf, msgs) =>
77+
var content = string.Create(_contentLength, _queuedLogMessages, (buf, msgs) =>
4978
{
5079
foreach (var msg in msgs)
5180
{
52-
msg.Item1.CopyTo(buf);
53-
buf[msg.Item1.Length] = ' ';
54-
buf = buf[(msg.Item1.Length + 1)..];
55-
56-
buf[0] = '[';
57-
buf[1] = msg.Level.ToString()[0];
58-
buf[2] = ']';
59-
buf[3] = ' ';
60-
buf = buf[4..];
61-
62-
msg.Item3.CopyTo(buf);
63-
Environment.NewLine.CopyTo(buf[msg.Item3.Length..]);
64-
buf = buf[(msg.Item3.Length + Environment.NewLine.Length)..];
81+
msg.CopyTo(buf);
82+
buf = buf[msg.Length..];
6583
}
6684
});
6785

6886
RxApp.MainThreadScheduler.Schedule(() =>
6987
{
70-
Content = text;
88+
Content = content;
7189
});
7290
}
7391
}

0 commit comments

Comments
 (0)