Skip to content
This repository was archived by the owner on Jul 5, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Changelog

## Version 2.6.0-beta3
- [Implement unhandled exception auto-tracking (500 requests) for MVC 5 and WebAPI 2 applications.](https://github.com/Microsoft/ApplicationInsights-dotnet-server/pull/847)

## Version 2.6.0-beta2
- [Added a max length restriction to values passed in through requests.](https://github.com/Microsoft/ApplicationInsights-dotnet-server/pull/810)
Expand Down
1 change: 0 additions & 1 deletion Src/Common/StringUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public static class StringUtilities
/// </summary>
public static string EnforceMaxLength(string input, int maxLength)
{
Debug.Assert(input != null, $"{nameof(input)} must not be null");
Debug.Assert(maxLength > 0, $"{nameof(maxLength)} must be greater than 0");

if (input != null && input.Length > maxLength)
Expand Down
155 changes: 155 additions & 0 deletions Src/Web/Web.Net45.Tests/MvcExceptionHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
namespace Microsoft.ApplicationInsights.Web
{
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Web.Mvc;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Web.Helpers;
using Microsoft.ApplicationInsights.Web.TestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class MvcExceptionHandlerTests
{
private ConcurrentQueue<ExceptionTelemetry> sentTelemetry;
private TelemetryConfiguration configuration;

[TestInitialize]
public void TestInit()
{
GlobalFilters.Filters.Clear();
this.sentTelemetry = new ConcurrentQueue<ExceptionTelemetry>();

var stubTelemetryChannel = new StubTelemetryChannel
{
OnSend = t =>
{
if (t is ExceptionTelemetry telemetry)
{
this.sentTelemetry.Enqueue(telemetry);
}
}
};

this.configuration = new TelemetryConfiguration
{
InstrumentationKey = Guid.NewGuid().ToString(),
TelemetryChannel = stubTelemetryChannel
};
}

[TestCleanup]
public void Cleanup()
{
while (this.sentTelemetry.TryDequeue(out var _))
{
}

GlobalFilters.Filters.Clear();
}

[TestMethod]
public void MvcExceptionFilterIsInjectedAndTracksException()
{
using (var exceptionModule = new ExceptionTrackingTelemetryModule())
{
exceptionModule.Initialize(this.configuration);

var mvcExceptionFilters = GlobalFilters.Filters;
Assert.AreEqual(1, mvcExceptionFilters.Count);

var handleExceptionFilter = (HandleErrorAttribute)mvcExceptionFilters.Single().Instance;
Assert.IsNotNull(handleExceptionFilter);

var exception = new Exception("test");
var controllerCtx = HttpModuleHelper.GetFakeControllerContext(isCustomErrorEnabled: true);
handleExceptionFilter.OnException(new ExceptionContext(controllerCtx, exception));

Assert.AreEqual(1, this.sentTelemetry.Count);

var trackedException = this.sentTelemetry.Single();
Assert.IsNotNull(trackedException);
Assert.AreEqual(exception, trackedException.Exception);
}
}

[TestMethod]
public void MvcExceptionFilterIsNotInjectedIsInjectionIsDisabled()
{
using (var exceptionModule = new ExceptionTrackingTelemetryModule())
{
exceptionModule.EnableMvcAndWebApiExceptionAutoTracking = false;
exceptionModule.Initialize(this.configuration);

Assert.IsFalse(GlobalFilters.Filters.Any());
}
}

[TestMethod]
public void MvcExceptionLoggerIsNotInjectedIfAnotherInjectionDetected()
{
GlobalFilters.Filters.Add(new MvcAutoInjectedFilter());
Assert.AreEqual(1, GlobalFilters.Filters.Count);

using (var exceptionModule = new ExceptionTrackingTelemetryModule())
{
exceptionModule.Initialize(this.configuration);

var filters = GlobalFilters.Filters;
Assert.AreEqual(1, filters.Count);
Assert.IsInstanceOfType(filters.Single().Instance, typeof(MvcAutoInjectedFilter));
}
}

[TestMethod]
public void MvcExceptionFilterNoopIfCustomErrorsIsFalse()
{
using (var exceptionModule = new ExceptionTrackingTelemetryModule())
{
exceptionModule.Initialize(this.configuration);

var mvcExceptionFilters = GlobalFilters.Filters;
Assert.AreEqual(1, mvcExceptionFilters.Count);

var handleExceptionFilter = (HandleErrorAttribute)mvcExceptionFilters.Single().Instance;
Assert.IsNotNull(handleExceptionFilter);

var exception = new Exception("test");
var controllerCtx = HttpModuleHelper.GetFakeControllerContext(isCustomErrorEnabled: false);
handleExceptionFilter.OnException(new ExceptionContext(controllerCtx, exception));

Assert.IsFalse(this.sentTelemetry.Any());
}
}

[TestMethod]
public void MvcExceptionFilterNoopIfExceptionIsNull()
{
using (var exceptionModule = new ExceptionTrackingTelemetryModule())
{
exceptionModule.Initialize(this.configuration);

var mvcExceptionFilters = GlobalFilters.Filters;
Assert.AreEqual(1, mvcExceptionFilters.Count);

var handleExceptionFilter = (HandleErrorAttribute)mvcExceptionFilters.Single().Instance;
Assert.IsNotNull(handleExceptionFilter);

var controllerCtx = HttpModuleHelper.GetFakeControllerContext(isCustomErrorEnabled: true);
var exceptionContext = new ExceptionContext(controllerCtx, new Exception());
exceptionContext.Exception = null;
handleExceptionFilter.OnException(exceptionContext);

Assert.IsFalse(this.sentTelemetry.Any());
}
}

private class MvcAutoInjectedFilter : HandleErrorAttribute
{
public const bool IsAutoInjected = true;
}
}
}
54 changes: 52 additions & 2 deletions Src/Web/Web.Net45.Tests/Web.Net45.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Test.props'))\Test.props" />
<PropertyGroup>
Expand All @@ -25,25 +25,73 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
<ItemGroup>
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ApplicationInsights, Version=2.6.0-beta2, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.ApplicationInsights.2.6.0-beta2\lib\net45\Microsoft.ApplicationInsights.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.WindowsAzure.ServiceRuntime, Version=2.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Web.Shared.Net.Tests\Azure\Emulation\Microsoft.WindowsAzure.ServiceRuntime.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.8.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Moq.4.8.2\lib\net45\Moq.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
<Reference Include="System.Configuration" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\System.Diagnostics.DiagnosticSource.4.4.0\lib\net45\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Management" />
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Formatting, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.4\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\System.Threading.Tasks.Extensions.4.3.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.4\lib\net45\System.Web.Http.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.WebHost, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.4\lib\net45\System.Web.Http.WebHost.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.AspNet.Mvc.5.2.4\lib\net45\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.AspNet.Razor.3.2.4\lib\net45\System.Web.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.WebPages.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Deployment, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.WebPages.Deployment.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
Expand Down Expand Up @@ -76,7 +124,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AspNetDiagnosticTelemetryModuleTest.cs" />
<Compile Include="MvcExceptionHandlerTests.cs" />
<Compile Include="RequestTrackingTelemetryModuleTest.Net45.cs" />
<Compile Include="WebApiExceptionLoggerTests.cs" />
</ItemGroup>
<Import Project="..\Web.Shared.Net.Tests\Web.Shared.Net.Tests.projitems" Label="Shared" />
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
Expand All @@ -90,4 +140,4 @@
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets'))" />
</Target>
</Project>
</Project>
103 changes: 103 additions & 0 deletions Src/Web/Web.Net45.Tests/WebApiExceptionLoggerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
namespace Microsoft.ApplicationInsights
{
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Web.Http;
using System.Web.Http.ExceptionHandling;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Web;
using Microsoft.ApplicationInsights.Web.TestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class WebApiExceptionLoggerTests
{
private ConcurrentQueue<ITelemetry> sentTelemetry;
private TelemetryConfiguration configuration;

[TestInitialize]
public void TestInit()
{
GlobalConfiguration.Configuration.Services.Clear(typeof(IExceptionLogger));
this.sentTelemetry = new ConcurrentQueue<ITelemetry>();

var stubTelemetryChannel = new StubTelemetryChannel
{
OnSend = t =>
{
if (t is ExceptionTelemetry telemetry)
{
this.sentTelemetry.Enqueue(telemetry);
}
}
};

this.configuration = new TelemetryConfiguration
{
InstrumentationKey = Guid.NewGuid().ToString(),
TelemetryChannel = stubTelemetryChannel
};
}

[TestCleanup]
public void Cleanup()
{
while (this.sentTelemetry.TryDequeue(out var _))
{
}

GlobalConfiguration.Configuration.Services.Clear(typeof(IExceptionLogger));
}

[TestMethod]
public void WebApiExceptionLoggerIsInjectedAndTracksException()
{
Assert.IsFalse(GlobalConfiguration.Configuration.Services.GetServices(typeof(IExceptionLogger)).Any());

using (var exceptionModule = new ExceptionTrackingTelemetryModule())
{
exceptionModule.Initialize(this.configuration);

var webApiExceptionLoggers = GlobalConfiguration.Configuration.Services.GetServices(typeof(IExceptionLogger)).ToList();
Assert.AreEqual(1, webApiExceptionLoggers.Count);

var logger = (ExceptionLogger)webApiExceptionLoggers[0];
Assert.IsNotNull(logger);

var exception = new Exception("test");
var exceptionContext = new ExceptionLoggerContext(new ExceptionContext(exception, new ExceptionContextCatchBlock("catch block name", true, false)));
logger.Log(exceptionContext);

Assert.AreEqual(1, this.sentTelemetry.Count);

var trackedException = (ExceptionTelemetry)this.sentTelemetry.Single();
Assert.IsNotNull(trackedException);
Assert.AreEqual(exception, trackedException.Exception);
}
}

[TestMethod]
public void WebApiExceptionLoggerIsNotInjectedIfAnotherInjectionDetected()
{
GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new WebApiAutoInjectedLogger());
Assert.AreEqual(1, GlobalConfiguration.Configuration.Services.GetServices(typeof(IExceptionLogger)).Count());

using (var exceptionModule = new ExceptionTrackingTelemetryModule())
{
exceptionModule.Initialize(this.configuration);

var loggers = GlobalConfiguration.Configuration.Services.GetServices(typeof(IExceptionLogger)).ToList();
Assert.AreEqual(1, loggers.Count);
Assert.IsInstanceOfType(loggers.Single(), typeof(WebApiAutoInjectedLogger));
}
}

private class WebApiAutoInjectedLogger : ExceptionLogger
{
public const bool IsAutoInjected = true;
}
}
}
Loading