From 694c5d2c3c211f87b3b0cf66dbd3399e386bf9b1 Mon Sep 17 00:00:00 2001 From: SonjaKhan Date: Thu, 9 Oct 2014 09:39:49 -0700 Subject: [PATCH 1/3] initial commit for the elm logger --- Logging.sln | 25 ++++++++++ samples/ElmSampleApp/ElmSampleApp.kproj | 18 +++++++ samples/ElmSampleApp/Startup.cs | 25 ++++++++++ samples/ElmSampleApp/project.json | 15 ++++++ .../ElmExtensions.cs | 17 +++++++ src/Microsoft.AspNet.Logging.Elm/ElmLog.cs | 15 ++++++ src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs | 47 +++++++++++++++++++ .../ElmLoggerProvider.cs | 13 +++++ .../ElmMiddleware.cs | 40 ++++++++++++++++ .../ElmOptions.cs | 15 ++++++ .../Microsoft.AspNet.Logging.Elm.kproj | 19 ++++++++ src/Microsoft.AspNet.Logging.Elm/project.json | 14 ++++++ 12 files changed, 263 insertions(+) create mode 100644 samples/ElmSampleApp/ElmSampleApp.kproj create mode 100644 samples/ElmSampleApp/Startup.cs create mode 100644 samples/ElmSampleApp/project.json create mode 100644 src/Microsoft.AspNet.Logging.Elm/ElmExtensions.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/ElmLog.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/Microsoft.AspNet.Logging.Elm.kproj create mode 100644 src/Microsoft.AspNet.Logging.Elm/project.json diff --git a/Logging.sln b/Logging.sln index 4df337f5..e88b9e8d 100644 --- a/Logging.sln +++ b/Logging.sln @@ -19,6 +19,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleApp", "samples\Sample EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Logging.Console", "src\Microsoft.Framework.Logging.Console\Microsoft.Framework.Logging.Console.kproj", "{75A4DE6D-BBAA-4D59-829D-94009E759A18}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Logging.Elm", "src\Microsoft.AspNet.Logging.Elm\Microsoft.AspNet.Logging.Elm.kproj", "{889A6055-2498-4AD0-A866-3B1638257D8B}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ElmSampleApp", "samples\ElmSampleApp\ElmSampleApp.kproj", "{55FB3329-DC96-461C-8E80-B409DF4B494C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -89,6 +93,26 @@ Global {75A4DE6D-BBAA-4D59-829D-94009E759A18}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {75A4DE6D-BBAA-4D59-829D-94009E759A18}.Release|Mixed Platforms.Build.0 = Release|Any CPU {75A4DE6D-BBAA-4D59-829D-94009E759A18}.Release|x86.ActiveCfg = Release|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Debug|x86.ActiveCfg = Debug|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Release|Any CPU.Build.0 = Release|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {889A6055-2498-4AD0-A866-3B1638257D8B}.Release|x86.ActiveCfg = Release|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Debug|x86.ActiveCfg = Debug|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Release|Any CPU.Build.0 = Release|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {55FB3329-DC96-461C-8E80-B409DF4B494C}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -96,5 +120,6 @@ Global GlobalSection(NestedProjects) = preSolution {96B1D6A8-7E40-43C7-813F-898DC8192DDE} = {09920C51-6220-4D8D-94DC-E70C13446187} {550E0247-0BDD-4016-A29B-250F075686FD} = {8C1F5D80-88EA-4961-84DC-7AC6E13951F4} + {55FB3329-DC96-461C-8E80-B409DF4B494C} = {8C1F5D80-88EA-4961-84DC-7AC6E13951F4} EndGlobalSection EndGlobal diff --git a/samples/ElmSampleApp/ElmSampleApp.kproj b/samples/ElmSampleApp/ElmSampleApp.kproj new file mode 100644 index 00000000..d54ac0e9 --- /dev/null +++ b/samples/ElmSampleApp/ElmSampleApp.kproj @@ -0,0 +1,18 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 55fb3329-dc96-461c-8e80-b409df4b494c + Web + WebApplication2 + + + 2.0 + 42601 + + + \ No newline at end of file diff --git a/samples/ElmSampleApp/Startup.cs b/samples/ElmSampleApp/Startup.cs new file mode 100644 index 00000000..ae32d7f1 --- /dev/null +++ b/samples/ElmSampleApp/Startup.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Logging.Elm; +using Microsoft.Framework.Logging; + +namespace ElmSampleApp +{ + public class Startup + { + public void Configure(IApplicationBuilder app, ILoggerFactory factory) + { + app.UseElm(new ElmOptions()); + + app.Run(async context => + { + context.Response.ContentType = "text/plain"; + await context.Response.WriteAsync("Hello world"); + + var logger = factory.Create("HelloWorld"); + logger.WriteCritical("Hello"); + logger.WriteInformation("World"); + }); + } + } +} diff --git a/samples/ElmSampleApp/project.json b/samples/ElmSampleApp/project.json new file mode 100644 index 00000000..20b1efe2 --- /dev/null +++ b/samples/ElmSampleApp/project.json @@ -0,0 +1,15 @@ +{ + "webroot": "wwwroot", + "exclude": "wwwroot/**/*.*", + "dependencies": { + "Microsoft.AspNet.Server.IIS": "1.0.0-*", + "Microsoft.AspNet.Diagnostics": "1.0.0-*", + "Microsoft.AspNet.PipelineCore": "1.0.0-*", + "Microsoft.AspNet.Http": "1.0.0-*", + "Microsoft.AspNet.Logging.Elm": "1.0.0-*" + }, + "frameworks" : { + "aspnet50" : { }, + "aspnetcore50" : { } + } +} diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmExtensions.cs b/src/Microsoft.AspNet.Logging.Elm/ElmExtensions.cs new file mode 100644 index 00000000..36eb5217 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/ElmExtensions.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNet.Logging.Elm; + +namespace Microsoft.AspNet.Builder +{ + public static class ElmExtensions + { + public static IApplicationBuilder UseElm(this IApplicationBuilder builder) + { + return builder.UseElm(new ElmOptions()); + } + + public static IApplicationBuilder UseElm(this IApplicationBuilder builder, ElmOptions options) + { + return builder.UseMiddleware(options); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmLog.cs b/src/Microsoft.AspNet.Logging.Elm/ElmLog.cs new file mode 100644 index 00000000..092024bb --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/ElmLog.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace Microsoft.Framework.Logging.Elm +{ + public static class ElmLog + { + static ElmLog() + { + Log = new List(); + } + + public static List Log { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs b/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs new file mode 100644 index 00000000..828ed118 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs @@ -0,0 +1,47 @@ +using System; +using Microsoft.Framework.Logging; +using Microsoft.Framework.Logging.Elm; + +namespace Microsoft.AspNet.Logging.Elm +{ + public class ElmLogger : ILogger + { + // TODO: allow filtering by name and TraceType + + public void Write(TraceType traceType, int eventId, object state, Exception exception, Func formatter) + { + var message = string.Empty; + if (formatter != null) + { + message = formatter(state, exception); + } + else + { + if (state != null) + { + message += state; + } + if (exception != null) + { + message += Environment.NewLine + exception; + } + } + if (string.IsNullOrEmpty(message)) + { + return; + } + // TODO: write to a database instead, and write more interesting information + ElmLog.Log.Add(string.Format("[{0}]: {1}", traceType, message)); + } + + public bool IsEnabled(TraceType traceType) + { + return true; + } + + public IDisposable BeginScope(object state) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs b/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs new file mode 100644 index 00000000..b20ee726 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Logging.Elm +{ + public class ElmLoggerProvider : ILoggerProvider + { + public ILogger Create(string name) + { + return new ElmLogger(); + } + } +} diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs b/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs new file mode 100644 index 00000000..1e997c13 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Logging; +using Microsoft.Framework.Logging.Elm; + +namespace Microsoft.AspNet.Logging.Elm +{ + /// + /// Enables the Elm logging service + /// + public class ElmMiddleware + { + private readonly RequestDelegate _next; + private readonly ElmOptions _options; + + public ElmMiddleware(RequestDelegate next, ILoggerFactory factory, ElmOptions options) + { + _next = next; + _options = options ?? new ElmOptions(); + factory.AddProvider(new ElmLoggerProvider()); + } + + public async Task Invoke(HttpContext context) + { + if (!context.Request.Path.Value.Equals(_options.Path ?? "/Elm")) + { + await _next(context); + return; + } + await context.Response.WriteAsync("------------Logs-----------\r\n"); + foreach (var log in ElmLog.Log) + { + await context.Response.WriteAsync(string.Format("{0}\r\n", log)); + } + await context.Response.WriteAsync("---------------------------\r\n"); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs b/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs new file mode 100644 index 00000000..e77cfbad --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs @@ -0,0 +1,15 @@ +using System; + +namespace Microsoft.AspNet.Logging.Elm +{ + /// + /// Options for ElmMiddleware + /// + public class ElmOptions + { + /// + /// Specifies the path to view the logs + /// + public string Path { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/Microsoft.AspNet.Logging.Elm.kproj b/src/Microsoft.AspNet.Logging.Elm/Microsoft.AspNet.Logging.Elm.kproj new file mode 100644 index 00000000..e39fc101 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/Microsoft.AspNet.Logging.Elm.kproj @@ -0,0 +1,19 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 889a6055-2498-4ad0-a866-3b1638257d8b + Library + Microsoft.Framework.Logging.Elm + + + + 2.0 + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/project.json b/src/Microsoft.AspNet.Logging.Elm/project.json new file mode 100644 index 00000000..b6587f13 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/project.json @@ -0,0 +1,14 @@ +{ + "version": "1.0.0-*", + "dependencies": { + "Microsoft.Framework.Logging.Interfaces": { "version": "1.0.0-*", "type": "build" }, + "Microsoft.AspNet.Http": "1.0.0-*", + "Microsoft.AspNet.RequestContainer": "1.0.0-*", + "Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" } + }, + + "frameworks": { + "aspnet50": { }, + "aspnetcore50": { } + } +} From 5b8b866f05ff2ca1263f60c9c0e1a65fad5a8405 Mon Sep 17 00:00:00 2001 From: SonjaKhan Date: Wed, 15 Oct 2014 18:39:38 -0700 Subject: [PATCH 2/3] making elm slightly better --- Logging.sln | 12 ++ samples/ElmSampleApp/Startup.cs | 25 ++- .../ElmExtensions.cs | 14 +- src/Microsoft.AspNet.Logging.Elm/ElmLog.cs | 15 -- src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs | 62 +++++- .../ElmLoggerProvider.cs | 19 +- .../ElmMiddleware.cs | 37 ++-- .../ElmOptions.cs | 7 +- src/Microsoft.AspNet.Logging.Elm/ElmStore.cs | 22 ++ src/Microsoft.AspNet.Logging.Elm/IElmStore.cs | 13 ++ .../LogContext.cs | 25 +++ src/Microsoft.AspNet.Logging.Elm/LogInfo.cs | 25 +++ .../Views/LogPage.cs | 198 ++++++++++++++++++ .../Views/LogPage.cshtml | 56 +++++ .../Views/LogPage.css | 47 +++++ .../Views/LogPageModel.cs | 9 + src/Microsoft.AspNet.Logging.Elm/project.json | 4 +- src/PageGenerator/PageGenerator.kproj | 20 ++ src/PageGenerator/Program.cs | 119 +++++++++++ src/PageGenerator/project.json | 18 ++ 20 files changed, 696 insertions(+), 51 deletions(-) delete mode 100644 src/Microsoft.AspNet.Logging.Elm/ElmLog.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/ElmStore.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/IElmStore.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/LogContext.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/LogInfo.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cshtml create mode 100644 src/Microsoft.AspNet.Logging.Elm/Views/LogPage.css create mode 100644 src/Microsoft.AspNet.Logging.Elm/Views/LogPageModel.cs create mode 100644 src/PageGenerator/PageGenerator.kproj create mode 100644 src/PageGenerator/Program.cs create mode 100644 src/PageGenerator/project.json diff --git a/Logging.sln b/Logging.sln index e88b9e8d..21f95227 100644 --- a/Logging.sln +++ b/Logging.sln @@ -23,6 +23,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Logging.El EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ElmSampleApp", "samples\ElmSampleApp\ElmSampleApp.kproj", "{55FB3329-DC96-461C-8E80-B409DF4B494C}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PageGenerator", "src\PageGenerator\PageGenerator.kproj", "{4D4A785A-ECB9-4916-A88F-0FD306EE3B74}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -113,6 +115,16 @@ Global {55FB3329-DC96-461C-8E80-B409DF4B494C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {55FB3329-DC96-461C-8E80-B409DF4B494C}.Release|Mixed Platforms.Build.0 = Release|Any CPU {55FB3329-DC96-461C-8E80-B409DF4B494C}.Release|x86.ActiveCfg = Release|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Debug|x86.ActiveCfg = Debug|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Release|Any CPU.Build.0 = Release|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {4D4A785A-ECB9-4916-A88F-0FD306EE3B74}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/ElmSampleApp/Startup.cs b/samples/ElmSampleApp/Startup.cs index ae32d7f1..1df8aa50 100644 --- a/samples/ElmSampleApp/Startup.cs +++ b/samples/ElmSampleApp/Startup.cs @@ -1,24 +1,39 @@ -using Microsoft.AspNet.Builder; +using System; +using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.AspNet.Logging.Elm; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; namespace ElmSampleApp { public class Startup { + public void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); // registering the service so it can be injected into constructors + services.Configure(options => + { + options.Path = new PathString("/foo"); + }); + } + public void Configure(IApplicationBuilder app, ILoggerFactory factory) { - app.UseElm(new ElmOptions()); + app.UseElm(); app.Run(async context => - { + { context.Response.ContentType = "text/plain"; await context.Response.WriteAsync("Hello world"); var logger = factory.Create("HelloWorld"); - logger.WriteCritical("Hello"); - logger.WriteInformation("World"); + context.Response.StatusCode = 200; + logger.WriteInformation("Hello"); + context.Response.StatusCode = 400; + logger.WriteCritical("World", new Exception("Bad Request")); + context.Response.StatusCode = 404; + logger.WriteWarning("Not Found", new Exception("Existential Crisis")); }); } } diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmExtensions.cs b/src/Microsoft.AspNet.Logging.Elm/ElmExtensions.cs index 36eb5217..e01ac09f 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmExtensions.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmExtensions.cs @@ -1,17 +1,15 @@ -using Microsoft.AspNet.Logging.Elm; +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Logging.Elm; namespace Microsoft.AspNet.Builder { public static class ElmExtensions - { + { public static IApplicationBuilder UseElm(this IApplicationBuilder builder) { - return builder.UseElm(new ElmOptions()); - } - - public static IApplicationBuilder UseElm(this IApplicationBuilder builder, ElmOptions options) - { - return builder.UseMiddleware(options); + return builder.UseMiddleware(); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmLog.cs b/src/Microsoft.AspNet.Logging.Elm/ElmLog.cs deleted file mode 100644 index 092024bb..00000000 --- a/src/Microsoft.AspNet.Logging.Elm/ElmLog.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Microsoft.Framework.Logging.Elm -{ - public static class ElmLog - { - static ElmLog() - { - Log = new List(); - } - - public static List Log { get; set; } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs b/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs index 828ed118..b8a4488c 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs @@ -1,12 +1,29 @@ -using System; +// Copyright (c) Microsoft Open Technologies, Inc. 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.Http; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; -using Microsoft.Framework.Logging.Elm; namespace Microsoft.AspNet.Logging.Elm { public class ElmLogger : ILogger { - // TODO: allow filtering by name and TraceType + private readonly string _name; + private readonly ElmLoggerProvider _provider; + private IElmStore _store; + private readonly IContextAccessor _contextAccessor; + private readonly object _requestIdentifierKey; + + public ElmLogger(string name, ElmLoggerProvider provider, IElmStore store, IContextAccessor contextAccessor, object requestIdentifierKey) + { + _name = name; + _provider = provider; + _store = store; + _contextAccessor = contextAccessor; + _requestIdentifierKey = requestIdentifierKey; + } public void Write(TraceType traceType, int eventId, object state, Exception exception, Func formatter) { @@ -30,8 +47,18 @@ public void Write(TraceType traceType, int eventId, object state, Exception exce { return; } - // TODO: write to a database instead, and write more interesting information - ElmLog.Log.Add(string.Format("[{0}]: {1}", traceType, message)); + + LogInfo info = new LogInfo() + { + Context = GetLogContext(), + Name = _name, + EventID = eventId, + Severity = traceType, + Exception = exception, + State = state, + Time = DateTime.Now + }; + _store.Write(info); } public bool IsEnabled(TraceType traceType) @@ -41,7 +68,30 @@ public bool IsEnabled(TraceType traceType) public IDisposable BeginScope(object state) { - throw new NotImplementedException(); + // TODO: use NullDisposable once it's moved to this repo #33 + return null; + } + + private LogContext GetLogContext() + { + var context = _contextAccessor.Value; + if (context == null) + { + return new LogContext(); + } + else + { + return new LogContext() + { + RequestID = (Guid)context.Items[_requestIdentifierKey], + Host = context.Request.Host, + ContentType = context.Request.ContentType, + Path = context.Request.Path, + Scheme = context.Request.Scheme, + StatusCode = context.Response.StatusCode, + User = context.User.Identity.Name + }; + } } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs b/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs index b20ee726..21ed5aa2 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs @@ -1,13 +1,28 @@ -using System; +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Http; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; namespace Microsoft.AspNet.Logging.Elm { public class ElmLoggerProvider : ILoggerProvider { + private readonly IElmStore _store; + private readonly IContextAccessor _contextAccessor; + private readonly object _requestIdentifierKey; + + public ElmLoggerProvider(IElmStore store, IContextAccessor contextAccessor, object requestIdentifierKey) + { + _store = store; + _contextAccessor = contextAccessor; + _requestIdentifierKey = requestIdentifierKey; + } + public ILogger Create(string name) { - return new ElmLogger(); + return new ElmLogger(name, this, _store, _contextAccessor, _requestIdentifierKey); } } } diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs b/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs index 1e997c13..1ae30857 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs @@ -1,9 +1,14 @@ -using System; +// Copyright (c) Microsoft Open Technologies, Inc. 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.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Logging.Elm.Views; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; -using Microsoft.Framework.Logging.Elm; +using Microsoft.Framework.OptionsModel; namespace Microsoft.AspNet.Logging.Elm { @@ -14,27 +19,35 @@ public class ElmMiddleware { private readonly RequestDelegate _next; private readonly ElmOptions _options; + private readonly ElmLoggerProvider _provider; + private readonly IElmStore _store; + private IContextAccessor _contextAccessor; + private static readonly object _requestIdentifier = new object(); - public ElmMiddleware(RequestDelegate next, ILoggerFactory factory, ElmOptions options) + public ElmMiddleware( + RequestDelegate next, ILoggerFactory factory, IOptions options, + IElmStore store, IContextAccessor contextAccessor) { _next = next; - _options = options ?? new ElmOptions(); - factory.AddProvider(new ElmLoggerProvider()); + _options = options.Options; + _store = store; + _contextAccessor = contextAccessor; + _provider = new ElmLoggerProvider(_store, contextAccessor, _requestIdentifier); + factory.AddProvider(_provider); } public async Task Invoke(HttpContext context) { - if (!context.Request.Path.Value.Equals(_options.Path ?? "/Elm")) + _contextAccessor.SetContextSource(() => context, null); + context.Items[_requestIdentifier] = Guid.NewGuid(); + if (!context.Request.Path.Value.Equals(_options.Path.Value ?? "/Elm")) { await _next(context); return; } - await context.Response.WriteAsync("------------Logs-----------\r\n"); - foreach (var log in ElmLog.Log) - { - await context.Response.WriteAsync(string.Format("{0}\r\n", log)); - } - await context.Response.WriteAsync("---------------------------\r\n"); + var model = new LogPageModel() { Logs = _store.GetLogs() }; + var logPage = new LogPage(model); + await logPage.ExecuteAsync(context); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs b/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs index e77cfbad..6158b1a7 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Http; namespace Microsoft.AspNet.Logging.Elm { @@ -10,6 +13,6 @@ public class ElmOptions /// /// Specifies the path to view the logs /// - public string Path { get; set; } + public PathString Path { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmStore.cs b/src/Microsoft.AspNet.Logging.Elm/ElmStore.cs new file mode 100644 index 00000000..e176697b --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/ElmStore.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.AspNet.Logging.Elm +{ + public class ElmStore : IElmStore + { + private static List Log { get; set; } = new List(); + + public IEnumerable GetLogs() + { + return Log; + } + + public void Write(LogInfo info) + { + Log.Add(info); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/IElmStore.cs b/src/Microsoft.AspNet.Logging.Elm/IElmStore.cs new file mode 100644 index 00000000..dfe347d9 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/IElmStore.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.AspNet.Logging.Elm +{ + public interface IElmStore + { + void Write(LogInfo info); + IEnumerable GetLogs(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/LogContext.cs b/src/Microsoft.AspNet.Logging.Elm/LogContext.cs new file mode 100644 index 00000000..963d63a1 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/LogContext.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.Http; + +namespace Microsoft.AspNet.Logging.Elm +{ + public class LogContext + { + public Guid RequestID { get; set; } + + public HostString Host { get; set; } + + public PathString Path { get; set; } + + public string ContentType { get; set; } + + public string Scheme { get; set; } + + public int StatusCode { get; set; } + + public string User { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/LogInfo.cs b/src/Microsoft.AspNet.Logging.Elm/LogInfo.cs new file mode 100644 index 00000000..bbe8395c --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/LogInfo.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Logging; + +namespace Microsoft.AspNet.Logging.Elm +{ + public class LogInfo + { + public LogContext Context { get; set; } + + public string Name { get; set; } + + public object State { get; set; } + + public Exception Exception { get; set; } + + public TraceType Severity { get; set; } + + public int EventID { get; set; } + + public DateTime Time { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cs b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cs new file mode 100644 index 00000000..cc9188b6 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cs @@ -0,0 +1,198 @@ +namespace Microsoft.AspNet.Logging.Elm.Views +{ +#line 1 "LogPage.cshtml" +using System + +#line default +#line hidden + ; +#line 2 "LogPage.cshtml" +using System.Globalization + +#line default +#line hidden + ; +#line 3 "LogPage.cshtml" +using System.Linq + +#line default +#line hidden + ; +#line 4 "LogPage.cshtml" +using Microsoft.AspNet.Logging.Elm.Views + +#line default +#line hidden + ; + using System.Threading.Tasks; + + public class LogPage : Microsoft.AspNet.Diagnostics.Views.BaseView + { +#line 7 "LogPage.cshtml" + + public LogPage(LogPageModel model) + { + Model = model; + } + + public LogPageModel Model { get; set; } + +#line default +#line hidden + #line hidden + public LogPage() + { + } + + #pragma warning disable 1998 + public override async Task ExecuteAsync() + { + WriteLiteral("\r\n"); + WriteLiteral(@" + + + + ELM + + + +

ELM

+ + + + + + + + + + + + + + +"); +#line 40 "LogPage.cshtml" + + +#line default +#line hidden + +#line 40 "LogPage.cshtml" + foreach (var log in Model.Logs) + { + +#line default +#line hidden + + WriteLiteral(" \r\n \r\n \r\n (log.Severity, 1141), false)); + WriteLiteral(">"); +#line 45 "LogPage.cshtml" + Write(log.Severity); + +#line default +#line hidden + WriteLiteral("\r\n \r\n \r\n \r\n \r\n \r\n \r\n"); +#line 52 "LogPage.cshtml" + } + +#line default +#line hidden + + WriteLiteral(" \r\n
DateTimeSeverityRequestHostCodeStateError
"); +#line 43 "LogPage.cshtml" + Write(string.Format("{0:MM/dd/yy}", log.Time)); + +#line default +#line hidden + WriteLiteral(""); +#line 44 "LogPage.cshtml" + Write(string.Format("{0:H:mm:ss}", log.Time)); + +#line default +#line hidden + WriteLiteral(""); +#line 46 "LogPage.cshtml" + Write(log.Context.Path); + +#line default +#line hidden + WriteLiteral(""); +#line 47 "LogPage.cshtml" + Write(log.Context.Host); + +#line default +#line hidden + WriteLiteral(""); +#line 48 "LogPage.cshtml" + Write(log.Context.StatusCode); + +#line default +#line hidden + WriteLiteral(""); +#line 49 "LogPage.cshtml" + Write(log.State); + +#line default +#line hidden + WriteLiteral(""); +#line 50 "LogPage.cshtml" + Write(log.Exception); + +#line default +#line hidden + WriteLiteral("
\r\n\r\n"); + } + #pragma warning restore 1998 + } +} diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cshtml b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cshtml new file mode 100644 index 00000000..69a0b0d0 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cshtml @@ -0,0 +1,56 @@ +@using System +@using System.Globalization +@using System.Linq +@using Microsoft.AspNet.Logging.Elm.Views + +@functions +{ + public LogPage(LogPageModel model) + { + Model = model; + } + + public LogPageModel Model { get; set; } +} + + + + + ELM + + + +

ELM

+ + + + + + + + + + + + + + + @foreach (var log in Model.Logs) + { + + + + + + + + + + + } + +
DateTimeSeverityRequestHostCodeStateError
@string.Format("{0:MM/dd/yy}", log.Time)@string.Format("{0:H:mm:ss}", log.Time)@log.Severity@log.Context.Path@log.Context.Host@log.Context.StatusCode@log.State@log.Exception
+ + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.css b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.css new file mode 100644 index 00000000..6a049a9f --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.css @@ -0,0 +1,47 @@ +body { + width: 800px; + font-family: monospace; + white-space: nowrap; +} + +table { + margin: 0px auto; + border-spacing: 1px; +} + +td { + padding: 4px; +} + +thead { + background-color: #B2F0E0; +} + +tr { + height: 23px; +} + +tr:nth-child(2n) { + background-color: #F5F5F5; +} + +.Critical { + background-color: red; + color: white; +} + +.Error { + color: red; +} + +.Information { + color: blue; +} + +.Verbose { + color: black; +} + +.Warning { + color: orange; +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/LogPageModel.cs b/src/Microsoft.AspNet.Logging.Elm/Views/LogPageModel.cs new file mode 100644 index 00000000..28814690 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/Views/LogPageModel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Microsoft.AspNet.Logging.Elm.Views +{ + public class LogPageModel + { + public IEnumerable Logs { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/project.json b/src/Microsoft.AspNet.Logging.Elm/project.json index b6587f13..d9e5b9c3 100644 --- a/src/Microsoft.AspNet.Logging.Elm/project.json +++ b/src/Microsoft.AspNet.Logging.Elm/project.json @@ -1,10 +1,12 @@ { "version": "1.0.0-*", "dependencies": { - "Microsoft.Framework.Logging.Interfaces": { "version": "1.0.0-*", "type": "build" }, + "Microsoft.AspNet.Diagnostics": "1.0.0-*", "Microsoft.AspNet.Http": "1.0.0-*", "Microsoft.AspNet.RequestContainer": "1.0.0-*", + "Microsoft.Framework.Logging.Interfaces": { "version": "1.0.0-*", "type": "build" }, "Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" } + }, "frameworks": { diff --git a/src/PageGenerator/PageGenerator.kproj b/src/PageGenerator/PageGenerator.kproj new file mode 100644 index 00000000..dc172378 --- /dev/null +++ b/src/PageGenerator/PageGenerator.kproj @@ -0,0 +1,20 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 4d4a785a-ecb9-4916-a88f-0fd306ee3b74 + Console + + + + + + + 2.0 + + + \ No newline at end of file diff --git a/src/PageGenerator/Program.cs b/src/PageGenerator/Program.cs new file mode 100644 index 00000000..c4b5048c --- /dev/null +++ b/src/PageGenerator/Program.cs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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 System.IO; +using System.Linq; +using Microsoft.AspNet.Razor; +using Microsoft.Framework.Runtime; + +namespace PageGenerator +{ + public class Program + { + private readonly ILibraryManager _libraryManager; + + public Program(ILibraryManager libraryManager) + { + _libraryManager = libraryManager; + } + + public void Main(string[] args) + { + var diagnosticsLibInfo = _libraryManager.GetLibraryInformation("Microsoft.AspNet.Logging.Elm"); + var viewBasePath = Path.Combine(Path.GetDirectoryName(diagnosticsLibInfo.Path), "Views"); + + Console.WriteLine("Generating code files for views in {0}", viewBasePath); + Console.WriteLine(); + + var cshtmlFiles = GetCshtmlFiles(viewBasePath); + + var fileCount = 0; + foreach (var fileName in cshtmlFiles) + { + Console.WriteLine(" Generating code file for view {0}...", Path.GetFileName(fileName)); + GenerateCodeFile(fileName); + Console.WriteLine(" Done!"); + fileCount++; + } + + Console.WriteLine(); + Console.WriteLine("{0} files successfully generated.", fileCount); + Console.WriteLine(); + + Console.Write("Press enter to close application..."); + Console.ReadLine(); + } + + private static IEnumerable GetCshtmlFiles(string path) + { + if (!Directory.Exists(path)) + { + throw new ArgumentException("path"); + } + + return Directory.EnumerateFiles(path, "*.cshtml"); + } + + private static void GenerateCodeFile(string cshtmlFilePath) + { + var basePath = Path.GetDirectoryName(cshtmlFilePath); + var fileName = Path.GetFileName(cshtmlFilePath); + var fileNameNoExtension = Path.GetFileNameWithoutExtension(fileName); + var codeLang = new CSharpRazorCodeLanguage(); + var host = new RazorEngineHost(codeLang); + host.DefaultBaseClass = "Microsoft.AspNet.Diagnostics.Views.BaseView"; + var engine = new RazorTemplateEngine(host); + + using (var fileStream = File.OpenText(cshtmlFilePath)) + { + var code = engine.GenerateCode( + input: fileStream, + className: fileNameNoExtension, + rootNamespace: "Microsoft.AspNet.Logging.Elm.Views", + sourceFileName: fileName); + + var source = code.GeneratedCode; + var startIndex = 0; + while (startIndex < source.Length) + { + var startMatch = @"<%$ include: "; + var endMatch = @" %>"; + startIndex = source.IndexOf(startMatch, startIndex); + if (startIndex == -1) + { + break; + } + var endIndex = source.IndexOf(endMatch, startIndex); + if (endIndex == -1) + { + break; + } + var includeFileName = source.Substring(startIndex + startMatch.Length, endIndex - (startIndex + startMatch.Length)); + includeFileName = SanitizeFileName(includeFileName); + Console.WriteLine(" Inlining file {0}", includeFileName); + var replacement = File.ReadAllText(Path.Combine(basePath, includeFileName)).Replace("\"", "\\\""); + source = source.Substring(0, startIndex) + replacement + source.Substring(endIndex + endMatch.Length); + startIndex = startIndex + replacement.Length; + } + + File.WriteAllText(Path.Combine(basePath, string.Format("{0}.cs", fileNameNoExtension)), source); + } + } + + private static string SanitizeFileName(string fileName) + { + // The Razor generated code sometimes splits strings across multiple lines + // which can hit the include file name, so we need to strip out the non-filename chars. + //ErrorPage.j" + + //"s + + var invalidChars = new List(Path.GetInvalidFileNameChars()); + invalidChars.Add('+'); + invalidChars.Add(' '); + + return string.Join(string.Empty, fileName.Where(c => !invalidChars.Contains(c)).ToArray()); + } + } +} diff --git a/src/PageGenerator/project.json b/src/PageGenerator/project.json new file mode 100644 index 00000000..db68833d --- /dev/null +++ b/src/PageGenerator/project.json @@ -0,0 +1,18 @@ +{ + "version": "1.0.0-*", + "dependencies": { + "Microsoft.AspNet.Logging.Elm": "1.0.0-*", + "Microsoft.AspNet.Diagnostics": "1.0.0-*", + "Microsoft.AspNet.Razor": "4.0.0-*", + "Microsoft.Framework.Runtime.Interfaces": "1.0.0-*" + }, + + "frameworks": { + "aspnet50": { }, + "aspnetcore50": { + "dependencies": { + "System.Console": "4.0.0-beta-*" + } + } + } +} From 208ed6b46ee57a6186c5554b06454cd686e3793d Mon Sep 17 00:00:00 2001 From: SonjaKhan Date: Thu, 16 Oct 2014 17:42:01 -0700 Subject: [PATCH 3/3] lots of changes to elm --- samples/ElmSampleApp/Startup.cs | 27 +- samples/ElmSampleApp/project.json | 7 +- src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs | 70 +-- .../ElmLoggerProvider.cs | 7 +- .../ElmMiddleware.cs | 87 +++- .../ElmOptions.cs | 16 + src/Microsoft.AspNet.Logging.Elm/ElmScope.cs | 53 ++ src/Microsoft.AspNet.Logging.Elm/ElmStore.cs | 15 +- src/Microsoft.AspNet.Logging.Elm/IElmStore.cs | 4 +- .../LogContext.cs | 15 +- src/Microsoft.AspNet.Logging.Elm/LogInfo.cs | 3 + .../Views/LogPage.cs | 344 +++++++++++-- .../Views/LogPage.cshtml | 133 ++++- .../Views/LogPage.css | 70 ++- .../Views/LogPageModel.cs | 2 + .../Views/RequestPage.cs | 483 ++++++++++++++++++ .../Views/RequestPage.cshtml | 164 ++++++ .../Views/RequestPage.css | 79 +++ .../Views/RequestPageModel.cs | 14 + src/Microsoft.AspNet.Logging.Elm/project.json | 1 - 20 files changed, 1450 insertions(+), 144 deletions(-) create mode 100644 src/Microsoft.AspNet.Logging.Elm/ElmScope.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.cs create mode 100644 src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.cshtml create mode 100644 src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.css create mode 100644 src/Microsoft.AspNet.Logging.Elm/Views/RequestPageModel.cs diff --git a/samples/ElmSampleApp/Startup.cs b/samples/ElmSampleApp/Startup.cs index 1df8aa50..36ce8abc 100644 --- a/samples/ElmSampleApp/Startup.cs +++ b/samples/ElmSampleApp/Startup.cs @@ -11,30 +11,33 @@ public class Startup { public void ConfigureServices(IServiceCollection services) { - services.AddTransient(); // registering the service so it can be injected into constructors + services.AddSingleton(); // registering the service so it can be injected into constructors services.Configure(options => { options.Path = new PathString("/foo"); }); + services.AddMvc(); } public void Configure(IApplicationBuilder app, ILoggerFactory factory) { + app.UseErrorPage(); app.UseElm(); - + app.UseMvc(); +#pragma warning disable CS1998 app.Run(async context => +#pragma warning restore CS1998 { - context.Response.ContentType = "text/plain"; - await context.Response.WriteAsync("Hello world"); - - var logger = factory.Create("HelloWorld"); - context.Response.StatusCode = 200; - logger.WriteInformation("Hello"); - context.Response.StatusCode = 400; - logger.WriteCritical("World", new Exception("Bad Request")); - context.Response.StatusCode = 404; - logger.WriteWarning("Not Found", new Exception("Existential Crisis")); + throw new InvalidOperationException(); }); } } + + public class HomeController + { + public string Index() + { + return "Hello World"; + } + } } diff --git a/samples/ElmSampleApp/project.json b/samples/ElmSampleApp/project.json index 20b1efe2..c2cf91d1 100644 --- a/samples/ElmSampleApp/project.json +++ b/samples/ElmSampleApp/project.json @@ -2,11 +2,12 @@ "webroot": "wwwroot", "exclude": "wwwroot/**/*.*", "dependencies": { - "Microsoft.AspNet.Server.IIS": "1.0.0-*", "Microsoft.AspNet.Diagnostics": "1.0.0-*", - "Microsoft.AspNet.PipelineCore": "1.0.0-*", "Microsoft.AspNet.Http": "1.0.0-*", - "Microsoft.AspNet.Logging.Elm": "1.0.0-*" + "Microsoft.AspNet.Logging.Elm": "1.0.0-*", + "Microsoft.AspNet.Mvc": "6.0.0-*", + "Microsoft.AspNet.PipelineCore": "1.0.0-*", + "Microsoft.AspNet.Server.IIS": "1.0.0-*" }, "frameworks" : { "aspnet50" : { }, diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs b/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs index b8a4488c..dabfa3de 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs @@ -2,9 +2,12 @@ // 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 System.Threading; using Microsoft.AspNet.Http; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; +using Microsoft.Framework.Logging.Elm; namespace Microsoft.AspNet.Logging.Elm { @@ -15,39 +18,23 @@ public class ElmLogger : ILogger private IElmStore _store; private readonly IContextAccessor _contextAccessor; private readonly object _requestIdentifierKey; + private readonly object _logContextKey; - public ElmLogger(string name, ElmLoggerProvider provider, IElmStore store, IContextAccessor contextAccessor, object requestIdentifierKey) + public ElmLogger(string name, ElmLoggerProvider provider, IElmStore store, + IContextAccessor contextAccessor, + object requestIdentifierKey, object logContextKey) { _name = name; _provider = provider; _store = store; _contextAccessor = contextAccessor; _requestIdentifierKey = requestIdentifierKey; + _logContextKey = logContextKey; } - public void Write(TraceType traceType, int eventId, object state, Exception exception, Func formatter) + public void Write(TraceType traceType, int eventId, object state, Exception exception, + Func formatter) { - var message = string.Empty; - if (formatter != null) - { - message = formatter(state, exception); - } - else - { - if (state != null) - { - message += state; - } - if (exception != null) - { - message += Environment.NewLine + exception; - } - } - if (string.IsNullOrEmpty(message)) - { - return; - } - LogInfo info = new LogInfo() { Context = GetLogContext(), @@ -58,7 +45,17 @@ public void Write(TraceType traceType, int eventId, object state, Exception exce State = state, Time = DateTime.Now }; - _store.Write(info); + if (ElmScope.Counts.ContainsKey(GetLogContext().RequestID)) + { + // TODO: display nested scopes nicely + for (var i = 0; i < ElmScope.Counts[GetLogContext().RequestID].Count; i++) + { + state = "-----" + state; + } + info.State = state; + info.Scopes = new List(ElmScope.Counts[GetLogContext().RequestID]); + } + _store.Add(info); } public bool IsEnabled(TraceType traceType) @@ -68,8 +65,7 @@ public bool IsEnabled(TraceType traceType) public IDisposable BeginScope(object state) { - // TODO: use NullDisposable once it's moved to this repo #33 - return null; + return new ElmScope(this, state, GetLogContext().RequestID); } private LogContext GetLogContext() @@ -77,11 +73,17 @@ private LogContext GetLogContext() var context = _contextAccessor.Value; if (context == null) { - return new LogContext(); + // TODO: group non-request logs by Thread ID + return new LogContext() + { + ThreadID = Thread.CurrentThread.ManagedThreadId + }; } - else + + var logContext = context.Items[_logContextKey] as LogContext; + if (logContext == null) { - return new LogContext() + logContext = new LogContext() { RequestID = (Guid)context.Items[_requestIdentifierKey], Host = context.Request.Host, @@ -89,9 +91,17 @@ private LogContext GetLogContext() Path = context.Request.Path, Scheme = context.Request.Scheme, StatusCode = context.Response.StatusCode, - User = context.User.Identity.Name + User = context.User, + Method = context.Request.Method, + Protocol = context.Request.Protocol, + Headers = context.Request.Headers, + Query = context.Request.QueryString, + Cookies = context.Request.Cookies }; + context.Items[_logContextKey] = logContext; } + + return logContext; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs b/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs index 21ed5aa2..f9cafebf 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs @@ -12,17 +12,20 @@ public class ElmLoggerProvider : ILoggerProvider private readonly IElmStore _store; private readonly IContextAccessor _contextAccessor; private readonly object _requestIdentifierKey; + private readonly object _logContextKey; - public ElmLoggerProvider(IElmStore store, IContextAccessor contextAccessor, object requestIdentifierKey) + public ElmLoggerProvider(IElmStore store, IContextAccessor contextAccessor, + object requestIdentifierKey, object logContextKey) { _store = store; _contextAccessor = contextAccessor; _requestIdentifierKey = requestIdentifierKey; + _logContextKey = logContextKey; } public ILogger Create(string name) { - return new ElmLogger(name, this, _store, _contextAccessor, _requestIdentifierKey); + return new ElmLogger(name, this, _store, _contextAccessor, _requestIdentifierKey, _logContextKey); } } } diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs b/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs index 1ae30857..dd9f88df 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs @@ -2,6 +2,8 @@ // 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 System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; @@ -21,8 +23,10 @@ public class ElmMiddleware private readonly ElmOptions _options; private readonly ElmLoggerProvider _provider; private readonly IElmStore _store; + private readonly ILogger _logger; private IContextAccessor _contextAccessor; - private static readonly object _requestIdentifier = new object(); + private static readonly object _requestIdentifierKey = new object(); + private static readonly object _logContextKey = new object(); public ElmMiddleware( RequestDelegate next, ILoggerFactory factory, IOptions options, @@ -31,23 +35,86 @@ public ElmMiddleware( _next = next; _options = options.Options; _store = store; + _logger = factory.Create(); _contextAccessor = contextAccessor; - _provider = new ElmLoggerProvider(_store, contextAccessor, _requestIdentifier); + _provider = new ElmLoggerProvider(_store, contextAccessor, _requestIdentifierKey, _logContextKey); factory.AddProvider(_provider); + // non-request logs + _logger.WriteWarning("hello world"); + _logger.WriteCritical("critical: aliens approaching"); } public async Task Invoke(HttpContext context) { - _contextAccessor.SetContextSource(() => context, null); - context.Items[_requestIdentifier] = Guid.NewGuid(); - if (!context.Request.Path.Value.Equals(_options.Path.Value ?? "/Elm")) + context.Items[_requestIdentifierKey] = Guid.NewGuid(); + if (context.Request.Path != _options.Path && !context.Request.Path.StartsWithSegments(_options.Path)) { - await _next(context); - return; + try + { + await _next(context); + } + catch (Exception ex) + { + _contextAccessor.SetContextSource(() => context, null); + _logger.WriteError("An unhandled exception has occurred: " + ex.Message, ex); + throw; + } + } + + // parse params + var logs = (IEnumerable)null; + if (context.Request.Query.ContainsKey("level")) + { + var minLevel = (TraceType)int.Parse(context.Request.Query.GetValues("level")[0]); + logs = _store.GetLogs(minLevel); + _options.MinLevel = minLevel; + } + else + { + logs = _store.GetLogs(); + _options.MinLevel = TraceType.Verbose; + } + if (context.Request.Query.ContainsKey("name")) + { + var namePrefix = context.Request.Query.GetValues("name")[0]; + logs = logs.Where(l => l.Name.StartsWith(namePrefix)); + _options.NamePrefix = namePrefix; + } + + // main log page + if (context.Request.Path == _options.Path) + { + var model = new LogPageModel() + { + // sort so most recent logs are first + Logs = logs.OrderBy(l => l.Time).Reverse(), + Options = _options + }; + var logPage = new LogPage(model); + await logPage.ExecuteAsync(context); + } + // request details page + else + { + try + { + var parts = context.Request.Path.Value.Split('/'); + var id = Guid.Parse(parts[parts.Length - 1]); + var requestLogs = logs.Where(l => l.Context.RequestID == id); + var model = new RequestPageModel() + { + RequestID = id, + Logs = requestLogs, + Options = _options + }; + var requestPage = new RequestPage(model); + await requestPage.ExecuteAsync(context); + } + catch (Exception) + { + // TODO: bad url + } } - var model = new LogPageModel() { Logs = _store.GetLogs() }; - var logPage = new LogPage(model); - await logPage.ExecuteAsync(context); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs b/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs index 6158b1a7..c18c219b 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNet.Http; +using Microsoft.Framework.Logging; namespace Microsoft.AspNet.Logging.Elm { @@ -10,9 +11,24 @@ namespace Microsoft.AspNet.Logging.Elm /// public class ElmOptions { + public ElmOptions() + { + Path = new PathString("/Elm"); + } + /// /// Specifies the path to view the logs /// public PathString Path { get; set; } + + /// + /// The minimum severity level shown + /// + public TraceType MinLevel { get; set; } + + /// + /// prefix filter for the loggers shown + /// + public string NamePrefix { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmScope.cs b/src/Microsoft.AspNet.Logging.Elm/ElmScope.cs new file mode 100644 index 00000000..ff1a028b --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/ElmScope.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Framework.Logging; + +namespace Microsoft.Framework.Logging.Elm +{ + public class ElmScope : IDisposable + { + private bool _isDisposed; + private readonly Stopwatch _stopwatch; + private readonly ILogger _logger; + private readonly object _state; + private readonly Guid _request; + private readonly Guid _id; + + // Maps a request id to a list of Guids representing each scope within that request + public static IDictionary> Counts = new Dictionary>(); + private readonly object _lock = new object(); + + public ElmScope(ILogger logger, object state, Guid request) + { + _logger = logger; + _state = state; + _request = request; + _id = Guid.NewGuid(); + _stopwatch = Stopwatch.StartNew(); + lock (_lock) + { + if (!Counts.ContainsKey(_request)) + { + Counts.Add(_request, new List()); + } + Counts[_request].Add(_id); + } + _logger.WriteInformation(string.Format("Begin {0}", _state)); + } + + public void Dispose() + { + if (!_isDisposed) + { + _stopwatch.Stop(); + _logger.WriteInformation(string.Format("Completed {0} in {1}ms", _state, _stopwatch.ElapsedMilliseconds)); + lock (_lock) + { + Counts[_request].Remove(_id); + } + _isDisposed = true; + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/ElmStore.cs b/src/Microsoft.AspNet.Logging.Elm/ElmStore.cs index e176697b..c9cd3cbd 100644 --- a/src/Microsoft.AspNet.Logging.Elm/ElmStore.cs +++ b/src/Microsoft.AspNet.Logging.Elm/ElmStore.cs @@ -2,21 +2,28 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Linq; +using Microsoft.Framework.Logging; namespace Microsoft.AspNet.Logging.Elm { public class ElmStore : IElmStore { - private static List Log { get; set; } = new List(); + private readonly List _logs = new List(); public IEnumerable GetLogs() { - return Log; + return _logs; } - public void Write(LogInfo info) + public IEnumerable GetLogs(TraceType minLevel) { - Log.Add(info); + return _logs.Where(l => l.Severity >= minLevel); + } + + public void Add(LogInfo info) + { + _logs.Add(info); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/IElmStore.cs b/src/Microsoft.AspNet.Logging.Elm/IElmStore.cs index dfe347d9..004305f1 100644 --- a/src/Microsoft.AspNet.Logging.Elm/IElmStore.cs +++ b/src/Microsoft.AspNet.Logging.Elm/IElmStore.cs @@ -2,12 +2,14 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using Microsoft.Framework.Logging; namespace Microsoft.AspNet.Logging.Elm { public interface IElmStore { - void Write(LogInfo info); + void Add(LogInfo info); IEnumerable GetLogs(); + IEnumerable GetLogs(TraceType minLevel); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/LogContext.cs b/src/Microsoft.AspNet.Logging.Elm/LogContext.cs index 963d63a1..d1bd835d 100644 --- a/src/Microsoft.AspNet.Logging.Elm/LogContext.cs +++ b/src/Microsoft.AspNet.Logging.Elm/LogContext.cs @@ -2,12 +2,15 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Security.Claims; using Microsoft.AspNet.Http; namespace Microsoft.AspNet.Logging.Elm { public class LogContext { + public int ThreadID { get; set; } + public Guid RequestID { get; set; } public HostString Host { get; set; } @@ -20,6 +23,16 @@ public class LogContext public int StatusCode { get; set; } - public string User { get; set; } + public ClaimsPrincipal User { get; set; } + + public string Method { get; set; } + + public string Protocol { get; set; } + + public IHeaderDictionary Headers { get; set; } + + public QueryString Query { get; set; } + + public IReadableStringCollection Cookies { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/LogInfo.cs b/src/Microsoft.AspNet.Logging.Elm/LogInfo.cs index bbe8395c..fb34b0e5 100644 --- a/src/Microsoft.AspNet.Logging.Elm/LogInfo.cs +++ b/src/Microsoft.AspNet.Logging.Elm/LogInfo.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.Collections.Generic; using Microsoft.Framework.Logging; namespace Microsoft.AspNet.Logging.Elm @@ -21,5 +22,7 @@ public class LogInfo public int EventID { get; set; } public DateTime Time { get; set; } + + public IList Scopes { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cs b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cs index cc9188b6..bc7a8114 100644 --- a/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cs +++ b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.cs @@ -21,6 +21,12 @@ namespace Microsoft.AspNet.Logging.Elm.Views #line 4 "LogPage.cshtml" using Microsoft.AspNet.Logging.Elm.Views +#line default +#line hidden + ; +#line 5 "LogPage.cshtml" +using Microsoft.Framework.Logging + #line default #line hidden ; @@ -28,7 +34,7 @@ namespace Microsoft.AspNet.Logging.Elm.Views public class LogPage : Microsoft.AspNet.Diagnostics.Views.BaseView { -#line 7 "LogPage.cshtml" +#line 8 "LogPage.cshtml" public LogPage(LogPageModel model) { @@ -53,24 +59,45 @@ public override async Task ExecuteAsync() ELM +

ELM

- - + + + + + + +
+ - - - - - - - - + + + + - - @foreach (var log in Model.Logs) - { - - - - - - - - - + + + + + + + @foreach (var logs in Model.Logs.GroupBy(g => g.Context)) + { + + + @{ + var requestPath = Model.Options.Path.Value + "/" + logs.Key.RequestID; + } + + + + - } - + + }
DateTimeSeverityRequestHostCodeStateErrorPathHostStatus CodeLogs
@string.Format("{0:MM/dd/yy}", log.Time)@string.Format("{0:H:mm:ss}", log.Time)@log.Severity@log.Context.Path@log.Context.Host@log.Context.StatusCode@log.State@log.Exception
@logs.Key.Path@logs.Key.Host@logs.Key.StatusCode + + + + + + + + + + + + + @* logs are chronologically ordered within each request *@ + @foreach (var log in logs.Reverse()) + { + var scopes = ""; + if (log.Scopes != null) + { + // classes cannot begin with a number, prepend an underscore + scopes = string.Join(" _", log.Scopes); + } + var logState = string.Format("{0} _{1}", "logState", scopes); + + + + + + + + + } + +
DateTimeNameSeverityStateError
@string.Format("{0:MM/dd/yy}", log.Time)@string.Format("{0:H:mm:ss}", log.Time)@log.Name@log.Severity@log.State@log.Exception
+
+ \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.css b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.css index 6a049a9f..eea8f021 100644 --- a/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.css +++ b/src/Microsoft.AspNet.Logging.Elm/Views/LogPage.css @@ -1,20 +1,40 @@ body { - width: 800px; - font-family: monospace; + font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif; + font-size: .813em; + line-height: 1.4em; white-space: nowrap; + margin: 10px; +} + +col:nth-child(2) { + background-color: #FAFAFA; +} + +h1 { + font-family: Arial, Helvetica, sans-serif; + font-size: 2em; } table { margin: 0px auto; - border-spacing: 1px; + border-spacing: 0px; + table-layout: fixed; + width: 100%; + border-collapse: collapse; } td { + text-overflow: ellipsis; + overflow: hidden; +} + +td, th { padding: 4px; } thead { - background-color: #B2F0E0; + font-size: 1em; + font-family: Arial; } tr { @@ -22,7 +42,47 @@ tr { } tr:nth-child(2n) { - background-color: #F5F5F5; + background-color: #F6F6F6; +} + +#requestHeader { + border-bottom: solid 1px gray; + border-top: solid 1px gray; + margin-bottom: 2px; + font-size: 1em; + line-height: 2em; +} + +.date, .time { + width: 70px; +} + +.logHeader { + border-bottom: 1px solid lightgray; + color: gray; + text-align: left; +} + +.logState { + text-overflow: ellipsis; + overflow: hidden; +} + +.logTd { + border-left: 1px solid gray; + padding: 0px; +} + +.logs { + width: 80%; +} + +.requestRow>td { + border-bottom: solid 1px gray; +} + +.severity { + width: 80px; } .Critical { diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/LogPageModel.cs b/src/Microsoft.AspNet.Logging.Elm/Views/LogPageModel.cs index 28814690..2aa84bc4 100644 --- a/src/Microsoft.AspNet.Logging.Elm/Views/LogPageModel.cs +++ b/src/Microsoft.AspNet.Logging.Elm/Views/LogPageModel.cs @@ -5,5 +5,7 @@ namespace Microsoft.AspNet.Logging.Elm.Views public class LogPageModel { public IEnumerable Logs { get; set; } + + public ElmOptions Options { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.cs b/src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.cs new file mode 100644 index 00000000..b07b4320 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.cs @@ -0,0 +1,483 @@ +namespace Microsoft.AspNet.Logging.Elm.Views +{ +#line 1 "RequestPage.cshtml" +using System + +#line default +#line hidden + ; +#line 2 "RequestPage.cshtml" +using System.Globalization + +#line default +#line hidden + ; +#line 3 "RequestPage.cshtml" +using System.Linq + +#line default +#line hidden + ; +#line 4 "RequestPage.cshtml" +using Microsoft.AspNet.Logging.Elm + +#line default +#line hidden + ; +#line 5 "RequestPage.cshtml" +using Microsoft.AspNet.Logging.Elm.Views + +#line default +#line hidden + ; +#line 6 "RequestPage.cshtml" +using Microsoft.Framework.Logging + +#line default +#line hidden + ; + using System.Threading.Tasks; + + public class RequestPage : Microsoft.AspNet.Diagnostics.Views.BaseView + { +#line 9 "RequestPage.cshtml" + + public RequestPage(RequestPageModel model) + { + Model = model; + } + + public RequestPageModel Model { get; set; } + +#line default +#line hidden + #line hidden + public RequestPage() + { + } + + #pragma warning disable 1998 + public override async Task ExecuteAsync() + { + WriteLiteral("\r\n"); + WriteLiteral(@" + + + + ELM + + + +

ELM

+ +

Request Details

+ + +"); +#line 32 "RequestPage.cshtml" + + +#line default +#line hidden + +#line 32 "RequestPage.cshtml" + + var context = Model.Logs.First().Context; + + +#line default +#line hidden + + WriteLiteral("\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n " + +" \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n "); +#line 53 "RequestPage.cshtml" + Write(context.Protocol); + +#line default +#line hidden + WriteLiteral(@" + + + + \r\n " + +" \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n " + +" \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n"); +#line 97 "RequestPage.cshtml" + + +#line default +#line hidden + +#line 97 "RequestPage.cshtml" + if (context.Cookies.Any()) + { + +#line default +#line hidden + + WriteLiteral(@"
Path"); +#line 37 "RequestPage.cshtml" + Write(context.Path); + +#line default +#line hidden + WriteLiteral("
Host"); +#line 41 "RequestPage.cshtml" + Write(context.Host); + +#line default +#line hidden + WriteLiteral("
Content Type"); +#line 45 "RequestPage.cshtml" + Write(context.ContentType); + +#line default +#line hidden + WriteLiteral("
Method" + +""); +#line 49 "RequestPage.cshtml" + Write(context.Method); + +#line default +#line hidden + WriteLiteral("
Protocol
Headers + + + + + + + + +"); +#line 66 "RequestPage.cshtml" + + +#line default +#line hidden + +#line 66 "RequestPage.cshtml" + foreach (var header in context.Headers) + { + +#line default +#line hidden + + WriteLiteral(" \r\n \r\n \r\n \r\n"); +#line 72 "RequestPage.cshtml" + } + +#line default +#line hidden + + WriteLiteral(" \r\n
VariableValue
"); +#line 69 "RequestPage.cshtml" + Write(header.Key); + +#line default +#line hidden + WriteLiteral(""); +#line 70 "RequestPage.cshtml" + Write(string.Join(";", header.Value)); + +#line default +#line hidden + WriteLiteral("
\r\n
Status Code"); +#line 79 "RequestPage.cshtml" + Write(context.StatusCode); + +#line default +#line hidden + WriteLiteral("
User"); +#line 84 "RequestPage.cshtml" + Write(context.User.Identity.Name); + +#line default +#line hidden + WriteLiteral("
Scheme" + +""); +#line 88 "RequestPage.cshtml" + Write(context.Scheme); + +#line default +#line hidden + WriteLiteral("
Query"); +#line 92 "RequestPage.cshtml" + Write(context.Query.Value); + +#line default +#line hidden + WriteLiteral("
Cookies
+ + + + + + + +"); +#line 107 "RequestPage.cshtml" + + +#line default +#line hidden + +#line 107 "RequestPage.cshtml" + foreach (var cookie in context.Cookies) + { + +#line default +#line hidden + + WriteLiteral(" \r\n \r\n \r\n \r\n"); +#line 113 "RequestPage.cshtml" + } + +#line default +#line hidden + + WriteLiteral(" \r\n
VariableValue
"); +#line 110 "RequestPage.cshtml" + Write(cookie.Key); + +#line default +#line hidden + WriteLiteral(""); +#line 111 "RequestPage.cshtml" + Write(string.Join(";", cookie.Value)); + +#line default +#line hidden + WriteLiteral("
\r\n"); +#line 116 "RequestPage.cshtml" + } + +#line default +#line hidden + + WriteLiteral(" \r\n \r\n \r\n\r\n

Logs

\r\n
\r\n \r\n (Model.Options.NamePrefix, 3938), false)); + WriteLiteral(@" /> + +
+ + + + + + + + + + + +"); +#line 151 "RequestPage.cshtml" + + +#line default +#line hidden + +#line 151 "RequestPage.cshtml" + foreach (var log in Model.Logs) + { + +#line default +#line hidden + + WriteLiteral(" \r\n \r\n \r\n (log.Severity, 4548), false)); + WriteLiteral(">"); +#line 156 "RequestPage.cshtml" + Write(log.Severity); + +#line default +#line hidden + WriteLiteral("\r\n (log.Name, 4610), false)); + WriteLiteral(">"); +#line 157 "RequestPage.cshtml" + Write(log.Name); + +#line default +#line hidden + WriteLiteral("\r\n (log.State, 4664), false)); + WriteLiteral(" class=\"logState\" width=\"100px\">"); +#line 158 "RequestPage.cshtml" + Write(log.State); + +#line default +#line hidden + WriteLiteral("\r\n (log.Exception, 4751), false)); + WriteLiteral(">"); +#line 159 "RequestPage.cshtml" + Write(log.Exception); + +#line default +#line hidden + WriteLiteral("\r\n \r\n"); +#line 161 "RequestPage.cshtml" + } + +#line default +#line hidden + + WriteLiteral("
DateTimeSeverityNameStateError
"); +#line 154 "RequestPage.cshtml" + Write(string.Format("{0:MM/dd/yy}", log.Time)); + +#line default +#line hidden + WriteLiteral(""); +#line 155 "RequestPage.cshtml" + Write(string.Format("{0:H:mm:ss}", log.Time)); + +#line default +#line hidden + WriteLiteral("
\r\n\r\n"); + } + #pragma warning restore 1998 + } +} diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.cshtml b/src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.cshtml new file mode 100644 index 00000000..83500678 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.cshtml @@ -0,0 +1,164 @@ +@using System +@using System.Globalization +@using System.Linq +@using Microsoft.AspNet.Logging.Elm +@using Microsoft.AspNet.Logging.Elm.Views +@using Microsoft.Framework.Logging + +@functions +{ + public RequestPage(RequestPageModel model) + { + Model = model; + } + + public RequestPageModel Model { get; set; } +} + + + + + ELM + + + +

ELM

+ +

Request Details

+ + + @{ + var context = Model.Logs.First().Context; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @* TODO: show more information about the user*@ + + + + + + + + + + + + + + +
Path@context.Path
Host@context.Host
Content Type@context.ContentType
Method@context.Method
Protocol@context.Protocol
Headers + + + + + + + + + @foreach (var header in context.Headers) + { + + + + + } + +
VariableValue
@header.Key@string.Join(";", header.Value)
+
Status Code@context.StatusCode
User@context.User.Identity.Name
Scheme@context.Scheme
Query@context.Query.Value
Cookies + @if (context.Cookies.Any()) + { + + + + + + + + + @foreach (var cookie in context.Cookies) + { + + + + + } + +
VariableValue
@cookie.Key@string.Join(";", cookie.Value)
+ } +
+ +

Logs

+
+ + + +
+ + + + + + + + + + + + @foreach (var log in Model.Logs) + { + + + + + + + + + } +
DateTimeSeverityNameStateError
@string.Format("{0:MM/dd/yy}", log.Time)@string.Format("{0:H:mm:ss}", log.Time)@log.Severity@log.Name@log.State@log.Exception
+ + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.css b/src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.css new file mode 100644 index 00000000..7f5abbea --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/Views/RequestPage.css @@ -0,0 +1,79 @@ +body { + font-family: 'Segoe UI', Tahoma, Arial, Helvtica, sans-serif; + font-size: 0.9em; + line-height: 1.4em; + width: 90%; + margin: 0px auto; +} + +h1, h2 { + font-weight: normal; +} + +table { + border-spacing: 0px; + width: 100%; + border-collapse: collapse; + border: 1px solid black; + white-space: pre-wrap; +} + +td { + text-overflow: ellipsis; + overflow: hidden; +} + +th { + font-family: Arial; +} + +td, th { + padding: 8px; +} + +tr:nth-child(2n) { + background-color: #F6F6F6; +} + +#headerTable { + border: none; + height: 100%; +} + +#headerTd { + white-space: normal; +} + +#label { + width: 20%; + border-right: 1px solid black; +} + +#logs>tbody>tr>td { + border-right: 1px dashed lightgray; +} + +#logs>thead>tr>th { + border: 1px solid black; +} + +.Critical { + background-color: red; + color: white; +} + +.Error { + color: red; +} + +.Information { + color: blue; +} + +.Verbose { + color: black; +} + +.Warning { + color: orange; +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/Views/RequestPageModel.cs b/src/Microsoft.AspNet.Logging.Elm/Views/RequestPageModel.cs new file mode 100644 index 00000000..fcdea619 --- /dev/null +++ b/src/Microsoft.AspNet.Logging.Elm/Views/RequestPageModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace Microsoft.AspNet.Logging.Elm.Views +{ + public class RequestPageModel + { + public Guid RequestID { get; set; } + + public IEnumerable Logs { get; set; } + + public ElmOptions Options { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Logging.Elm/project.json b/src/Microsoft.AspNet.Logging.Elm/project.json index d9e5b9c3..d2c3640b 100644 --- a/src/Microsoft.AspNet.Logging.Elm/project.json +++ b/src/Microsoft.AspNet.Logging.Elm/project.json @@ -6,7 +6,6 @@ "Microsoft.AspNet.RequestContainer": "1.0.0-*", "Microsoft.Framework.Logging.Interfaces": { "version": "1.0.0-*", "type": "build" }, "Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" } - }, "frameworks": {