From 6758010e1af8af6fd775d9c8903237f03a030d4a Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 16 Sep 2015 11:01:29 -0700 Subject: [PATCH] #353,#354 Add telemetry for begin/end request and unhandled exceptions. --- .../Internal/HostingEngine.cs | 30 ++++- .../WebHostBuilder.cs | 5 + src/Microsoft.AspNet.Hosting/project.json | 9 +- .../TestServerTests.cs | 107 ++++++++++++++++++ .../project.json | 1 + 5 files changed, 147 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs b/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs index d77831fd18..50cd13443f 100644 --- a/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs +++ b/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Tracing; using System.Linq; using System.Threading; using Microsoft.AspNet.Builder; @@ -82,6 +83,7 @@ namespace Microsoft.AspNet.Hosting.Internal var logger = _applicationServices.GetRequiredService>(); var contextFactory = _applicationServices.GetRequiredService(); var contextAccessor = _applicationServices.GetRequiredService(); + var telemetrySource = _applicationServices.GetRequiredService(); var server = ServerFactory.Start(_serverInstance, async features => { @@ -89,10 +91,32 @@ namespace Microsoft.AspNet.Hosting.Internal httpContext.ApplicationServices = _applicationServices; var requestIdentifier = GetRequestIdentifier(httpContext); - using (logger.BeginScope("Request Id: {RequestId}", requestIdentifier)) + if (telemetrySource.IsEnabled("Microsoft.AspNet.Hosting.BeginRequest")) { - contextAccessor.HttpContext = httpContext; - await application(httpContext); + telemetrySource.WriteTelemetry("Microsoft.AspNet.Hosting.BeginRequest", new { httpContext = httpContext }); + } + + try + { + using (logger.BeginScope("Request Id: {RequestId}", requestIdentifier)) + { + contextAccessor.HttpContext = httpContext; + await application(httpContext); + } + } + catch (Exception ex) + { + if (telemetrySource.IsEnabled("Microsoft.AspNet.Hosting.UnhandledException")) + { + telemetrySource.WriteTelemetry("Microsoft.AspNet.Hosting.UnhandledException", new { httpContext = httpContext, exception = ex }); + } + + throw; + } + + if (telemetrySource.IsEnabled("Microsoft.AspNet.Hosting.EndRequest")) + { + telemetrySource.WriteTelemetry("Microsoft.AspNet.Hosting.EndRequest", new { httpContext = httpContext }); } }); diff --git a/src/Microsoft.AspNet.Hosting/WebHostBuilder.cs b/src/Microsoft.AspNet.Hosting/WebHostBuilder.cs index 6efc8042d5..4d2bc9eebf 100644 --- a/src/Microsoft.AspNet.Hosting/WebHostBuilder.cs +++ b/src/Microsoft.AspNet.Hosting/WebHostBuilder.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics.Tracing; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Hosting.Builder; using Microsoft.AspNet.Hosting.Internal; @@ -91,6 +92,10 @@ namespace Microsoft.AspNet.Hosting services.AddSingleton(); services.AddLogging(); + var telemetrySource = new TelemetryListener("Microsoft.AspNet"); + services.AddInstance(telemetrySource); + services.AddInstance(telemetrySource); + // Conjure up a RequestServices services.AddTransient(); diff --git a/src/Microsoft.AspNet.Hosting/project.json b/src/Microsoft.AspNet.Hosting/project.json index 00218c9fb5..c8b50aad56 100644 --- a/src/Microsoft.AspNet.Hosting/project.json +++ b/src/Microsoft.AspNet.Hosting/project.json @@ -19,10 +19,15 @@ "Microsoft.Framework.DependencyInjection": "1.0.0-*", "Microsoft.Framework.Logging": "1.0.0-*", "Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*", - "Newtonsoft.Json": "6.0.6" + "Newtonsoft.Json": "6.0.6", + "System.Diagnostics.Tracing.Telemetry": "4.0.0-beta-*" }, "frameworks": { - "dnx451": { }, + "dnx451": { + "frameworkAssemblies": { + "System.Runtime": "" + } + }, "dnxcore50": { "dependencies": { "System.Console": "4.0.0-beta-*" diff --git a/test/Microsoft.AspNet.TestHost.Tests/TestServerTests.cs b/test/Microsoft.AspNet.TestHost.Tests/TestServerTests.cs index 47277d7286..ed4bc90a05 100644 --- a/test/Microsoft.AspNet.TestHost.Tests/TestServerTests.cs +++ b/test/Microsoft.AspNet.TestHost.Tests/TestServerTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics.Tracing; using System.IO; using System.Net; using System.Net.Http; @@ -13,6 +14,7 @@ using Microsoft.AspNet.Http; using Microsoft.Framework.Configuration; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; +using Microsoft.Framework.TelemetryAdapter; using Xunit; namespace Microsoft.AspNet.TestHost @@ -310,6 +312,111 @@ namespace Microsoft.AspNet.TestHost Assert.Equal("FoundFoo:False", await result.Content.ReadAsStringAsync()); } + [Fact] + public async Task BeginEndTelemetryAvailable() + { + TelemetryListener telemetryListener = null; + var server = TestServer.Create(app => + { + telemetryListener = app.ApplicationServices.GetRequiredService(); + app.Run(context => + { + return context.Response.WriteAsync("Hello World"); + }); + }); + var listener = new TestTelemetryListener(); + telemetryListener.SubscribeWithAdapter(listener); + var result = await server.CreateClient().GetStringAsync("/path"); + + Assert.Equal("Hello World", result); + Assert.NotNull(listener.BeginRequest?.HttpContext); + Assert.NotNull(listener.EndRequest?.HttpContext); + Assert.Null(listener.UnhandledException); + } + + [Fact] + public async Task ExceptionTelemetryAvailable() + { + TelemetryListener telemetryListener = null; + var server = TestServer.Create(app => + { + telemetryListener = app.ApplicationServices.GetRequiredService(); + app.Run(context => + { + throw new Exception("Test exception"); + }); + }); + var listener = new TestTelemetryListener(); + telemetryListener.SubscribeWithAdapter(listener); + await Assert.ThrowsAsync(() => server.CreateClient().GetAsync("/path")); + + Assert.NotNull(listener.BeginRequest?.HttpContext); + Assert.Null(listener.EndRequest?.HttpContext); + Assert.NotNull(listener.UnhandledException?.HttpContext); + Assert.NotNull(listener.UnhandledException?.Exception); + } + + public class TestTelemetryListener + { + public class OnBeginRequestEventData + { + public IProxyHttpContext HttpContext { get; set; } + } + + public OnBeginRequestEventData BeginRequest { get; set; } + + [TelemetryName("Microsoft.AspNet.Hosting.BeginRequest")] + public virtual void OnBeginRequest(IProxyHttpContext httpContext) + { + BeginRequest = new OnBeginRequestEventData() + { + HttpContext = httpContext, + }; + } + + public class OnEndRequestEventData + { + public IProxyHttpContext HttpContext { get; set; } + } + + public OnEndRequestEventData EndRequest { get; set; } + + [TelemetryName("Microsoft.AspNet.Hosting.EndRequest")] + public virtual void OnEndRequest(IProxyHttpContext httpContext) + { + EndRequest = new OnEndRequestEventData() + { + HttpContext = httpContext, + }; + } + + public class OnUnhandledExceptionEventData + { + public IProxyHttpContext HttpContext { get; set; } + public IProxyException Exception { get; set; } + } + + public OnUnhandledExceptionEventData UnhandledException { get; set; } + + [TelemetryName("Microsoft.AspNet.Hosting.UnhandledException")] + public virtual void OnUnhandledException(IProxyHttpContext httpContext, IProxyException exception) + { + UnhandledException = new OnUnhandledExceptionEventData() + { + HttpContext = httpContext, + Exception = exception, + }; + } + } + + public interface IProxyHttpContext + { + } + + public interface IProxyException + { + } + public class Startup { public void Configure(IApplicationBuilder builder) diff --git a/test/Microsoft.AspNet.TestHost.Tests/project.json b/test/Microsoft.AspNet.TestHost.Tests/project.json index b203628aa6..1d9db6febe 100644 --- a/test/Microsoft.AspNet.TestHost.Tests/project.json +++ b/test/Microsoft.AspNet.TestHost.Tests/project.json @@ -2,6 +2,7 @@ "dependencies": { "Microsoft.AspNet.Testing": "1.0.0-*", "Microsoft.AspNet.TestHost": "1.0.0-*", + "Microsoft.Framework.TelemetryAdapter": "1.0.0-*", "xunit.runner.aspnet": "2.0.0-aspnet-*" }, "commands": {