From c8990bcc291f2196d76a73b35d79ab6e36d32452 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 17 Nov 2015 16:07:03 -0800 Subject: [PATCH] Hosting#318 Add middleware analysis tools, sample. --- DiagnosticsPages.sln | 45 ++++++++ .../MiddlewareAnalysisSample.xproj | 19 ++++ samples/MiddlewareAnalysisSample/Startup.cs | 106 ++++++++++++++++++ samples/MiddlewareAnalysisSample/project.json | 19 ++++ .../AnalysisBuilder.cs | 65 +++++++++++ .../AnalysisMiddleware.cs | 54 +++++++++ .../AnalysisServiceCollectionExtensions.cs | 19 ++++ .../AnalysisStartupFilter.cs | 27 +++++ .../Microsoft.AspNet.MiddlewareAnalysis.xproj | 20 ++++ .../Properties/AssemblyInfo.cs | 8 ++ .../project.json | 22 ++++ ...soft.AspNet.MiddlewareAnalysis.Tests.xproj | 21 ++++ .../MiddlewareAnalysisTests.cs | 49 ++++++++ .../TestDiagnosticListener.cs | 35 ++++++ .../project.json | 21 ++++ 15 files changed, 530 insertions(+) create mode 100644 samples/MiddlewareAnalysisSample/MiddlewareAnalysisSample.xproj create mode 100644 samples/MiddlewareAnalysisSample/Startup.cs create mode 100644 samples/MiddlewareAnalysisSample/project.json create mode 100644 src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisBuilder.cs create mode 100644 src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisMiddleware.cs create mode 100644 src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisServiceCollectionExtensions.cs create mode 100644 src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisStartupFilter.cs create mode 100644 src/Microsoft.AspNet.MiddlewareAnalysis/Microsoft.AspNet.MiddlewareAnalysis.xproj create mode 100644 src/Microsoft.AspNet.MiddlewareAnalysis/Properties/AssemblyInfo.cs create mode 100644 src/Microsoft.AspNet.MiddlewareAnalysis/project.json create mode 100644 test/Microsoft.AspNet.MiddlewareAnalysis.Tests/Microsoft.AspNet.MiddlewareAnalysis.Tests.xproj create mode 100644 test/Microsoft.AspNet.MiddlewareAnalysis.Tests/MiddlewareAnalysisTests.cs create mode 100644 test/Microsoft.AspNet.MiddlewareAnalysis.Tests/TestDiagnosticListener.cs create mode 100644 test/Microsoft.AspNet.MiddlewareAnalysis.Tests/project.json diff --git a/DiagnosticsPages.sln b/DiagnosticsPages.sln index 03831ee26b..f46829d52a 100644 --- a/DiagnosticsPages.sln +++ b/DiagnosticsPages.sln @@ -44,6 +44,12 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ElmPageSample", "samples\El EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "DatabaseErrorPageSample", "samples\DatabaseErrorPageSample\DatabaseErrorPageSample.xproj", "{FF7F11A1-14E7-4948-A853-2487D99DE0C6}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.MiddlewareAnalysis", "src\Microsoft.AspNet.MiddlewareAnalysis\Microsoft.AspNet.MiddlewareAnalysis.xproj", "{C2DEDD37-D1EB-4819-9A9C-475E37A52358}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MiddlewareAnalysisSample", "samples\MiddlewareAnalysisSample\MiddlewareAnalysisSample.xproj", "{95981429-F212-44E1-83AA-32DA9E2A517C}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.MiddlewareAnalysis.Tests", "test\Microsoft.AspNet.MiddlewareAnalysis.Tests\Microsoft.AspNet.MiddlewareAnalysis.Tests.xproj", "{B0166AED-738F-42EE-AF4D-C487C8B21521}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -220,6 +226,42 @@ Global {FF7F11A1-14E7-4948-A853-2487D99DE0C6}.Release|Mixed Platforms.Build.0 = Release|Any CPU {FF7F11A1-14E7-4948-A853-2487D99DE0C6}.Release|x86.ActiveCfg = Release|Any CPU {FF7F11A1-14E7-4948-A853-2487D99DE0C6}.Release|x86.Build.0 = Release|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Debug|x86.ActiveCfg = Debug|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Debug|x86.Build.0 = Debug|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Release|Any CPU.Build.0 = Release|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Release|x86.ActiveCfg = Release|Any CPU + {C2DEDD37-D1EB-4819-9A9C-475E37A52358}.Release|x86.Build.0 = Release|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Debug|x86.ActiveCfg = Debug|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Debug|x86.Build.0 = Debug|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Release|Any CPU.Build.0 = Release|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Release|x86.ActiveCfg = Release|Any CPU + {95981429-F212-44E1-83AA-32DA9E2A517C}.Release|x86.Build.0 = Release|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Debug|x86.ActiveCfg = Debug|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Debug|x86.Build.0 = Debug|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Release|Any CPU.Build.0 = Release|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Release|x86.ActiveCfg = Release|Any CPU + {B0166AED-738F-42EE-AF4D-C487C8B21521}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -240,5 +282,8 @@ Global {83FFB65A-97B1-45AA-BCB8-3F43966BC8A3} = {509A6F36-AD80-4A18-B5B1-717D38DFF29D} {FFD28DCF-C24F-4C59-9B6B-F3B74CE13129} = {ACAA0157-A8C4-4152-93DE-90CCDF304087} {FF7F11A1-14E7-4948-A853-2487D99DE0C6} = {ACAA0157-A8C4-4152-93DE-90CCDF304087} + {C2DEDD37-D1EB-4819-9A9C-475E37A52358} = {509A6F36-AD80-4A18-B5B1-717D38DFF29D} + {95981429-F212-44E1-83AA-32DA9E2A517C} = {ACAA0157-A8C4-4152-93DE-90CCDF304087} + {B0166AED-738F-42EE-AF4D-C487C8B21521} = {2AF90579-B118-4583-AE88-672EFACB5BC4} EndGlobalSection EndGlobal diff --git a/samples/MiddlewareAnalysisSample/MiddlewareAnalysisSample.xproj b/samples/MiddlewareAnalysisSample/MiddlewareAnalysisSample.xproj new file mode 100644 index 0000000000..92177a647e --- /dev/null +++ b/samples/MiddlewareAnalysisSample/MiddlewareAnalysisSample.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 95981429-f212-44e1-83aa-32da9e2a517c + MiddlewareAnalysisSample + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + 2764 + + + \ No newline at end of file diff --git a/samples/MiddlewareAnalysisSample/Startup.cs b/samples/MiddlewareAnalysisSample/Startup.cs new file mode 100644 index 0000000000..3a10098043 --- /dev/null +++ b/samples/MiddlewareAnalysisSample/Startup.cs @@ -0,0 +1,106 @@ +using System; +using System.Diagnostics; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DiagnosticAdapter; +using Microsoft.Extensions.Logging; + +namespace MiddlewareAnaysisSample +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddMiddlewareAnalysis(); + } + + public void Configure(IApplicationBuilder app, ILoggerFactory factory, DiagnosticListener diagnosticListener) + { + // Displays all log levels + factory.AddConsole(LogLevel.Verbose); + + // Listen for middleware events and log them to the console. + var listener = new TestDiagnosticListener(); + diagnosticListener.SubscribeWithAdapter(listener); + + // Build our application pipeline + + // Named via app.UseMiddleware() + app.UseDeveloperExceptionPage(); + app.UseIISPlatformHandler(); + + // Anonymous method inline middleware + app.Use((context, next) => + { + // No-op + return next(); + }); + + app.Map("/map", subApp => + { + subApp.Run(context => + { + return context.Response.WriteAsync("Hello World"); + }); + }); + + // Low level anonymous method inline middleware, named Diagnostics.Middleware.Analysis.Startup+<>c by default + app.Use(next => + { + return context => + { + return next(context); + }; + }); + + app.Map("/throw", throwApp => + { + throwApp.Run(context => { throw new Exception("Application Exception"); }); + }); + + // The home page. + app.Properties["analysis.NextMiddlewareName"] = "HomePage"; + app.Use(async (context, next) => + { + if (context.Request.Path == "/") + { + context.Response.ContentType = "text/html"; + await context.Response.WriteAsync("Welcome to the sample

