diff --git a/src/Serilog.Sinks.File/Sinks/File/FileSink.cs b/src/Serilog.Sinks.File/Sinks/File/FileSink.cs
index dd83a3f..b1e3cca 100644
--- a/src/Serilog.Sinks.File/Sinks/File/FileSink.cs
+++ b/src/Serilog.Sinks.File/Sinks/File/FileSink.cs
@@ -80,7 +80,9 @@ internal FileSink(
Directory.CreateDirectory(directory);
}
- Stream outputStream = _underlyingStream = System.IO.File.Open(path, FileMode.Append, FileAccess.Write, FileShare.Read);
+ Stream outputStream = _underlyingStream = System.IO.File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
+ outputStream.Seek(0, SeekOrigin.End);
+
if (_fileSizeLimitBytes != null)
{
outputStream = _countingStreamWrapper = new WriteCountingStream(_underlyingStream);
diff --git a/src/Serilog.Sinks.File/Sinks/File/WriteCountingStream.cs b/src/Serilog.Sinks.File/Sinks/File/WriteCountingStream.cs
index fe0d5d3..e247144 100644
--- a/src/Serilog.Sinks.File/Sinks/File/WriteCountingStream.cs
+++ b/src/Serilog.Sinks.File/Sinks/File/WriteCountingStream.cs
@@ -63,7 +63,13 @@ public override long Seek(long offset, SeekOrigin origin)
public override void SetLength(long value)
{
- throw new NotSupportedException();
+ _stream.SetLength(value);
+
+ if (value < CountedLength)
+ {
+ // File is now shorter and our position has changed to _stream.Length
+ CountedLength = _stream.Length;
+ }
}
public override int Read(byte[] buffer, int offset, int count)
diff --git a/test/Serilog.Sinks.File.Tests/FileSinkTests.cs b/test/Serilog.Sinks.File.Tests/FileSinkTests.cs
index 80c088f..a33261f 100644
--- a/test/Serilog.Sinks.File.Tests/FileSinkTests.cs
+++ b/test/Serilog.Sinks.File.Tests/FileSinkTests.cs
@@ -223,6 +223,32 @@ public static void OnOpenedLifecycleHookCanCaptureFilePath()
}
}
+ [Fact]
+ public static void OnOpenedLifecycleHookCanEmptyTheFileContents()
+ {
+ using (var tmp = TempFolder.ForCaller())
+ {
+ var emptyFileHook = new TruncateFileHook();
+
+ var path = tmp.AllocateFilename("txt");
+ using (var sink = new FileSink(path, new JsonFormatter(), fileSizeLimitBytes: null, encoding: new UTF8Encoding(false), buffered: false))
+ {
+ sink.Emit(Some.LogEvent());
+ }
+
+ using (var sink = new FileSink(path, new JsonFormatter(), fileSizeLimitBytes: null, encoding: new UTF8Encoding(false), buffered: false, hooks: emptyFileHook))
+ {
+ // Hook will clear the contents of the file before emitting the log events
+ sink.Emit(Some.LogEvent());
+ }
+
+ var lines = System.IO.File.ReadAllLines(path);
+
+ Assert.Single(lines);
+ Assert.Equal('{', lines[0][0]);
+ }
+ }
+
static void WriteTwoEventsAndCheckOutputFileLength(long? maxBytes, Encoding encoding)
{
using (var tmp = TempFolder.ForCaller())
diff --git a/test/Serilog.Sinks.File.Tests/Support/TruncateFileHook.cs b/test/Serilog.Sinks.File.Tests/Support/TruncateFileHook.cs
new file mode 100644
index 0000000..63f8497
--- /dev/null
+++ b/test/Serilog.Sinks.File.Tests/Support/TruncateFileHook.cs
@@ -0,0 +1,18 @@
+using System.IO;
+using System.Text;
+
+namespace Serilog.Sinks.File.Tests.Support
+{
+ ///
+ ///
+ /// Demonstrates the use of , by emptying the file before it's written to
+ ///
+ public class TruncateFileHook : FileLifecycleHooks
+ {
+ public override Stream OnFileOpened(Stream underlyingStream, Encoding encoding)
+ {
+ underlyingStream.SetLength(0);
+ return base.OnFileOpened(underlyingStream, encoding);
+ }
+ }
+}
diff --git a/test/Serilog.Sinks.File.Tests/WriteCountingStreamTests.cs b/test/Serilog.Sinks.File.Tests/WriteCountingStreamTests.cs
new file mode 100644
index 0000000..887ffe2
--- /dev/null
+++ b/test/Serilog.Sinks.File.Tests/WriteCountingStreamTests.cs
@@ -0,0 +1,83 @@
+using System.IO;
+using System.Text;
+using Serilog.Sinks.File.Tests.Support;
+using Xunit;
+
+namespace Serilog.Sinks.File.Tests
+{
+ public class WriteCountingStreamTests
+ {
+ [Fact]
+ public void CountedLengthIsResetToStreamLengthIfNewSizeIsSmaller()
+ {
+ // If we counted 10 bytes written and SetLength was called with a smaller length (e.g. 5)
+ // we adjust the counter to the new byte count of the file to reflect reality
+
+ using (var tmp = TempFolder.ForCaller())
+ {
+ var path = tmp.AllocateFilename("txt");
+
+ long streamLengthAfterSetLength;
+ long countedLengthAfterSetLength;
+
+ using (var fileStream = System.IO.File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var countingStream = new WriteCountingStream(fileStream))
+ using (var writer = new StreamWriter(countingStream, new UTF8Encoding(false)))
+ {
+ writer.WriteLine("Hello, world!");
+ writer.Flush();
+
+ countingStream.SetLength(5);
+ streamLengthAfterSetLength = countingStream.Length;
+ countedLengthAfterSetLength = countingStream.CountedLength;
+ }
+
+ Assert.Equal(5, streamLengthAfterSetLength);
+ Assert.Equal(5, countedLengthAfterSetLength);
+
+ var lines = System.IO.File.ReadAllLines(path);
+
+ Assert.Single(lines);
+ Assert.Equal("Hello", lines[0]);
+ }
+ }
+
+ [Fact]
+ public void CountedLengthRemainsTheSameIfNewSizeIsLarger()
+ {
+ // If we counted 10 bytes written and SetLength was called with a larger length (e.g. 100)
+ // we leave the counter intact because our position on the stream remains the same... The
+ // file just grew larger in size
+
+ using (var tmp = TempFolder.ForCaller())
+ {
+ var path = tmp.AllocateFilename("txt");
+
+ long streamLengthBeforeSetLength;
+ long streamLengthAfterSetLength;
+ long countedLengthAfterSetLength;
+
+ using (var fileStream = System.IO.File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var countingStream = new WriteCountingStream(fileStream))
+ using (var writer = new StreamWriter(countingStream, new UTF8Encoding(false)))
+ {
+ writer.WriteLine("Hello, world!");
+ writer.Flush();
+
+ streamLengthBeforeSetLength = countingStream.CountedLength;
+ countingStream.SetLength(100);
+ streamLengthAfterSetLength = countingStream.Length;
+ countedLengthAfterSetLength = countingStream.CountedLength;
+ }
+
+ Assert.Equal(100, streamLengthAfterSetLength);
+ Assert.Equal(streamLengthBeforeSetLength, countedLengthAfterSetLength);
+
+ var lines = System.IO.File.ReadAllLines(path);
+
+ Assert.Equal(2, lines.Length);
+ Assert.Equal("Hello, world!", lines[0]);
+ }
+ }
+ }
+}