99using System . Linq ;
1010using System . Threading ;
1111using System . Threading . Tasks ;
12+ using System . Runtime . InteropServices ;
13+ using System . Buffers ;
1214
1315namespace HttpStress
1416{
1517 public sealed class LogHttpEventListener : EventListener
1618 {
1719 private int _lastLogNumber = 0 ;
18- private StreamWriter _log ;
20+ private FileStream _log ;
1921 private Channel < string > _messagesChannel = Channel . CreateUnbounded < string > ( ) ;
2022 private Task _processMessages ;
21- private CancellationTokenSource _stopProcessing ;
23+ private CancellationTokenSource _stopProcessing = new CancellationTokenSource ( ) ;
24+ private const string LogDirectory = "./clientlog" ;
25+
26+ private FileStream CreateNextLogFileStream ( )
27+ {
28+ string fn = Path . Combine ( LogDirectory , $ "client_{ ++ _lastLogNumber : 000} .log") ;
29+ if ( File . Exists ( fn ) )
30+ {
31+ File . Delete ( fn ) ;
32+ }
33+ return new FileStream ( fn , FileMode . CreateNew , FileAccess . Write ) ;
34+ }
2235
2336 public LogHttpEventListener ( )
2437 {
25- foreach ( var filename in Directory . GetFiles ( "." , "client*.log" ) )
38+ if ( ! Directory . Exists ( LogDirectory ) )
39+ {
40+ Directory . CreateDirectory ( LogDirectory ) ;
41+ }
42+
43+ foreach ( string filename in Directory . GetFiles ( LogDirectory , "client*.log" ) )
2644 {
2745 try
2846 {
2947 File . Delete ( filename ) ;
3048 } catch { }
3149 }
32- _log = new StreamWriter ( "client.log" , false ) { AutoFlush = true } ;
33-
50+ _log = CreateNextLogFileStream ( ) ;
3451 _messagesChannel = Channel . CreateUnbounded < string > ( ) ;
3552 _processMessages = ProcessMessagesAsync ( ) ;
36- _stopProcessing = new CancellationTokenSource ( ) ;
3753 }
3854
3955 protected override void OnEventSourceCreated ( EventSource eventSource )
@@ -46,40 +62,51 @@ protected override void OnEventSourceCreated(EventSource eventSource)
4662
4763 private async Task ProcessMessagesAsync ( )
4864 {
49- await Task . Yield ( ) ;
50-
5165 try
5266 {
67+ byte [ ] buffer = new byte [ 8192 ] ;
68+ var encoding = Encoding . ASCII ;
69+
5370 int i = 0 ;
5471 await foreach ( string message in _messagesChannel . Reader . ReadAllAsync ( _stopProcessing . Token ) )
5572 {
5673 if ( ( ++ i % 10_000 ) == 0 )
5774 {
58- RotateFiles ( ) ;
75+ await RotateFiles ( ) ;
5976 }
60-
61- _log . WriteLine ( message ) ;
77+ int maxLen = encoding . GetMaxByteCount ( message . Length ) ;
78+ if ( maxLen > buffer . Length )
79+ {
80+ buffer = new byte [ maxLen ] ;
81+ }
82+ int byteCount = encoding . GetBytes ( message , buffer ) ;
83+
84+ await _log . WriteAsync ( buffer . AsMemory ( 0 , byteCount ) , _stopProcessing . Token ) ;
6285 }
6386 }
6487 catch ( OperationCanceledException )
6588 {
6689 return ;
6790 }
6891
69- void RotateFiles ( )
92+ async ValueTask RotateFiles ( )
7093 {
94+ await _log . FlushAsync ( _stopProcessing . Token ) ;
7195 // Rotate the log if it reaches 50 MB size.
72- if ( _log . BaseStream . Length > ( 50 << 20 ) )
96+ if ( _log . Length > ( 100 << 20 ) )
7397 {
74- _log . Close ( ) ;
75- _log = new StreamWriter ( $ "client_ { ++ _lastLogNumber : 000 } .log" , false ) { AutoFlush = true } ;
98+ await _log . DisposeAsync ( ) ;
99+ _log = CreateNextLogFileStream ( ) ;
76100 }
77101 }
78102 }
79103
104+ private StringBuilder ? _cachedStringBuilder ;
105+
80106 protected override async void OnEventWritten ( EventWrittenEventArgs eventData )
81107 {
82- var sb = new StringBuilder ( ) . Append ( $ "{ eventData . TimeStamp : HH:mm:ss.fffffff} [{ eventData . EventName } ] ") ;
108+ StringBuilder sb = Interlocked . Exchange ( ref _cachedStringBuilder , null ) ?? new StringBuilder ( ) ;
109+ sb . Append ( $ "{ eventData . TimeStamp : HH:mm:ss.fffffff} [{ eventData . EventName } ] ") ;
83110 for ( int i = 0 ; i < eventData . Payload ? . Count ; i ++ )
84111 {
85112 if ( i > 0 )
@@ -88,13 +115,17 @@ protected override async void OnEventWritten(EventWrittenEventArgs eventData)
88115 }
89116 sb . Append ( eventData . PayloadNames ? [ i ] ) . Append ( ": " ) . Append ( eventData . Payload [ i ] ) ;
90117 }
91- await _messagesChannel . Writer . WriteAsync ( sb . ToString ( ) ) ;
118+ sb . Append ( Environment . NewLine ) ;
119+ string s = sb . ToString ( ) ;
120+ sb . Clear ( ) ;
121+ Interlocked . Exchange ( ref _cachedStringBuilder , sb ) ;
122+ await _messagesChannel . Writer . WriteAsync ( s , _stopProcessing . Token ) ;
92123 }
93124
94125 public override void Dispose ( )
95126 {
96127 base . Dispose ( ) ;
97-
128+ _log . Flush ( ) ;
98129 if ( ! _processMessages . Wait ( TimeSpan . FromSeconds ( 30 ) ) )
99130 {
100131 _stopProcessing . Cancel ( ) ;
0 commit comments