Adding telemetry publish for unhandled exceptions to developer exception page and exception handler #180
This commit is contained in:
parent
3657a1a14b
commit
7b9cfac65a
|
|
@ -5,6 +5,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Diagnostics.Tracing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -29,6 +30,7 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
private static readonly bool IsMono = Type.GetType("Mono.Runtime") != null;
|
private static readonly bool IsMono = Type.GetType("Mono.Runtime") != null;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IFileProvider _fileProvider;
|
private readonly IFileProvider _fileProvider;
|
||||||
|
private readonly TelemetrySource _telemetrySource;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DeveloperExceptionPageMiddleware"/> class
|
/// Initializes a new instance of the <see cref="DeveloperExceptionPageMiddleware"/> class
|
||||||
|
|
@ -39,7 +41,8 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
RequestDelegate next,
|
RequestDelegate next,
|
||||||
ErrorPageOptions options,
|
ErrorPageOptions options,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
IApplicationEnvironment appEnvironment)
|
IApplicationEnvironment appEnvironment,
|
||||||
|
TelemetrySource telemetrySource)
|
||||||
{
|
{
|
||||||
if (next == null)
|
if (next == null)
|
||||||
{
|
{
|
||||||
|
|
@ -55,6 +58,7 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
_options = options;
|
_options = options;
|
||||||
_logger = loggerFactory.CreateLogger<DeveloperExceptionPageMiddleware>();
|
_logger = loggerFactory.CreateLogger<DeveloperExceptionPageMiddleware>();
|
||||||
_fileProvider = options.FileProvider ?? new PhysicalFileProvider(appEnvironment.ApplicationBasePath);
|
_fileProvider = options.FileProvider ?? new PhysicalFileProvider(appEnvironment.ApplicationBasePath);
|
||||||
|
_telemetrySource = telemetrySource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -72,6 +76,7 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError("An unhandled exception has occurred while executing the request", ex);
|
_logger.LogError("An unhandled exception has occurred while executing the request", ex);
|
||||||
|
|
||||||
if (context.Response.HasStarted)
|
if (context.Response.HasStarted)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("The response has already started, the error page middleware will not be executed.");
|
_logger.LogWarning("The response has already started, the error page middleware will not be executed.");
|
||||||
|
|
@ -84,6 +89,9 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
context.Response.StatusCode = 500;
|
context.Response.StatusCode = 500;
|
||||||
|
|
||||||
await DisplayException(context, ex);
|
await DisplayException(context, ex);
|
||||||
|
|
||||||
|
_telemetrySource.WriteTelemetry("Microsoft.AspNet.Diagnostics.UnhandledException", new { httpContext = context, exception = ex });
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception ex2)
|
catch (Exception ex2)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.Tracing;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
|
@ -17,8 +18,13 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
private readonly ExceptionHandlerOptions _options;
|
private readonly ExceptionHandlerOptions _options;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly Func<object, Task> _clearCacheHeadersDelegate;
|
private readonly Func<object, Task> _clearCacheHeadersDelegate;
|
||||||
|
private readonly TelemetrySource _telemetrySource;
|
||||||
|
|
||||||
public ExceptionHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, ExceptionHandlerOptions options)
|
public ExceptionHandlerMiddleware(
|
||||||
|
RequestDelegate next,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
|
ExceptionHandlerOptions options,
|
||||||
|
TelemetrySource telemetrySource)
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_options = options;
|
_options = options;
|
||||||
|
|
@ -28,6 +34,7 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
_options.ExceptionHandler = _next;
|
_options.ExceptionHandler = _next;
|
||||||
}
|
}
|
||||||
_clearCacheHeadersDelegate = ClearCacheHeaders;
|
_clearCacheHeadersDelegate = ClearCacheHeaders;
|
||||||
|
_telemetrySource = telemetrySource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(HttpContext context)
|
||||||
|
|
@ -63,6 +70,9 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response);
|
context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response);
|
||||||
|
|
||||||
await _options.ExceptionHandler(context);
|
await _options.ExceptionHandler(context);
|
||||||
|
|
||||||
|
_telemetrySource.WriteTelemetry("Microsoft.AspNet.Diagnostics.HandledException", new { httpContext = context, exception = ex });
|
||||||
|
|
||||||
// TODO: Optional re-throw? We'll re-throw the original exception by default if the error handler throws.
|
// TODO: Optional re-throw? We'll re-throw the original exception by default if the error handler throws.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,15 @@
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "1.0.0-*",
|
"Microsoft.Extensions.Logging.Abstractions": "1.0.0-*",
|
||||||
"Microsoft.Extensions.OptionsModel": "1.0.0-*",
|
"Microsoft.Extensions.OptionsModel": "1.0.0-*",
|
||||||
"Microsoft.Dnx.Compilation.Abstractions": "1.0.0-*",
|
"Microsoft.Dnx.Compilation.Abstractions": "1.0.0-*",
|
||||||
"Microsoft.Extensions.WebEncoders.Core": "1.0.0-*"
|
"Microsoft.Extensions.WebEncoders.Core": "1.0.0-*",
|
||||||
|
"System.Diagnostics.Tracing.Telemetry": "4.0.0-beta-*"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"dnx451": {},
|
"dnx451": {
|
||||||
|
"frameworkAssemblies": {
|
||||||
|
"System.Runtime": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"dnxcore50": {
|
"dnxcore50": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"System.Reflection.Extensions": "4.0.1-beta-*"
|
"System.Reflection.Extensions": "4.0.1-beta-*"
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,20 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Tracing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Diagnostics.Views;
|
using Microsoft.AspNet.Diagnostics.Views;
|
||||||
using Microsoft.AspNet.FileProviders;
|
using Microsoft.AspNet.FileProviders;
|
||||||
|
using Microsoft.AspNet.TestHost;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
using Microsoft.Dnx.Runtime;
|
using Microsoft.Dnx.Runtime;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -298,7 +302,8 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
(httpContext) => { return Task.FromResult(0); },
|
(httpContext) => { return Task.FromResult(0); },
|
||||||
errorPageOptions,
|
errorPageOptions,
|
||||||
new LoggerFactory(),
|
new LoggerFactory(),
|
||||||
new TestApplicationEnvironment());
|
new TestApplicationEnvironment(),
|
||||||
|
new TelemetryListener("Microsoft.Aspnet"));
|
||||||
|
|
||||||
return middleware;
|
return middleware;
|
||||||
}
|
}
|
||||||
|
|
@ -471,5 +476,35 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
public IEnumerable<string> ExpectedErrorCode { get; set; }
|
public IEnumerable<string> ExpectedErrorCode { get; set; }
|
||||||
public IEnumerable<string> ExpectedPostErrorCode { get; set; }
|
public IEnumerable<string> ExpectedPostErrorCode { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UnhandledErrorsWriteToDiagnosticTelemetryWhenUsingExceptionPage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
TelemetryListener telemetryListener = null;
|
||||||
|
var server = TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
telemetryListener = app.ApplicationServices.GetRequiredService<TelemetryListener>();
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
throw new Exception("Test exception");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var listener = new TestTelemetryListener();
|
||||||
|
telemetryListener.SubscribeWithAdapter(listener);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await server.CreateClient().GetAsync("/path");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(listener.EndRequest?.HttpContext);
|
||||||
|
Assert.Null(listener.HostingUnhandledException?.HttpContext);
|
||||||
|
Assert.Null(listener.HostingUnhandledException?.Exception);
|
||||||
|
Assert.NotNull(listener.DiagnosticUnhandledException?.HttpContext);
|
||||||
|
Assert.NotNull(listener.DiagnosticUnhandledException?.Exception);
|
||||||
|
Assert.Null(listener.DiagnosticHandledException?.HttpContext);
|
||||||
|
Assert.Null(listener.DiagnosticHandledException?.Exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Tracing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
@ -10,6 +11,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.TestHost;
|
using Microsoft.AspNet.TestHost;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Diagnostics
|
namespace Microsoft.AspNet.Diagnostics
|
||||||
|
|
@ -321,5 +323,45 @@ namespace Microsoft.AspNet.Diagnostics
|
||||||
Assert.Equal("abcdef", values.First());
|
Assert.Equal("abcdef", values.First());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HandledErrorsWriteToDiagnosticTelemetryWhenUsingExceptionHandler()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
TelemetryListener telemetryListener = null;
|
||||||
|
|
||||||
|
var server = TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
telemetryListener = app.ApplicationServices.GetRequiredService<TelemetryListener>();
|
||||||
|
|
||||||
|
app.UseExceptionHandler("/handle-errors");
|
||||||
|
app.Map("/handle-errors", (innerAppBuilder) =>
|
||||||
|
{
|
||||||
|
innerAppBuilder.Run(async (httpContext) =>
|
||||||
|
{
|
||||||
|
await httpContext.Response.WriteAsync("Handled error in a custom way.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
throw new Exception("Test exception");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var listener = new TestTelemetryListener();
|
||||||
|
telemetryListener.SubscribeWithAdapter(listener);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await server.CreateClient().GetAsync(string.Empty);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(listener.EndRequest?.HttpContext);
|
||||||
|
Assert.Null(listener.HostingUnhandledException?.HttpContext);
|
||||||
|
Assert.Null(listener.HostingUnhandledException?.Exception);
|
||||||
|
Assert.Null(listener.DiagnosticUnhandledException?.HttpContext);
|
||||||
|
Assert.Null(listener.DiagnosticUnhandledException?.Exception);
|
||||||
|
Assert.NotNull(listener.DiagnosticHandledException?.HttpContext);
|
||||||
|
Assert.NotNull(listener.DiagnosticHandledException?.Exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.TelemetryAdapter;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Diagnostics
|
||||||
|
{
|
||||||
|
public class TestTelemetryListener
|
||||||
|
{
|
||||||
|
public class OnRequestEventData
|
||||||
|
{
|
||||||
|
public IProxyHttpContext HttpContext { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OnExceptionEventData
|
||||||
|
{
|
||||||
|
public IProxyHttpContext HttpContext { get; set; }
|
||||||
|
public IProxyException Exception { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public OnRequestEventData BeginRequest { get; set; }
|
||||||
|
public OnRequestEventData EndRequest { get; set; }
|
||||||
|
public OnExceptionEventData HostingUnhandledException { get; set; }
|
||||||
|
public OnExceptionEventData DiagnosticUnhandledException { get; set; }
|
||||||
|
public OnExceptionEventData DiagnosticHandledException { get; set; }
|
||||||
|
|
||||||
|
[TelemetryName("Microsoft.AspNet.Hosting.BeginRequest")]
|
||||||
|
public virtual void OnBeginRequest(IProxyHttpContext httpContext)
|
||||||
|
{
|
||||||
|
BeginRequest = new OnRequestEventData()
|
||||||
|
{
|
||||||
|
HttpContext = httpContext
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[TelemetryName("Microsoft.AspNet.Hosting.EndRequest")]
|
||||||
|
public virtual void OnEndRequest(IProxyHttpContext httpContext)
|
||||||
|
{
|
||||||
|
EndRequest = new OnRequestEventData()
|
||||||
|
{
|
||||||
|
HttpContext = httpContext
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[TelemetryName("Microsoft.AspNet.Hosting.UnhandledException")]
|
||||||
|
public virtual void OnHostingUnhandledException(IProxyHttpContext httpContext, IProxyException exception)
|
||||||
|
{
|
||||||
|
HostingUnhandledException = new OnExceptionEventData()
|
||||||
|
{
|
||||||
|
HttpContext = httpContext,
|
||||||
|
Exception = exception
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[TelemetryName("Microsoft.AspNet.Diagnostics.UnhandledException")]
|
||||||
|
public virtual void OnDiagnosticUnhandledException(IProxyHttpContext httpContext, IProxyException exception)
|
||||||
|
{
|
||||||
|
DiagnosticUnhandledException = new OnExceptionEventData()
|
||||||
|
{
|
||||||
|
HttpContext = httpContext,
|
||||||
|
Exception = exception
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[TelemetryName("Microsoft.AspNet.Diagnostics.HandledException")]
|
||||||
|
public virtual void OnDiagnosticHandledException(IProxyHttpContext httpContext, IProxyException exception)
|
||||||
|
{
|
||||||
|
DiagnosticHandledException = new OnExceptionEventData()
|
||||||
|
{
|
||||||
|
HttpContext = httpContext,
|
||||||
|
Exception = exception
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IProxyHttpContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IProxyException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Testing": "1.0.0-*",
|
"Microsoft.AspNet.Testing": "1.0.0-*",
|
||||||
"Microsoft.Extensions.DependencyInjection": "1.0.0-*",
|
"Microsoft.Extensions.DependencyInjection": "1.0.0-*",
|
||||||
|
"Microsoft.Extensions.TelemetryAdapter": "1.0.0-*",
|
||||||
"xunit.runner.aspnet": "2.0.0-aspnet-*"
|
"xunit.runner.aspnet": "2.0.0-aspnet-*"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue