Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Commit 208ed6b

Browse files
committed
lots of changes to elm
1 parent 5b8b866 commit 208ed6b

20 files changed

+1450
-144
lines changed

samples/ElmSampleApp/Startup.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,33 @@ public class Startup
1111
{
1212
public void ConfigureServices(IServiceCollection services)
1313
{
14-
services.AddTransient<IElmStore, ElmStore>(); // registering the service so it can be injected into constructors
14+
services.AddSingleton<IElmStore, ElmStore>(); // registering the service so it can be injected into constructors
1515
services.Configure<ElmOptions>(options =>
1616
{
1717
options.Path = new PathString("/foo");
1818
});
19+
services.AddMvc();
1920
}
2021

2122
public void Configure(IApplicationBuilder app, ILoggerFactory factory)
2223
{
24+
app.UseErrorPage();
2325
app.UseElm();
24-
26+
app.UseMvc();
27+
#pragma warning disable CS1998
2528
app.Run(async context =>
29+
#pragma warning restore CS1998
2630
{
27-
context.Response.ContentType = "text/plain";
28-
await context.Response.WriteAsync("Hello world");
29-
30-
var logger = factory.Create("HelloWorld");
31-
context.Response.StatusCode = 200;
32-
logger.WriteInformation("Hello");
33-
context.Response.StatusCode = 400;
34-
logger.WriteCritical("World", new Exception("Bad Request"));
35-
context.Response.StatusCode = 404;
36-
logger.WriteWarning("Not Found", new Exception("Existential Crisis"));
31+
throw new InvalidOperationException();
3732
});
3833
}
3934
}
35+
36+
public class HomeController
37+
{
38+
public string Index()
39+
{
40+
return "Hello World";
41+
}
42+
}
4043
}

samples/ElmSampleApp/project.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
"webroot": "wwwroot",
33
"exclude": "wwwroot/**/*.*",
44
"dependencies": {
5-
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
65
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
7-
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
86
"Microsoft.AspNet.Http": "1.0.0-*",
9-
"Microsoft.AspNet.Logging.Elm": "1.0.0-*"
7+
"Microsoft.AspNet.Logging.Elm": "1.0.0-*",
8+
"Microsoft.AspNet.Mvc": "6.0.0-*",
9+
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
10+
"Microsoft.AspNet.Server.IIS": "1.0.0-*"
1011
},
1112
"frameworks" : {
1213
"aspnet50" : { },

src/Microsoft.AspNet.Logging.Elm/ElmLogger.cs

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Collections.Generic;
6+
using System.Threading;
57
using Microsoft.AspNet.Http;
68
using Microsoft.Framework.DependencyInjection;
79
using Microsoft.Framework.Logging;
10+
using Microsoft.Framework.Logging.Elm;
811

912
namespace Microsoft.AspNet.Logging.Elm
1013
{
@@ -15,39 +18,23 @@ public class ElmLogger : ILogger
1518
private IElmStore _store;
1619
private readonly IContextAccessor<HttpContext> _contextAccessor;
1720
private readonly object _requestIdentifierKey;
21+
private readonly object _logContextKey;
1822

19-
public ElmLogger(string name, ElmLoggerProvider provider, IElmStore store, IContextAccessor<HttpContext> contextAccessor, object requestIdentifierKey)
23+
public ElmLogger(string name, ElmLoggerProvider provider, IElmStore store,
24+
IContextAccessor<HttpContext> contextAccessor,
25+
object requestIdentifierKey, object logContextKey)
2026
{
2127
_name = name;
2228
_provider = provider;
2329
_store = store;
2430
_contextAccessor = contextAccessor;
2531
_requestIdentifierKey = requestIdentifierKey;
32+
_logContextKey = logContextKey;
2633
}
2734

28-
public void Write(TraceType traceType, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
35+
public void Write(TraceType traceType, int eventId, object state, Exception exception,
36+
Func<object, Exception, string> formatter)
2937
{
30-
var message = string.Empty;
31-
if (formatter != null)
32-
{
33-
message = formatter(state, exception);
34-
}
35-
else
36-
{
37-
if (state != null)
38-
{
39-
message += state;
40-
}
41-
if (exception != null)
42-
{
43-
message += Environment.NewLine + exception;
44-
}
45-
}
46-
if (string.IsNullOrEmpty(message))
47-
{
48-
return;
49-
}
50-
5138
LogInfo info = new LogInfo()
5239
{
5340
Context = GetLogContext(),
@@ -58,7 +45,17 @@ public void Write(TraceType traceType, int eventId, object state, Exception exce
5845
State = state,
5946
Time = DateTime.Now
6047
};
61-
_store.Write(info);
48+
if (ElmScope.Counts.ContainsKey(GetLogContext().RequestID))
49+
{
50+
// TODO: display nested scopes nicely
51+
for (var i = 0; i < ElmScope.Counts[GetLogContext().RequestID].Count; i++)
52+
{
53+
state = "-----" + state;
54+
}
55+
info.State = state;
56+
info.Scopes = new List<Guid>(ElmScope.Counts[GetLogContext().RequestID]);
57+
}
58+
_store.Add(info);
6259
}
6360

6461
public bool IsEnabled(TraceType traceType)
@@ -68,30 +65,43 @@ public bool IsEnabled(TraceType traceType)
6865

6966
public IDisposable BeginScope(object state)
7067
{
71-
// TODO: use NullDisposable once it's moved to this repo #33
72-
return null;
68+
return new ElmScope(this, state, GetLogContext().RequestID);
7369
}
7470

7571
private LogContext GetLogContext()
7672
{
7773
var context = _contextAccessor.Value;
7874
if (context == null)
7975
{
80-
return new LogContext();
76+
// TODO: group non-request logs by Thread ID
77+
return new LogContext()
78+
{
79+
ThreadID = Thread.CurrentThread.ManagedThreadId
80+
};
8181
}
82-
else
82+
83+
var logContext = context.Items[_logContextKey] as LogContext;
84+
if (logContext == null)
8385
{
84-
return new LogContext()
86+
logContext = new LogContext()
8587
{
8688
RequestID = (Guid)context.Items[_requestIdentifierKey],
8789
Host = context.Request.Host,
8890
ContentType = context.Request.ContentType,
8991
Path = context.Request.Path,
9092
Scheme = context.Request.Scheme,
9193
StatusCode = context.Response.StatusCode,
92-
User = context.User.Identity.Name
94+
User = context.User,
95+
Method = context.Request.Method,
96+
Protocol = context.Request.Protocol,
97+
Headers = context.Request.Headers,
98+
Query = context.Request.QueryString,
99+
Cookies = context.Request.Cookies
93100
};
101+
context.Items[_logContextKey] = logContext;
94102
}
103+
104+
return logContext;
95105
}
96106
}
97107
}

src/Microsoft.AspNet.Logging.Elm/ElmLoggerProvider.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,20 @@ public class ElmLoggerProvider : ILoggerProvider
1212
private readonly IElmStore _store;
1313
private readonly IContextAccessor<HttpContext> _contextAccessor;
1414
private readonly object _requestIdentifierKey;
15+
private readonly object _logContextKey;
1516

16-
public ElmLoggerProvider(IElmStore store, IContextAccessor<HttpContext> contextAccessor, object requestIdentifierKey)
17+
public ElmLoggerProvider(IElmStore store, IContextAccessor<HttpContext> contextAccessor,
18+
object requestIdentifierKey, object logContextKey)
1719
{
1820
_store = store;
1921
_contextAccessor = contextAccessor;
2022
_requestIdentifierKey = requestIdentifierKey;
23+
_logContextKey = logContextKey;
2124
}
2225

2326
public ILogger Create(string name)
2427
{
25-
return new ElmLogger(name, this, _store, _contextAccessor, _requestIdentifierKey);
28+
return new ElmLogger(name, this, _store, _contextAccessor, _requestIdentifierKey, _logContextKey);
2629
}
2730
}
2831
}

src/Microsoft.AspNet.Logging.Elm/ElmMiddleware.cs

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
57
using System.Threading.Tasks;
68
using Microsoft.AspNet.Builder;
79
using Microsoft.AspNet.Http;
@@ -21,8 +23,10 @@ public class ElmMiddleware
2123
private readonly ElmOptions _options;
2224
private readonly ElmLoggerProvider _provider;
2325
private readonly IElmStore _store;
26+
private readonly ILogger _logger;
2427
private IContextAccessor<HttpContext> _contextAccessor;
25-
private static readonly object _requestIdentifier = new object();
28+
private static readonly object _requestIdentifierKey = new object();
29+
private static readonly object _logContextKey = new object();
2630

