21
21
using System . Diagnostics ;
22
22
using System . IO ;
23
23
using System . Text ;
24
- using System . Threading ;
24
+ using System . Threading . Channels ;
25
+ using System . Threading . Tasks ;
25
26
using System . Windows . Threading ;
26
27
using Serilog . Core ;
27
28
using Serilog . Events ;
@@ -43,8 +44,8 @@ internal sealed class RichTextBoxSink : ILogEventSink, IDisposable
43
44
private const int _defaultWriteBufferCapacity = 256 ;
44
45
45
46
private const int _batchSize = 200 ;
46
- private Thread _consumerThread ;
47
- private ConcurrentQueue < LogEvent > _messageQueue ;
47
+ private const int _minimumDelayForIncompleteBatch = 25 ;
48
+ private Channel < LogEvent > _messageChannel ;
48
49
49
50
public RichTextBoxSink ( IRichTextBox richTextBox , ITextFormatter formatter , DispatcherPriority dispatcherPriority , object syncRoot )
50
51
{
@@ -62,83 +63,64 @@ public RichTextBoxSink(IRichTextBox richTextBox, ITextFormatter formatter, Dispa
62
63
63
64
_renderAction = Render ;
64
65
65
- _messageQueue = new ConcurrentQueue < LogEvent > ( ) ;
66
+ _messageChannel = Channel . CreateUnbounded < LogEvent > ( ) ;
66
67
67
- _consumerThread = new Thread ( new ThreadStart ( ProcessMessages ) ) { IsBackground = true } ;
68
- _consumerThread . Start ( ) ;
68
+ Task . Run ( ProcessMessages ) ;
69
69
}
70
70
71
- private enum States
71
+ private async Task ProcessMessages ( )
72
72
{
73
- Init ,
74
- Dequeue ,
75
- Log ,
76
- }
77
-
78
- private void ProcessMessages ( )
79
- {
80
- StringBuilder sb = new ( ) ;
81
- Stopwatch sw = Stopwatch . StartNew ( ) ;
82
- States state = States . Init ;
83
73
int msgCounter = 0 ;
74
+ const string initial = $ "<Paragraph xmlns =\" http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xml:space=\" preserve\" >";
75
+
76
+ StringBuilder sb = new ( initial ) ;
77
+
78
+ async Task < string > ReadChannelAsync ( )
79
+ {
80
+ var logEvent = await _messageChannel . Reader . ReadAsync ( ) ;
81
+ StringWriter writer = new ( ) ;
82
+ _formatter . Format ( logEvent , writer ) ;
83
+ return writer . ToString ( ) ;
84
+ }
85
+
86
+ Task restartTimer ( ) => Task . Delay ( _minimumDelayForIncompleteBatch ) ;
87
+
88
+ var incompleteBatchTask = restartTimer ( ) ;
89
+ var logEventTask = ReadChannelAsync ( ) ;
84
90
85
91
while ( true )
86
92
{
87
- switch ( state )
93
+ var firstTask = await Task . WhenAny ( incompleteBatchTask , logEventTask ) ;
94
+
95
+ if ( firstTask == logEventTask )
88
96
{
89
- //prepare the string builder and data
90
- case States . Init :
91
- sb . Clear ( ) ;
92
- sb . Append ( $ "<Paragraph xmlns =\" http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xml:space=\" preserve\" >") ;
93
- msgCounter = 0 ;
94
- state = States . Dequeue ;
95
- break ;
96
-
97
- case States . Dequeue :
98
- if ( sw . Elapsed . TotalMilliseconds >= 25 || msgCounter >= _batchSize )
99
- {
100
- if ( msgCounter == 0 )
101
- {
102
- //no messages, retick
103
- sw . Restart ( ) ;
104
- }
105
- else
106
- {
107
- //valid log condition
108
- state = States . Log ;
109
- break ;
110
- }
111
- }
112
-
113
- if ( _messageQueue . TryDequeue ( out LogEvent logEvent ) == false )
114
- {
115
- Thread . Sleep ( 1 ) ;
116
- continue ;
117
- }
118
-
119
- StringWriter writer = new ( ) ;
120
- _formatter . Format ( logEvent , writer ) ;
121
-
122
- //got a message from the queue, retick
123
- sw . Restart ( ) ;
124
-
125
- msgCounter ++ ;
126
- sb . Append ( writer . ToString ( ) ) ;
127
- break ;
128
-
129
- case States . Log :
130
- sb . Append ( "</Paragraph>" ) ;
131
- string xamlParagraphText = sb . ToString ( ) ;
132
- _richTextBox . BeginInvoke ( _dispatcherPriority , _renderAction , xamlParagraphText ) ;
133
- state = States . Init ;
134
- break ;
97
+ sb . Append ( await logEventTask ) ;
98
+ msgCounter ++ ;
99
+ if ( msgCounter < _batchSize )
100
+ {
101
+ logEventTask = ReadChannelAsync ( ) ;
102
+ continue ;
103
+ }
135
104
}
105
+ else if ( msgCounter == 0 )
106
+ {
107
+ //no messages, restart timer
108
+ incompleteBatchTask = restartTimer ( ) ;
109
+ continue ;
110
+ }
111
+
112
+ sb . Append ( "</Paragraph>" ) ;
113
+ string xamlParagraphText = sb . ToString ( ) ;
114
+ await _richTextBox . BeginInvoke ( _dispatcherPriority , _renderAction , xamlParagraphText ) ;
115
+ sb . Clear ( ) ;
116
+ sb . Append ( initial ) ;
117
+ msgCounter = 0 ;
136
118
}
137
119
}
138
120
139
121
public void Emit ( LogEvent logEvent )
140
- {
141
- _messageQueue . Enqueue ( logEvent ) ;
122
+ {
123
+ _messageChannel . Writer . TryWrite ( logEvent ) ;
142
124
}
143
125
144
126
private void Render ( string xamlParagraphText )
0 commit comments