Skip to content

Commit fec05dc

Browse files
committed
Fixed issue with reading the log file when deleted
1 parent 07e5b78 commit fec05dc

2 files changed

Lines changed: 58 additions & 15 deletions

File tree

src/EventLogExpert.UI.Tests/Services/DebugLogServiceTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,41 @@ public async Task LoadAsync_WhenLogFileDoesNotExist_ShouldReturnNoLines()
258258
Assert.Empty(lines);
259259
}
260260

261+
[Fact]
262+
public async Task LoadAsync_WhenLogFileDeletedDuringRead_ShouldAllowDeletion()
263+
{
264+
// Arrange
265+
var fileLocationOptions = new FileLocationOptions(_testDirectory);
266+
var mockSettingsService = CreateMockSettingsService(LogLevel.Information);
267+
268+
var expectedLines = new[] { Constants.DebugLogLine1, Constants.DebugLogLine2, Constants.DebugLogLine3 };
269+
await File.WriteAllLinesAsync(_testLogPath, expectedLines, TestContext.Current.CancellationToken);
270+
271+
using var debugLogService = new DebugLogService(fileLocationOptions, mockSettingsService);
272+
273+
// Act - Start enumeration and delete the file while the reader holds a handle
274+
await using var enumerator = debugLogService.LoadAsync().GetAsyncEnumerator(TestContext.Current.CancellationToken);
275+
276+
// Read first line to ensure the file is open
277+
Assert.True(await enumerator.MoveNextAsync());
278+
Assert.Equal(Constants.DebugLogLine1, enumerator.Current);
279+
280+
// Delete should succeed because FileShare.Delete is set
281+
var deleteException = Record.Exception(() => File.Delete(_testLogPath));
282+
283+
// Assert - Deletion should not throw
284+
Assert.Null(deleteException);
285+
286+
// Continue reading remaining lines (file content is still accessible via open handle)
287+
Assert.True(await enumerator.MoveNextAsync());
288+
Assert.Equal(Constants.DebugLogLine2, enumerator.Current);
289+
290+
Assert.True(await enumerator.MoveNextAsync());
291+
Assert.Equal(Constants.DebugLogLine3, enumerator.Current);
292+
293+
Assert.False(await enumerator.MoveNextAsync());
294+
}
295+
261296
[Fact]
262297
public void LoadDebugLog_WhenNoSubscribers_ShouldNotThrow()
263298
{

src/EventLogExpert.UI/Services/DebugLogService.cs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,32 +64,40 @@ public void Dispose()
6464

6565
public async IAsyncEnumerable<string> LoadAsync()
6666
{
67-
// If the log file doesn't exist yet (fresh install, or before first write),
68-
// yield no lines instead of throwing FileNotFoundException.
69-
if (!File.Exists(_fileLocationOptions.LoggingPath))
70-
{
71-
yield break;
72-
}
73-
7467
// Read directly from the source file. Writers use File.AppendText which opens with
75-
// FileShare.Read, allowing concurrent readers. We open with FileShare.ReadWrite to
76-
// allow concurrent writers. This avoids the overhead of copying to a temp file and
77-
// eliminates lock contention between readers and writers.
68+
// FileShare.Read, allowing concurrent readers. We open with FileShare.ReadWrite | Delete
69+
// to allow concurrent writers and log rotation/deletion (e.g., InitTracing deletes oversized logs).
70+
// This avoids the overhead of copying to a temp file and eliminates lock contention.
7871
var options = new FileStreamOptions
7972
{
8073
Mode = FileMode.Open,
8174
Access = FileAccess.Read,
82-
Share = FileShare.ReadWrite,
75+
Share = FileShare.ReadWrite | FileShare.Delete,
8376
Options = FileOptions.Asynchronous | FileOptions.SequentialScan,
8477
BufferSize = 4096
8578
};
8679

87-
await using var stream = new FileStream(_fileLocationOptions.LoggingPath, options);
88-
using var reader = new StreamReader(stream);
80+
FileStream stream;
81+
82+
try
83+
{
84+
stream = new FileStream(_fileLocationOptions.LoggingPath, options);
85+
}
86+
catch (Exception ex) when (ex is FileNotFoundException or DirectoryNotFoundException)
87+
{
88+
// The log file doesn't exist yet (fresh install, before first write),
89+
// or was deleted between check and open (e.g., InitTracing deletes oversized logs).
90+
yield break;
91+
}
8992

90-
while (await reader.ReadLineAsync() is { } line)
93+
await using (stream)
9194
{
92-
yield return line;
95+
using var reader = new StreamReader(stream);
96+
97+
while (await reader.ReadLineAsync() is { } line)
98+
{
99+
yield return line;
100+
}
93101
}
94102
}
95103

0 commit comments

Comments
 (0)