2731
public ElmMiddleware(
2832
RequestDelegate next, ILoggerFactory factory, IOptions<ElmOptions> options,
@@ -31,23 +35,86 @@ public ElmMiddleware(
3135
_next = next;
3236
_options = options.Options;
3337
_store = store;
38+
_logger = factory.Create<ElmMiddleware>();
3439
_contextAccessor = contextAccessor;
35-
_provider = new ElmLoggerProvider(_store, contextAccessor, _requestIdentifier);
40+
_provider = new ElmLoggerProvider(_store, contextAccessor, _requestIdentifierKey, _logContextKey);
3641
factory.AddProvider(_provider);
42+
// non-request logs
43+
_logger.WriteWarning("hello world");
44+
_logger.WriteCritical("critical: aliens approaching");
3745
}
3846

3947
public async Task Invoke(HttpContext context)
4048
{
41-
_contextAccessor.SetContextSource(() => context, null);
42-
context.Items[_requestIdentifier] = Guid.NewGuid();
43-
if (!context.Request.Path.Value.Equals(_options.Path.Value ?? "/Elm"))
49+
context.Items[_requestIdentifierKey] = Guid.NewGuid();
50+
if (context.Request.Path != _options.Path && !context.Request.Path.StartsWithSegments(_options.Path))
4451
{
45-
await _next(context);
46-
return;
52+
try
53+
{
54+
await _next(context);
55+
}
56+
catch (Exception ex)
57+
{
58+
_contextAccessor.SetContextSource(() => context, null);
59+
_logger.WriteError("An unhandled exception has occurred: " + ex.Message, ex);
60+
throw;
61+
}
62+
}
63+
64+
// parse params
65+
var logs = (IEnumerable<LogInfo>)null;
66+
if (context.Request.Query.ContainsKey("level"))
67+
{
68+
var minLevel = (TraceType)int.Parse(context.Request.Query.GetValues("level")[0]);
69+
logs = _store.GetLogs(minLevel);
70+
_options.MinLevel = minLevel;
71+
}
72+
else
73+
{
74+
logs = _store.GetLogs();
75+
_options.MinLevel = TraceType.Verbose;
76+
}
77+
if (context.Request.Query.ContainsKey("name"))
78+
{
79+
var namePrefix = context.Request.Query.GetValues("name")[0];
80+
logs = logs.Where(l => l.Name.StartsWith(namePrefix));
81+
_options.NamePrefix = namePrefix;
82+
}
83+
84+
// main log page
85+
if (context.Request.Path == _options.Path)
86+
{
87+
var model = new LogPageModel()
88+
{
89+
// sort so most recent logs are first
90+
Logs = logs.OrderBy(l => l.Time).Reverse(),
91+
Options = _options
92+
};
93+
var logPage = new LogPage(model);
94+
await logPage.ExecuteAsync(context);
95+
}
96+
// request details page
97+
else
98+
{
99+
try
100+
{
101+
var parts = context.Request.Path.Value.Split('/');
102+
var id = Guid.Parse(parts[parts.Length - 1]);
103+
var requestLogs = logs.Where(l => l.Context.RequestID == id);
104+
var model = new RequestPageModel()
105+
{
106+
RequestID = id,
107+
Logs = requestLogs,
108+
Options = _options
109+
};
110+
var requestPage = new RequestPage(model);
111+
await requestPage.ExecuteAsync(context);
112+
}
113+
catch (Exception)
114+
{
115+
// TODO: bad url
116+
}
47117
}
48-
var model = new LogPageModel() { Logs = _store.GetLogs() };
49-
var logPage = new LogPage(model);
50-
await logPage.ExecuteAsync(context);
51118
}
52119
}
53120
}

src/Microsoft.AspNet.Logging.Elm/ElmOptions.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using Microsoft.AspNet.Http;
5+
using Microsoft.Framework.Logging;
56

67
namespace Microsoft.AspNet.Logging.Elm
78
{
@@ -10,9 +11,24 @@ namespace Microsoft.AspNet.Logging.Elm
1011
/// </summary>
1112
public class ElmOptions
1213
{
14+
public ElmOptions()
15+
{
16+
Path = new PathString("/Elm");
17+
}
18+
1319
/// <summary>
1420
/// Specifies the path to view the logs
1521
/// </summary>
1622
public PathString Path { get; set; }
23+
24+
/// <summary>
25+
/// The minimum severity level shown
26+
/// </summary>
27+
public TraceType MinLevel { get; set; }
28+
29+
/// <summary>
30+
/// prefix filter for the loggers shown
31+
/// </summary>
32+
public string NamePrefix { get; set; }
1733
}
1834
}

0 commit comments

Comments
 (0)