Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix issue with FileSink not closing the stream on hook errors
  • Loading branch information
Falco20019 committed Feb 20, 2025
commit a3cd9301c8e5086b9f63d946a0d09347b65ab0e4
12 changes: 10 additions & 2 deletions src/Serilog.Sinks.File/Sinks/File/FileSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,16 @@ internal FileSink(

if (hooks != null)
{
outputStream = hooks.OnFileOpened(path, outputStream, encoding) ??
throw new InvalidOperationException($"The file lifecycle hook `{nameof(FileLifecycleHooks.OnFileOpened)}(...)` returned `null`.");
try
{
outputStream = hooks.OnFileOpened(path, outputStream, encoding) ??
throw new InvalidOperationException($"The file lifecycle hook `{nameof(FileLifecycleHooks.OnFileOpened)}(...)` returned `null`.");
}
catch
{
outputStream?.Dispose();
throw;
}
}

_output = new StreamWriter(outputStream, encoding);
Expand Down
61 changes: 24 additions & 37 deletions test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Serilog.Sinks.File.Tests.Support;
using Serilog.Configuration;
using Serilog.Core;
using Serilog.Debugging;
using Xunit.Abstractions;

namespace Serilog.Sinks.File.Tests;
Expand Down Expand Up @@ -196,85 +197,71 @@ public void WhenFirstOpeningFailedWithLockRetryDelayed30Minutes()
{
var fileName = Some.String() + ".txt";
using var temp = new TempFolder();
using var log = new LoggerConfiguration()
.WriteTo.File(Path.Combine(temp.Path, fileName), rollOnFileSizeLimit: true, fileSizeLimitBytes: 1, rollingInterval: RollingInterval.Hour, hooks: new FailOpeningHook(true, 2, 3, 4))
.CreateLogger();
LogEvent e1 = Some.InformationEvent(new DateTime(2012, 10, 28)),
e2 = Some.InformationEvent(e1.Timestamp.AddSeconds(1)),
e3 = Some.InformationEvent(e1.Timestamp.AddMinutes(5)),
e4 = Some.InformationEvent(e1.Timestamp.AddMinutes(31));
LogEvent[] logEvents = new[] { e1, e2, e3, e4 };

using (var log = new LoggerConfiguration()
.WriteTo.File(Path.Combine(temp.Path, fileName), rollOnFileSizeLimit: true, fileSizeLimitBytes: 1,
rollingInterval: RollingInterval.Hour, hooks: new FailOpeningHook(true, 2, 3, 4))
.CreateLogger())

SelfLog.Enable(_testOutputHelper.WriteLine);
foreach (var logEvent in logEvents)
{
foreach (var logEvent in logEvents)
{
Clock.SetTestDateTimeNow(logEvent.Timestamp.DateTime);
log.Write(logEvent);
}
Clock.SetTestDateTimeNow(logEvent.Timestamp.DateTime);
log.Write(logEvent);
}

var files = Directory.GetFiles(temp.Path)
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)
.ToArray();
var pattern = "yyyyMMddHH";

foreach (var file in files)
{
_testOutputHelper.WriteLine(file + ": " + System.IO.File.ReadAllText(file));
}
Assert.Equal(5, files.Length);
Assert.Equal(4, files.Length);
// Successful write of e1:
Assert.True(files[0].EndsWith(ExpectedFileName(fileName, e1.Timestamp, pattern)), files[0]);
// Failing writes for e2, will be dropped and logged to SelfLog; on lock it will try it three times:
Assert.True(files[1].EndsWith("_001.txt"), files[1]);
Assert.True(files[2].EndsWith("_002.txt"), files[2]);
Assert.True(files[3].EndsWith("_003.txt"), files[3]);
/* e3 will be dropped and logged to SelfLog without new file as it's in the 30 minutes cooldown and roller only starts on next hour! */
// Successful write of e4:
Assert.True(files[4].EndsWith("_004.txt"), files[4]);
// Successful write of e4, the third file will be retried after failing initially:
Assert.True(files[3].EndsWith("_003.txt"), files[3]);
}

[Fact]
public void WhenFirstOpeningFailedWithoutLockRetryDelayed30Minutes()
{
var fileName = Some.String() + ".txt";
using var temp = new TempFolder();
using var log = new LoggerConfiguration()
.WriteTo.File(Path.Combine(temp.Path, fileName), rollOnFileSizeLimit: true, fileSizeLimitBytes: 1, rollingInterval: RollingInterval.Hour, hooks: new FailOpeningHook(false, 2))
.CreateLogger();
LogEvent e1 = Some.InformationEvent(new DateTime(2012, 10, 28)),
e2 = Some.InformationEvent(e1.Timestamp.AddSeconds(1)),
e3 = Some.InformationEvent(e1.Timestamp.AddMinutes(5)),
e4 = Some.InformationEvent(e1.Timestamp.AddMinutes(31));
LogEvent[] logEvents = new[] { e1, e2, e3, e4 };

using (var log = new LoggerConfiguration()
.WriteTo.File(Path.Combine(temp.Path, fileName), rollOnFileSizeLimit: true, fileSizeLimitBytes: 1,
rollingInterval: RollingInterval.Hour, hooks: new FailOpeningHook(false, 2))
.CreateLogger())

SelfLog.Enable(_testOutputHelper.WriteLine);
foreach (var logEvent in logEvents)
{
foreach (var logEvent in logEvents)
{
Clock.SetTestDateTimeNow(logEvent.Timestamp.DateTime);
log.Write(logEvent);
}
Clock.SetTestDateTimeNow(logEvent.Timestamp.DateTime);
log.Write(logEvent);
}

var files = Directory.GetFiles(temp.Path)
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)
.ToArray();
var pattern = "yyyyMMddHH";

foreach (var file in files)
{
_testOutputHelper.WriteLine(file + ": " + System.IO.File.ReadAllText(file));
}
Assert.Equal(3, files.Length);
Assert.Equal(2, files.Length);
// Successful write of e1:
Assert.True(files[0].EndsWith(ExpectedFileName(fileName, e1.Timestamp, pattern)), files[0]);
// Failing writes for e2, will be dropped and logged to SelfLog; on non-lock it will try it once:
Assert.True(files[1].EndsWith("_001.txt"), files[1]);
/* Failing writes for e2, will be dropped and logged to SelfLog; on non-lock it will try it once */
/* e3 will be dropped and logged to SelfLog without new file as it's in the 30 minutes cooldown and roller only starts on next hour! */
// Successful write of e4:
Assert.True(files[2].EndsWith("_002.txt"), files[2]);
// Successful write of e4, the file will be retried after failing initially:
Assert.True(files[1].EndsWith("_001.txt"), files[1]);
}

[Fact]
Expand Down
6 changes: 3 additions & 3 deletions test/Serilog.Sinks.File.Tests/Support/FailOpeningHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ public FailOpeningHook(bool asFileLocked, params int[] failingInstances)
_failingInstances = failingInstances;
}

public override Stream OnFileOpened(string path, Stream _, Encoding __)
public override Stream OnFileOpened(string path, Stream stream, Encoding encoding)
{
TimesOpened++;
if (_failingInstances.Contains(TimesOpened))
{
var message = $"We failed on try {TimesOpened}, the file was locked: {_asFileLocked}";

throw _asFileLocked
? new IOException(message)
: new Exception(message);
}

return base.OnFileOpened(path, _, __);
return base.OnFileOpened(path, stream, encoding);
}
}