15
15
#endregion
16
16
17
17
using System ;
18
+ using System . Collections . Concurrent ;
19
+ using System . Collections . Generic ;
18
20
using System . ComponentModel ;
21
+ using System . Diagnostics ;
19
22
using System . IO ;
20
23
using System . Text ;
24
+ using System . Threading ;
21
25
using System . Windows . Threading ;
22
26
using Serilog . Core ;
23
27
using Serilog . Events ;
24
28
using Serilog . Formatting ;
25
29
using Serilog . Sinks . RichTextBox . Abstraction ;
26
30
31
+
32
+
27
33
namespace Serilog . Sinks . RichTextBox
28
34
{
29
35
internal sealed class RichTextBoxSink : ILogEventSink , IDisposable
@@ -36,42 +42,103 @@ internal sealed class RichTextBoxSink : ILogEventSink, IDisposable
36
42
private readonly RenderAction _renderAction ;
37
43
private const int _defaultWriteBufferCapacity = 256 ;
38
44
45
+ private const int _batchSize = 200 ;
46
+ private Thread _consumerThread ;
47
+ private ConcurrentQueue < LogEvent > _messageQueue ;
48
+
39
49
public RichTextBoxSink ( IRichTextBox richTextBox , ITextFormatter formatter , DispatcherPriority dispatcherPriority , object syncRoot )
40
50
{
41
51
_richTextBox = richTextBox ?? throw new ArgumentNullException ( nameof ( richTextBox ) ) ;
42
52
_formatter = formatter ?? throw new ArgumentNullException ( nameof ( formatter ) ) ;
43
53
44
54
if ( ! Enum . IsDefined ( typeof ( DispatcherPriority ) , dispatcherPriority ) )
45
55
{
46
- throw new InvalidEnumArgumentException ( nameof ( dispatcherPriority ) , ( int ) dispatcherPriority ,
56
+ throw new InvalidEnumArgumentException ( nameof ( dispatcherPriority ) , ( int ) dispatcherPriority ,
47
57
typeof ( DispatcherPriority ) ) ;
48
58
}
49
59
50
60
_dispatcherPriority = dispatcherPriority ;
51
61
_syncRoot = syncRoot ?? throw new ArgumentNullException ( nameof ( syncRoot ) ) ;
52
62
53
63
_renderAction = Render ;
54
- }
55
64
56
- public void Emit ( LogEvent logEvent )
57
- {
58
- var buffer = new StringWriter ( new StringBuilder ( _defaultWriteBufferCapacity ) ) ;
59
- _formatter . Format ( logEvent , buffer ) ;
65
+ _messageQueue = new ConcurrentQueue < LogEvent > ( ) ;
60
66
61
- var formattedLogEventText = buffer . ToString ( ) ;
67
+ _consumerThread = new Thread ( new ThreadStart ( ProcessMessages ) ) { IsBackground = true } ;
68
+ _consumerThread . Start ( ) ;
69
+ }
62
70
63
- var xamlParagraphText =
64
- $ "<Paragraph xmlns=\" http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xml:space=\" preserve\" >{ formattedLogEventText } </Paragraph>";
71
+ private enum States
72
+ {
73
+ Init ,
74
+ Dequeue ,
75
+ Log ,
76
+ }
65
77
66
- var richTextBox = _richTextBox ;
78
+ private void ProcessMessages ( )
79
+ {
80
+ StringBuilder sb = new ( ) ;
81
+ Stopwatch sw = Stopwatch . StartNew ( ) ;
82
+ States state = States . Init ;
83
+ int msgCounter = 0 ;
67
84
68
- if ( ! richTextBox . CheckAccess ( ) )
85
+ while ( true )
69
86
{
70
- richTextBox . BeginInvoke ( _dispatcherPriority , _renderAction , xamlParagraphText ) ;
71
- return ;
87
+ switch ( state )
88
+ {
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 ;
135
+ }
72
136
}
137
+ }
73
138
74
- Render ( xamlParagraphText ) ;
139
+ public void Emit ( LogEvent logEvent )
140
+ {
141
+ _messageQueue . Enqueue ( logEvent ) ;
75
142
}
76
143
77
144
private void Render ( string xamlParagraphText )
0 commit comments