Skip to content

Commit 70c62d7

Browse files
committed
Ensure all app level unhandled exceptions are logged
1 parent 40890ab commit 70c62d7

File tree

4 files changed

+145
-34
lines changed

4 files changed

+145
-34
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.AspNetCore.Http.Features;
8+
using Microsoft.Azure.WebJobs.Host;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Middleware
12+
{
13+
internal class ExceptionMiddleware
14+
{
15+
private readonly ILogger _logger;
16+
private readonly RequestDelegate _next;
17+
18+
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
19+
{
20+
_logger = logger;
21+
_next = next;
22+
}
23+
24+
public async Task Invoke(HttpContext context)
25+
{
26+
try
27+
{
28+
await _next.Invoke(context);
29+
}
30+
catch (Exception ex)
31+
{
32+
var responseFeature = context.Features.Get<IHttpResponseFeature>();
33+
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
34+
35+
if (ex is HttpException httpException)
36+
{
37+
context.Response.StatusCode = httpException.StatusCode;
38+
}
39+
40+
if (!(ex is FunctionInvocationException))
41+
{
42+
// exceptions throw by function code are handled/logged elsewhere
43+
// our goal here is to log exceptions coming from our own runtime
44+
_logger.LogError(ex, "An unhandled host error has occurred.");
45+
}
46+
}
47+
}
48+
}
49+
}

src/WebJobs.Script.WebHost/Middleware/HttpExceptionMiddleware.cs

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/WebJobs.Script.WebHost/WebJobsApplicationBuilderExtension.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public static IApplicationBuilder UseWebJobsScriptHost(this IApplicationBuilder
4747
builder.UseMiddleware<AppServiceHeaderFixupMiddleware>();
4848
}
4949

50-
builder.UseMiddleware<HttpExceptionMiddleware>();
50+
builder.UseMiddleware<ExceptionMiddleware>();
5151
builder.UseMiddleware<ResponseBufferingMiddleware>();
5252
builder.UseMiddleware<HomepageMiddleware>();
5353
builder.UseWhen(context => !context.Request.IsAdminRequest(), config =>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Http;
8+
using Microsoft.Azure.WebJobs.Host;
9+
using Microsoft.Azure.WebJobs.Script.WebHost;
10+
using Microsoft.Azure.WebJobs.Script.WebHost.Middleware;
11+
using Microsoft.Extensions.DependencyInjection;
12+
using Microsoft.Extensions.Hosting;
13+
using Microsoft.Extensions.Logging;
14+
using Microsoft.WebJobs.Script.Tests;
15+
using Xunit;
16+
17+
namespace Microsoft.Azure.WebJobs.Script.Tests.Middleware
18+
{
19+
public class ExceptionMiddlewareTests
20+
{
21+
private readonly ILogger<ExceptionMiddleware> _logger;
22+
private readonly TestLoggerProvider _loggerProvider;
23+
24+
public ExceptionMiddlewareTests()
25+
{
26+
_loggerProvider = new TestLoggerProvider();
27+
IHost host = new HostBuilder()
28+
.ConfigureDefaultTestWebScriptHost()
29+
.ConfigureLogging(l =>
30+
{
31+
l.AddProvider(_loggerProvider);
32+
})
33+
.Build();
34+
35+
_logger = host.Services.GetService<ILogger<ExceptionMiddleware>>();
36+
}
37+
38+
[Fact]
39+
public async Task Invoke_HandlesHttpExceptions()
40+
{
41+
var ex = new HttpException(StatusCodes.Status502BadGateway);
42+
RequestDelegate next = (ctxt) =>
43+
{
44+
throw ex;
45+
};
46+
var middleware = new ExceptionMiddleware(next, _logger);
47+
48+
var context = new DefaultHttpContext();
49+
await middleware.Invoke(context);
50+
51+
Assert.Equal(ex.StatusCode, context.Response.StatusCode);
52+
53+
var log = _loggerProvider.GetAllLogMessages().Single();
54+
Assert.Equal("An unhandled host error has occurred.", log.FormattedMessage);
55+
Assert.Same(ex, log.Exception);
56+
}
57+
58+
[Fact]
59+
public async Task Invoke_HandlesNonHttpExceptions()
60+
{
61+
var ex = new Exception("Kaboom!");
62+
RequestDelegate next = (ctxt) =>
63+
{
64+
throw ex;
65+
};
66+
var middleware = new ExceptionMiddleware(next, _logger);
67+
68+
var context = new DefaultHttpContext();
69+
await middleware.Invoke(context);
70+
71+
Assert.Equal(StatusCodes.Status500InternalServerError, context.Response.StatusCode);
72+
73+
var log = _loggerProvider.GetAllLogMessages().Single();
74+
Assert.Equal("An unhandled host error has occurred.", log.FormattedMessage);
75+
Assert.Same(ex, log.Exception);
76+
}
77+
78+
[Fact]
79+
public async Task Invoke_HandlesFunctionInvocationExceptions()
80+
{
81+
var ex = new FunctionInvocationException("Kaboom!");
82+
RequestDelegate next = (ctxt) =>
83+
{
84+
throw ex;
85+
};
86+
var middleware = new ExceptionMiddleware(next, _logger);
87+
88+
var context = new DefaultHttpContext();
89+
await middleware.Invoke(context);
90+
91+
Assert.Equal(StatusCodes.Status500InternalServerError, context.Response.StatusCode);
92+
Assert.Empty(_loggerProvider.GetAllLogMessages());
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)