@@ -26,18 +26,20 @@ namespace Serilog.Sinks.File
26
26
/// </summary>
27
27
public sealed class FileSink : ILogEventSink , IDisposable
28
28
{
29
- const int BytesPerCharacterApproximate = 1 ;
30
29
readonly TextWriter _output ;
31
30
readonly ITextFormatter _textFormatter ;
31
+ readonly long ? _fileSizeLimitBytes ;
32
32
readonly bool _buffered ;
33
33
readonly object _syncRoot = new object ( ) ;
34
+ readonly WriteCountingStream _countingStreamWrapper ;
34
35
35
36
/// <summary>Construct a <see cref="FileSink"/>.</summary>
36
37
/// <param name="path">Path to the file.</param>
37
38
/// <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>
41
43
/// <param name="buffered">Indicates if flushing to the output file can be buffered or not. The default
42
44
/// is false.</param>
43
45
/// <returns>Configuration object allowing method chaining.</returns>
@@ -50,6 +52,7 @@ public FileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBy
50
52
if ( fileSizeLimitBytes . HasValue && fileSizeLimitBytes < 0 ) throw new ArgumentException ( "Negative value provided; file size limit must be non-negative" ) ;
51
53
52
54
_textFormatter = textFormatter ;
55
+ _fileSizeLimitBytes = fileSizeLimitBytes ;
53
56
_buffered = buffered ;
54
57
55
58
var directory = Path . GetDirectoryName ( path ) ;
@@ -58,18 +61,13 @@ public FileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBy
58
61
Directory . CreateDirectory ( directory ) ;
59
62
}
60
63
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 )
64
66
{
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 ) ;
72
68
}
69
+
70
+ _output = new StreamWriter ( file , encoding ?? new UTF8Encoding ( encoderShouldEmitUTF8Identifier : false ) ) ;
73
71
}
74
72
75
73
/// <summary>
@@ -81,6 +79,12 @@ public void Emit(LogEvent logEvent)
81
79
if ( logEvent == null ) throw new ArgumentNullException ( nameof ( logEvent ) ) ;
82
80
lock ( _syncRoot )
83
81
{
82
+ if ( _fileSizeLimitBytes != null )
83
+ {
84
+ if ( _countingStreamWrapper . CountedLength >= _fileSizeLimitBytes . Value )
85
+ return ;
86
+ }
87
+
84
88
_textFormatter . Format ( logEvent , _output ) ;
85
89
if ( ! _buffered )
86
90
_output . Flush ( ) ;
0 commit comments