@@ -26,18 +26,20 @@ namespace Serilog.Sinks.File
2626 /// </summary>
2727 public sealed class FileSink : ILogEventSink , IDisposable
2828 {
29- const int BytesPerCharacterApproximate = 1 ;
3029 readonly TextWriter _output ;
3130 readonly ITextFormatter _textFormatter ;
31+ readonly long ? _fileSizeLimitBytes ;
3232 readonly bool _buffered ;
3333 readonly object _syncRoot = new object ( ) ;
34+ readonly WriteCountingStream _countingStreamWrapper ;
3435
3536 /// <summary>Construct a <see cref="FileSink"/>.</summary>
3637 /// <param name="path">Path to the file.</param>
3738 /// <param name="textFormatter">Formatter used to convert log events to text.</param>
38- /// <param name="fileSizeLimitBytes">The maximum size, in bytes, to which a log file will be allowed to grow.
39- /// For unrestricted growth, pass null. The default is 1 GB.</param>
40- /// <param name="encoding">Character encoding used to write the text file. The default is UTF-8.</param>
39+ /// <param name="fileSizeLimitBytes">The approximate maximum size, in bytes, to which a log file will be allowed to grow.
40+ /// For unrestricted growth, pass null. The default is 1 GB. To avoid writing partial events, the last event within the limit
41+ /// will be written in full even if it exceeds the limit.</param>
42+ /// <param name="encoding">Character encoding used to write the text file. The default is UTF-8 without BOM.</param>
4143 /// <param name="buffered">Indicates if flushing to the output file can be buffered or not. The default
4244 /// is false.</param>
4345 /// <returns>Configuration object allowing method chaining.</returns>
@@ -50,6 +52,7 @@ public FileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBy
5052 if ( fileSizeLimitBytes . HasValue && fileSizeLimitBytes < 0 ) throw new ArgumentException ( "Negative value provided; file size limit must be non-negative" ) ;
5153
5254 _textFormatter = textFormatter ;
55+ _fileSizeLimitBytes = fileSizeLimitBytes ;
5356 _buffered = buffered ;
5457
5558 var directory = Path . GetDirectoryName ( path ) ;
@@ -58,18 +61,13 @@ public FileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBy
5861 Directory . CreateDirectory ( directory ) ;
5962 }
6063
61- var file = System . IO . File . Open ( path , FileMode . Append , FileAccess . Write , FileShare . Read ) ;
62- var outputWriter = new StreamWriter ( file , encoding ?? new UTF8Encoding ( encoderShouldEmitUTF8Identifier : false ) ) ;
63- if ( fileSizeLimitBytes != null )
64+ Stream file = System . IO . File . Open ( path , FileMode . Append , FileAccess . Write , FileShare . Read ) ;
65+ if ( _fileSizeLimitBytes != null )
6466 {
65- var initialBytes = file . Length ;
66- var remainingCharacters = Math . Max ( fileSizeLimitBytes . Value - initialBytes , 0L ) / BytesPerCharacterApproximate ;
67- _output = new CharacterCountLimitedTextWriter ( outputWriter , remainingCharacters ) ;
68- }
69- else
70- {
71- _output = outputWriter ;
67+ file = _countingStreamWrapper = new WriteCountingStream ( file ) ;
7268 }
69+
70+ _output = new StreamWriter ( file , encoding ?? new UTF8Encoding ( encoderShouldEmitUTF8Identifier : false ) ) ;
7371 }
7472
7573 /// <summary>
@@ -81,6 +79,12 @@ public void Emit(LogEvent logEvent)
8179 if ( logEvent == null ) throw new ArgumentNullException ( nameof ( logEvent ) ) ;
8280 lock ( _syncRoot )
8381 {
82+ if ( _fileSizeLimitBytes != null )
83+ {
84+ if ( _countingStreamWrapper . CountedLength >= _fileSizeLimitBytes . Value )
85+ return ;
86+ }
87+
8488 _textFormatter . Format ( logEvent , _output ) ;
8589 if ( ! _buffered )
8690 _output . Flush ( ) ;
0 commit comments