diff --git a/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.AtomicAppend.cs b/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.AtomicAppend.cs
index 866f807..4f9786d 100644
--- a/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.AtomicAppend.cs
+++ b/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.AtomicAppend.cs
@@ -72,7 +72,7 @@ public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeL
path,
FileMode.Append,
FileSystemRights.AppendData,
- FileShare.ReadWrite,
+ FileShare.ReadWrite | FileShare.Delete,
_fileStreamBufferLength,
FileOptions.None);
@@ -101,7 +101,23 @@ bool IFileSink.EmitOrOverflow(LogEvent logEvent)
_path,
FileMode.Append,
FileSystemRights.AppendData,
- FileShare.ReadWrite,
+ FileShare.ReadWrite | FileShare.Delete,
+ length,
+ FileOptions.None);
+ _fileStreamBufferLength = length;
+
+ oldOutput.Dispose();
+ }
+
+ if (!System.IO.File.Exists(_path))
+ {
+ var oldOutput = _fileOutput;
+
+ _fileOutput = new FileStream(
+ _path,
+ FileMode.Append,
+ FileSystemRights.AppendData,
+ FileShare.ReadWrite | FileShare.Delete,
length,
FileOptions.None);
_fileStreamBufferLength = length;
diff --git a/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.OSMutex.cs b/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.OSMutex.cs
index 41a19ef..031bbcf 100644
--- a/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.OSMutex.cs
+++ b/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.OSMutex.cs
@@ -30,8 +30,9 @@ namespace Serilog.Sinks.File
[Obsolete("This type will be removed from the public API in a future version; use `WriteTo.File(shared: true)` instead.")]
public sealed class SharedFileSink : IFileSink, IDisposable
{
- readonly TextWriter _output;
- readonly FileStream _underlyingStream;
+ TextWriter _output;
+ FileStream _underlyingStream;
+ readonly string _path;
readonly ITextFormatter _textFormatter;
readonly long? _fileSizeLimitBytes;
readonly object _syncRoot = new object();
@@ -52,21 +53,21 @@ public sealed class SharedFileSink : IFileSink, IDisposable
///
public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes, Encoding encoding = null)
{
- if (path == null) throw new ArgumentNullException(nameof(path));
+ _path = path ?? throw new ArgumentNullException(nameof(path));
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0)
throw new ArgumentException("Negative value provided; file size limit must be non-negative");
_textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter));
_fileSizeLimitBytes = fileSizeLimitBytes;
- var directory = Path.GetDirectoryName(path);
+ var directory = Path.GetDirectoryName(_path);
if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
- var mutexName = Path.GetFullPath(path).Replace(Path.DirectorySeparatorChar, ':') + MutexNameSuffix;
+ var mutexName = Path.GetFullPath(_path).Replace(Path.DirectorySeparatorChar, ':') + MutexNameSuffix;
_mutex = new Mutex(false, mutexName);
- _underlyingStream = System.IO.File.Open(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
+ _underlyingStream = System.IO.File.Open(_path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete);
_output = new StreamWriter(_underlyingStream, encoding ?? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
}
@@ -81,6 +82,13 @@ bool IFileSink.EmitOrOverflow(LogEvent logEvent)
try
{
+ if (!System.IO.File.Exists(_path))
+ {
+ _underlyingStream.Dispose();
+ _underlyingStream = System.IO.File.Open(_path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete);
+ _output = new StreamWriter(_underlyingStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
+ }
+
_underlyingStream.Seek(0, SeekOrigin.End);
if (_fileSizeLimitBytes != null)
{
diff --git a/test/Serilog.Sinks.File.Tests/FileSinkTests.cs b/test/Serilog.Sinks.File.Tests/FileSinkTests.cs
index 10fe926..7c9698d 100644
--- a/test/Serilog.Sinks.File.Tests/FileSinkTests.cs
+++ b/test/Serilog.Sinks.File.Tests/FileSinkTests.cs
@@ -30,6 +30,38 @@ public void FileIsWrittenIfNonexistent()
Assert.Contains("Hello, world!", lines[0]);
}
}
+ [Fact]
+ public void FileIsReWrittenAfterEventIfDeleted()
+ {
+ using (var tmp = TempFolder.ForCaller())
+ {
+ var nonexistent = tmp.AllocateFilename("txt");
+ var evt = Some.LogEvent("Hello, world!");
+
+ void Emmit()
+ {
+ using (var sink = new FileSink(nonexistent, new JsonFormatter(), null))
+ {
+ sink.Emit(evt);
+ }
+ }
+
+ Emmit();
+ var lines = System.IO.File.ReadAllLines(nonexistent);
+ Assert.Contains("Hello, world!", lines[0]);
+ Assert.Single(lines);
+
+ System.IO.File.Delete(nonexistent);
+ Assert.False(System.IO.File.Exists(nonexistent));
+ Assert.Throws(() => System.IO.File.ReadAllLines(nonexistent));
+
+ Emmit();
+ lines = System.IO.File.ReadAllLines(nonexistent);
+ Assert.True(System.IO.File.Exists(nonexistent));
+ Assert.Contains("Hello, world!", lines[0]);
+ Assert.Single(lines);
+ }
+ }
[Fact]
public void FileIsAppendedToWhenAlreadyCreated()
diff --git a/test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs b/test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs
index d295dfc..e8a446f 100644
--- a/test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs
+++ b/test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs
@@ -4,6 +4,7 @@
using System.IO.Compression;
using System.Linq;
using System.Reflection;
+using System.Threading;
using Xunit;
using Serilog.Events;
using Serilog.Sinks.File.Tests.Support;
@@ -261,6 +262,73 @@ public void IfTheLogFolderDoesNotExistItWillBeCreated()
}
}
+ [Fact]
+ public void ShouldReCreateDeletedFiles()
+ {
+ var fileName = Some.String() + "-{Date}.txt";
+ var temp = Some.TempFolderPath();
+ var folder = Path.Combine(temp, Guid.NewGuid().ToString());
+ var pathFormat = Path.Combine(folder, fileName);
+
+ Logger log = null;
+
+ try
+ {
+ log = new LoggerConfiguration()
+ .WriteTo.File(pathFormat, rollingInterval: RollingInterval.Day, shared:true)
+ .CreateLogger();
+
+ log.Write(Some.InformationEvent());
+ Assert.True(Directory.Exists(folder));
+
+ var createdFile = Directory.GetFiles(folder)[0];
+ System.IO.File.Delete(createdFile);
+ Assert.False(System.IO.File.Exists(createdFile));
+
+ log = new LoggerConfiguration()
+ .WriteTo.File(pathFormat, rollingInterval: RollingInterval.Day, shared: true)
+ .CreateLogger();
+
+ log.Write(Some.InformationEvent());
+ Assert.True(System.IO.File.Exists(createdFile));
+ }
+ finally
+ {
+ log?.Dispose();
+ Directory.Delete(temp, true);
+ }
+ }
+
+ [Fact]
+ public void ShouldBeAbleToDeleteFile()
+ {
+ var fileName = Some.String() + "-{Date}.txt";
+ var temp = Some.TempFolderPath();
+ var folder = Path.Combine(temp, Guid.NewGuid().ToString());
+ var pathFormat = Path.Combine(folder, fileName);
+
+ Logger log = null;
+
+ try
+ {
+ log = new LoggerConfiguration()
+ .WriteTo.File(pathFormat, retainedFileCountLimit: 3, rollingInterval: RollingInterval.Day, shared:true, flushToDiskInterval:TimeSpan.FromSeconds(1), buffered:false)
+ .CreateLogger();
+
+ log.Write(Some.InformationEvent());
+ Assert.True(Directory.Exists(folder));
+
+ var createdFile = Directory.GetFiles(folder)[0];
+ System.IO.File.Delete(createdFile);
+ Assert.False(System.IO.File.Exists(createdFile));
+ }
+ finally
+ {
+ log?.Dispose();
+ Directory.Delete(temp, true);
+ }
+ }
+
[Fact]
public void AssemblyVersionIsFixedAt200()
{
diff --git a/test/Serilog.Sinks.File.Tests/SharedFileSinkTests.cs b/test/Serilog.Sinks.File.Tests/SharedFileSinkTests.cs
index 565be9b..5832825 100644
--- a/test/Serilog.Sinks.File.Tests/SharedFileSinkTests.cs
+++ b/test/Serilog.Sinks.File.Tests/SharedFileSinkTests.cs
@@ -26,6 +26,38 @@ public void FileIsWrittenIfNonexistent()
Assert.Contains("Hello, world!", lines[0]);
}
}
+ [Fact]
+ public void FileIsReWrittenAfterEventIfDeleted()
+ {
+ using (var tmp = TempFolder.ForCaller())
+ {
+ var nonexistent = tmp.AllocateFilename("txt");
+ var evt = Some.LogEvent("Hello, world!");
+
+ void Emmit()
+ {
+ using (var sink = new SharedFileSink(nonexistent, new JsonFormatter(), null))
+ {
+ sink.Emit(evt);
+ }
+ }
+
+ Emmit();
+ var lines = System.IO.File.ReadAllLines(nonexistent);
+ Assert.Contains("Hello, world!", lines[0]);
+ Assert.Single(lines);
+
+ System.IO.File.Delete(nonexistent);
+ Assert.False(System.IO.File.Exists(nonexistent));
+ Assert.Throws(() => System.IO.File.ReadAllLines(nonexistent));
+
+ Emmit();
+ lines = System.IO.File.ReadAllLines(nonexistent);
+ Assert.True(System.IO.File.Exists(nonexistent));
+ Assert.Contains("Hello, world!", lines[0]);
+ Assert.Single(lines);
+ }
+ }
[Fact]
public void FileIsAppendedToWhenAlreadyCreated()