\r\n"); + await context.Response.WriteAsync("Click here to take a side branch: Map
\r\n"); + await context.Response.WriteAsync("Click here to throw an exception: Throw
\r\n"); + await context.Response.WriteAsync("Click here to for a 404: 404
\r\n"); + await context.Response.WriteAsync("\r\n"); + return; + } + else + { + await next(); + } + }); + + // Note there's always a default 404 middleware at the end of the pipeline. + } + + public class TestDiagnosticListener + { + [DiagnosticName("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareStarting")] + public virtual void OnMiddlewareStarting(HttpContext httpContext, string name) + { + Console.WriteLine($"MiddlewareStarting: {name}; {httpContext.Request.Path}"); + } + + [DiagnosticName("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareException")] + public virtual void OnMiddlewareException(Exception exception, string name) + { + Console.WriteLine($"MiddlewareException: {name}; {exception.Message}"); + } + + [DiagnosticName("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareFinished")] + public virtual void OnMiddlewareFinished(HttpContext httpContext, string name) + { + Console.WriteLine($"MiddlewareFinished: {name}; {httpContext.Response.StatusCode}"); + } + } + } +} diff --git a/samples/MiddlewareAnalysisSample/project.json b/samples/MiddlewareAnalysisSample/project.json new file mode 100644 index 0000000000..b717045670 --- /dev/null +++ b/samples/MiddlewareAnalysisSample/project.json @@ -0,0 +1,19 @@ +{ + "dependencies": { + "Microsoft.AspNet.Diagnostics": "1.0.0-*", + "Microsoft.AspNet.IISPlatformHandler": "1.0.0-*", + "Microsoft.AspNet.MiddlewareAnalysis": "1.0.0-*", + "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", + "Microsoft.AspNet.Server.WebListener": "1.0.0-*", + "Microsoft.Extensions.DiagnosticAdapter": "1.0.0-*", + "Microsoft.Extensions.Logging.Console": "1.0.0-*" + }, + "commands": { + "web": "Microsoft.AspNet.Server.Kestrel", + "weblistener": "Microsoft.AspNet.Server.WebListener" + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + } +} diff --git a/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisBuilder.cs b/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisBuilder.cs new file mode 100644 index 0000000000..ca4f2c360d --- /dev/null +++ b/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisBuilder.cs @@ -0,0 +1,65 @@ +// 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 System; +using System.Collections.Generic; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Features; + +namespace Microsoft.AspNet.MiddlewareAnalysis +{ + public class AnalysisBuilder : IApplicationBuilder + { + private const string NextMiddlewareName = "analysis.NextMiddlewareName"; + + public AnalysisBuilder(IApplicationBuilder inner) + { + InnerBuilder = inner; + } + + private IApplicationBuilder InnerBuilder { get; } + + public IServiceProvider ApplicationServices + { + get { return InnerBuilder.ApplicationServices; } + set { InnerBuilder.ApplicationServices = value; } + } + + public IDictionary Properties + { + get { return InnerBuilder.Properties; } + } + + public IFeatureCollection ServerFeatures + { + get { return InnerBuilder.ServerFeatures;} + } + + public RequestDelegate Build() + { + // Add one maker at the end before the default 404 middleware (or any fancy Join middleware). + return InnerBuilder.UseMiddleware("EndOfPipeline") + .Build(); + } + + public IApplicationBuilder New() + { + return new AnalysisBuilder(InnerBuilder.New()); + } + + public IApplicationBuilder Use(Func middleware) + { + string middlewareName = string.Empty; // UseMiddleware doesn't work with null params. + object middlewareNameObj; + if (Properties.TryGetValue(NextMiddlewareName, out middlewareNameObj)) + { + middlewareName = middlewareNameObj?.ToString(); + Properties.Remove(NextMiddlewareName); + } + + return InnerBuilder.UseMiddleware(middlewareName) + .Use(middleware); + } + } +} diff --git a/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisMiddleware.cs b/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisMiddleware.cs new file mode 100644 index 0000000000..79bb52d81b --- /dev/null +++ b/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisMiddleware.cs @@ -0,0 +1,54 @@ +// 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 System; +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.MiddlewareAnalysis +{ + public class AnalysisMiddleware + { + private readonly RequestDelegate _next; + private readonly DiagnosticSource _diagnostics; + private readonly string _middlewareName; + + public AnalysisMiddleware(RequestDelegate next, DiagnosticSource diagnosticSource, string middlewareName) + { + _next = next; + _diagnostics = diagnosticSource; + if (string.IsNullOrEmpty(middlewareName)) + { + middlewareName = next.Target.GetType().FullName; + } + _middlewareName = middlewareName; + } + + public async Task Invoke(HttpContext httpContext) + { + if (_diagnostics.IsEnabled("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareStarting")) + { + _diagnostics.Write("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareStarting", new { name = _middlewareName, httpContext = httpContext }); + } + + try + { + await _next(httpContext); + + if (_diagnostics.IsEnabled("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareFinished")) + { + _diagnostics.Write("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareFinished", new { name = _middlewareName, httpContext = httpContext }); + } + } + catch (Exception ex) + { + if (_diagnostics.IsEnabled("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareException")) + { + _diagnostics.Write("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareException", new { name = _middlewareName, httpContext = httpContext, exception = ex }); + } + throw; + } + } + } +} diff --git a/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisServiceCollectionExtensions.cs b/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisServiceCollectionExtensions.cs new file mode 100644 index 0000000000..c337c959a8 --- /dev/null +++ b/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisServiceCollectionExtensions.cs @@ -0,0 +1,19 @@ +// 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.AspNet.Hosting; +using Microsoft.AspNet.MiddlewareAnalysis; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class AnalysisServiceCollectionExtensions + { + public static IServiceCollection AddMiddlewareAnalysis(this IServiceCollection services) + { + // This should prevent AnalysisStartupFilter from being registered more than once. + services.TryAddEnumerable(ServiceDescriptor.Transient()); + return services; + } + } +} diff --git a/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisStartupFilter.cs b/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisStartupFilter.cs new file mode 100644 index 0000000000..edb092c918 --- /dev/null +++ b/src/Microsoft.AspNet.MiddlewareAnalysis/AnalysisStartupFilter.cs @@ -0,0 +1,27 @@ +// 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 System; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Hosting; + +namespace Microsoft.AspNet.MiddlewareAnalysis +{ + public class AnalysisStartupFilter : IStartupFilter + { + public Action Configure(Action next) + { + return builder => + { + var wrappedBuilder = new AnalysisBuilder(builder); + next(wrappedBuilder); + + // The caller doesn't call build on our new builder, they call it on the original. Add this + // default middleware to the end. Compare with AnalysisBuilder.Build(); + + // Add one maker at the end before the default 404 middleware (or any fancy Join middleware). + builder.UseMiddleware("EndOfPipeline"); + }; + } + } +} diff --git a/src/Microsoft.AspNet.MiddlewareAnalysis/Microsoft.AspNet.MiddlewareAnalysis.xproj b/src/Microsoft.AspNet.MiddlewareAnalysis/Microsoft.AspNet.MiddlewareAnalysis.xproj new file mode 100644 index 0000000000..7b0842ca52 --- /dev/null +++ b/src/Microsoft.AspNet.MiddlewareAnalysis/Microsoft.AspNet.MiddlewareAnalysis.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + c2dedd37-d1eb-4819-9a9c-475e37a52358 + Microsoft.AspNet.MiddlewareAnalysis + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/Microsoft.AspNet.MiddlewareAnalysis/Properties/AssemblyInfo.cs b/src/Microsoft.AspNet.MiddlewareAnalysis/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..9cb7b1f01b --- /dev/null +++ b/src/Microsoft.AspNet.MiddlewareAnalysis/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +// 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 System.Reflection; +using System.Resources; + +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: NeutralResourcesLanguage("en-US")] diff --git a/src/Microsoft.AspNet.MiddlewareAnalysis/project.json b/src/Microsoft.AspNet.MiddlewareAnalysis/project.json new file mode 100644 index 0000000000..e4b76199f4 --- /dev/null +++ b/src/Microsoft.AspNet.MiddlewareAnalysis/project.json @@ -0,0 +1,22 @@ +{ + "version": "1.0.0-*", + "description": "ASP.NET 5 Middleware for analyzing middleware in the request pipeline.", + "compilationOptions": { + "warningsAsErrors": true, + "keyFile": "../../tools/Key.snk" + }, + "repository": { + "type": "git", + "url": "git://github.com/aspnet/diagnostics" + }, + "dependencies": { + "Microsoft.AspNet.Hosting.Abstractions": "1.0.0-*", + "Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.0-*", + "System.Diagnostics.DiagnosticSource": "4.0.0-beta-*" + }, + + "frameworks": { + "net451": { }, + "dotnet5.4": { } + } +} diff --git a/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/Microsoft.AspNet.MiddlewareAnalysis.Tests.xproj b/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/Microsoft.AspNet.MiddlewareAnalysis.Tests.xproj new file mode 100644 index 0000000000..5207d14faf --- /dev/null +++ b/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/Microsoft.AspNet.MiddlewareAnalysis.Tests.xproj @@ -0,0 +1,21 @@ + + + + 14.0.23107 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + b0166aed-738f-42ee-af4d-c487c8b21521 + Microsoft.AspNet.MiddlewareAnalysis.Tests + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/MiddlewareAnalysisTests.cs b/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/MiddlewareAnalysisTests.cs new file mode 100644 index 0000000000..9c50479053 --- /dev/null +++ b/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/MiddlewareAnalysisTests.cs @@ -0,0 +1,49 @@ +// 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 System; +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.AspNet.MiddlewareAnalysis +{ + public class MiddlewareAnalysisTests + { + [Fact] + public async Task ExceptionWrittenToDiagnostics() + { + DiagnosticListener diagnosticListener = null; + + var server = TestServer.Create(app => + { + diagnosticListener = app.ApplicationServices.GetRequiredService(); + + app.UseDeveloperExceptionPage(); + app.Run(context => + { + throw new Exception("Test exception"); + }); + }, services => services.AddMiddlewareAnalysis()); + + var listener = new TestDiagnosticListener(); + diagnosticListener.SubscribeWithAdapter(listener); + + await server.CreateClient().GetAsync(string.Empty); + + // "Microsoft.AspnNet.Hosting.RequestServicesContainerMiddleware","Microsoft.AspnNet.Diagnostics.DeveloperExceptionPageMiddleware", + // "Microsoft.AspNet.MiddlewareAnalysis.MiddlewareAnalysisTests.+<>c" + Assert.Equal(3, listener.MiddlewareStarting.Count); + Assert.Equal("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareAnalysisTests+<>c", listener.MiddlewareStarting[2]); + // reversed "RunInlineMiddleware" + Assert.Equal(1, listener.MiddlewareException.Count); + Assert.Equal("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareAnalysisTests+<>c", listener.MiddlewareException[0]); + // reversed "Microsoft.AspnNet.Diagnostics.DeveloperExceptionPageMiddleware","Microsoft.AspnNet.Hosting.RequestServicesContainerMiddleware" + Assert.Equal(2, listener.MiddlewareFinished.Count); + Assert.Equal("Microsoft.AspNet.Diagnostics.DeveloperExceptionPageMiddleware", listener.MiddlewareFinished[0]); + } + } +} diff --git a/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/TestDiagnosticListener.cs b/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/TestDiagnosticListener.cs new file mode 100644 index 0000000000..035b106648 --- /dev/null +++ b/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/TestDiagnosticListener.cs @@ -0,0 +1,35 @@ +// 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 System; +using System.Collections.Generic; +using Microsoft.AspNet.Http; +using Microsoft.Extensions.DiagnosticAdapter; + +namespace Microsoft.AspNet.MiddlewareAnalysis +{ + public class TestDiagnosticListener + { + public IList MiddlewareStarting { get; } = new List(); + public IList MiddlewareFinished { get; } = new List(); + public IList MiddlewareException { get; } = new List(); + + [DiagnosticName("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareStarting")] + public virtual void OnMiddlewareStarting(HttpContext httpContext, string name) + { + MiddlewareStarting.Add(name); + } + + [DiagnosticName("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareException")] + public virtual void OnMiddlewareException(Exception exception, string name) + { + MiddlewareException.Add(name); + } + + [DiagnosticName("Microsoft.AspNet.MiddlewareAnalysis.MiddlewareFinished")] + public virtual void OnMiddlewareFinished(HttpContext httpContext, string name) + { + MiddlewareFinished.Add(name); + } + } +} diff --git a/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/project.json b/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/project.json new file mode 100644 index 0000000000..9b014f1cbd --- /dev/null +++ b/test/Microsoft.AspNet.MiddlewareAnalysis.Tests/project.json @@ -0,0 +1,21 @@ +{ + "compilationOptions": { + "warningsAsErrors": false + }, + "dependencies": { + "Microsoft.AspNet.Diagnostics": "1.0.0-*", + "Microsoft.AspNet.MiddlewareAnalysis": "1.0.0-*", + "Microsoft.AspNet.TestHost": "1.0.0-*", + "Microsoft.AspNet.Testing": "1.0.0-*", + "Microsoft.Extensions.DependencyInjection": "1.0.0-*", + "Microsoft.Extensions.DiagnosticAdapter": "1.0.0-*", + "xunit.runner.aspnet": "2.0.0-aspnet-*" + }, + "frameworks": { + "dnx451": {}, + "dnxcore50": {} + }, + "commands": { + "test": "xunit.runner.aspnet" + } +}