Skip to content

Commit 19171d6

Browse files
committed
handling exceptions from FileWatcherEventSource
1 parent 482deff commit 19171d6

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

src/WebJobs.Script/Eventing/File/FileWatcherEventSource.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public sealed class FileWatcherEventSource : IDisposable
1313
private readonly AutoRecoveringFileSystemWatcher _fileWatcher;
1414
private readonly IScriptEventManager _eventManager;
1515
private readonly string _source;
16+
private readonly TraceWriter _traceWriter;
1617
private bool _disposed = false;
1718

1819
public FileWatcherEventSource(IScriptEventManager eventManager,
@@ -27,12 +28,22 @@ public FileWatcherEventSource(IScriptEventManager eventManager,
2728
_eventManager = eventManager;
2829
_fileWatcher = new AutoRecoveringFileSystemWatcher(path, filter, includeSubdirectories, changeTypes, traceWriter);
2930
_fileWatcher.Changed += FileChanged;
31+
_traceWriter = traceWriter;
3032
}
3133

3234
private void FileChanged(object sender, FileSystemEventArgs e)
3335
{
34-
var fileEvent = new FileEvent(_source, e);
35-
_eventManager.Publish(fileEvent);
36+
// This handler is called on a background thread, so any exceptions thrown will crash
37+
// the process. Handle and log all errors instead.
38+
try
39+
{
40+
var fileEvent = new FileEvent(_source, e);
41+
_eventManager.Publish(fileEvent);
42+
}
43+
catch (Exception ex)
44+
{
45+
_traceWriter?.Error($"Error handling '{e.ChangeType}' notification for '{e.FullPath}'.", ex);
46+
}
3647
}
3748

3849
private void Dispose(bool disposing)

test/WebJobs.Script.Tests/Eventing/File/FileWatcherEventSourceTests.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using System.IO;
78
using System.Linq;
89
using System.Reactive.Linq;
9-
using System.Text;
1010
using System.Threading.Tasks;
1111
using Microsoft.Azure.WebJobs.Script.Eventing;
1212
using Microsoft.Azure.WebJobs.Script.Eventing.File;
@@ -66,5 +66,29 @@ public async Task DisposedSource_DoesNotPublishEvents()
6666
Assert.Equal(2, events.Count);
6767
}
6868
}
69+
70+
[Fact]
71+
public async Task FileChangedHandlerExceptions_LogError_AndDoNotThrow()
72+
{
73+
// Note: The FileSystemWatcher handler is called on a background thread. This means that when the handler
74+
// throws an exception, it crashes the process. The Visual Studio test runner handles that scenario and
75+
// does not crash. Before this handler was fixed, this test would only fail in the console runner.
76+
77+
TestTraceWriter traceWriter = new TestTraceWriter(TraceLevel.Verbose);
78+
using (var directory = new TempDirectory())
79+
using (var eventManager = new ScriptEventManager())
80+
using (var eventSource = new FileWatcherEventSource(eventManager, "TestSource", directory.Path, traceWriter: traceWriter))
81+
{
82+
var expectedException = new InvalidOperationException("This should not crash the process!");
83+
eventManager.Subscribe(p => throw expectedException);
84+
85+
string fullPath = Path.Combine(directory.Path, "test.txt");
86+
File.WriteAllText(fullPath, "Test");
87+
88+
await TestHelpers.Await(
89+
() => traceWriter.GetTraces().Any(p => p.Level == TraceLevel.Error && p.Message.Contains(fullPath) && p.Exception == expectedException),
90+
timeout: 2000, pollingInterval: 250);
91+
}
92+
}
6993
}
7094
}

0 commit comments

Comments
 (0)