Log messages to DiagnosticListener in addition to page instrumentation

Fixes #3281
This commit is contained in:
Pranav K 2015-11-04 12:04:46 -08:00
parent cef4a66479
commit d17db92e19
5 changed files with 420 additions and 84 deletions

View File

@ -84,6 +84,12 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <inheritdoc />
public IPageExecutionContext PageExecutionContext { get; set; }
/// <summary>
/// Gets or sets a <see cref="DiagnosticSource.DiagnosticSource"/> instance used to instrument the page execution.
/// </summary>
[RazorInject]
public DiagnosticSource DiagnosticSource { get; set; }
/// <summary>
/// Gets the <see cref="TextWriter"/> that the page is writing output to.
/// </summary>
@ -1063,12 +1069,41 @@ namespace Microsoft.AspNet.Mvc.Razor
public void BeginContext(int position, int length, bool isLiteral)
{
const string BeginContextEvent = "Microsoft.AspNet.Mvc.Razor.BeginInstrumentationContext";
PageExecutionContext?.BeginContext(position, length, isLiteral);
if (DiagnosticSource?.IsEnabled(BeginContextEvent) == true)
{
DiagnosticSource.Write(
BeginContextEvent,
new
{
httpContext = Context,
path = Path,
isPartial = IsPartial,
position = position,
length = length,
isLiteral = isLiteral,
});
}
}
public void EndContext()
{
const string EndContextEvent = "Microsoft.AspNet.Mvc.Razor.EndInstrumentationContext";
PageExecutionContext?.EndContext();
if (DiagnosticSource?.IsEnabled(EndContextEvent) == true)
{
DiagnosticSource.Write(
EndContextEvent,
new
{
httpContext = Context,
path = Path,
isPartial = IsPartial,
});
}
}
/// <summary>

View File

@ -2,11 +2,13 @@
// 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.Globalization;
using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Razor.Internal;
@ -15,6 +17,7 @@ using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Microsoft.AspNet.Mvc.ViewFeatures.Internal;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.WebEncoders.Testing;
using Moq;
using Xunit;
@ -33,24 +36,26 @@ namespace Microsoft.AspNet.Mvc.Razor
var myService = new MyService();
var helper = Mock.Of<IHtmlHelper<object>>();
var htmlEncoder = new HtmlTestEncoder();
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(p => p.GetService(typeof(MyService)))
.Returns(myService);
serviceProvider.Setup(p => p.GetService(typeof(IHtmlHelper<object>)))
.Returns(helper);
serviceProvider.Setup(p => p.GetService(typeof(HtmlEncoder)))
.Returns(htmlEncoder);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.RequestServices)
.Returns(serviceProvider.Object);
var diagnosticSource = new DiagnosticListener("Microsoft.AspNet");
var serviceProvider = new ServiceCollection()
.AddInstance(myService)
.AddInstance(helper)
.AddInstance<HtmlEncoder>(htmlEncoder)
.AddInstance<DiagnosticSource>(diagnosticSource)
.BuildServiceProvider();
var httpContext = new DefaultHttpContext
{
RequestServices = serviceProvider
};
var actionContext = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var viewContext = new ViewContext(actionContext,
Mock.Of<IView>(),
new ViewDataDictionary(new EmptyModelMetadataProvider()),
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var viewContext = new ViewContext(
actionContext,
Mock.Of<IView>(),
new ViewDataDictionary(new EmptyModelMetadataProvider()),
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
// Act
activator.Activate(instance, viewContext);
@ -59,6 +64,7 @@ namespace Microsoft.AspNet.Mvc.Razor
Assert.Same(helper, instance.Html);
Assert.Same(myService, instance.MyService);
Assert.Same(viewContext, myService.ViewContext);
Assert.Same(diagnosticSource, instance.DiagnosticSource);
Assert.Null(instance.MyService2);
}
@ -72,25 +78,23 @@ namespace Microsoft.AspNet.Mvc.Razor
var myService = new MyService();
var helper = Mock.Of<IHtmlHelper<object>>();
var serviceProvider = new Mock<IServiceProvider>();
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.RequestServices)
.Returns(serviceProvider.Object);
var httpContext = new DefaultHttpContext
{
RequestServices = new ServiceCollection().BuildServiceProvider()
};
var actionContext = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var viewContext = new ViewContext(actionContext,
Mock.Of<IView>(),
new ViewDataDictionary(new EmptyModelMetadataProvider()),
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var viewContext = new ViewContext(
actionContext,
Mock.Of<IView>(),
new ViewDataDictionary(new EmptyModelMetadataProvider()),
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
// Act and Assert
var ex = Assert.Throws<InvalidOperationException>(() => activator.Activate(instance, viewContext));
var message = string.Format(CultureInfo.InvariantCulture,
"View of type '{0}' cannot be activated by '{1}'.",
instance.GetType().FullName,
typeof(RazorPageActivator).FullName);
var message = $"View of type '{instance.GetType()}' cannot be activated by '{typeof(RazorPageActivator)}'.";
Assert.Equal(message, ex.Message);
}
@ -104,28 +108,29 @@ namespace Microsoft.AspNet.Mvc.Razor
var myService = new MyService();
var helper = Mock.Of<IHtmlHelper<object>>();
var htmlEncoder = new HtmlTestEncoder();
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(p => p.GetService(typeof(MyService)))
.Returns(myService);
serviceProvider.Setup(p => p.GetService(typeof(IHtmlHelper<object>)))
.Returns(helper);
serviceProvider.Setup(p => p.GetService(typeof(HtmlEncoder)))
.Returns(htmlEncoder);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.RequestServices)
.Returns(serviceProvider.Object);
var serviceProvider = new ServiceCollection()
.AddInstance(myService)
.AddInstance(helper)
.AddInstance<HtmlEncoder>(htmlEncoder)
.AddInstance<DiagnosticSource>(new DiagnosticListener("Microsoft.Aspnet.Mvc"))
.BuildServiceProvider();
var httpContext = new DefaultHttpContext
{
RequestServices = serviceProvider
};
var actionContext = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var viewData = new ViewDataDictionary<object>(new EmptyModelMetadataProvider())
{
Model = new MyModel()
};
var viewContext = new ViewContext(actionContext,
Mock.Of<IView>(),
viewData,
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
var viewContext = new ViewContext(
actionContext,
Mock.Of<IView>(),
viewData,
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
// Act
activator.Activate(instance, viewContext);
@ -143,28 +148,29 @@ namespace Microsoft.AspNet.Mvc.Razor
var myService = new MyService();
var helper = Mock.Of<IHtmlHelper<object>>();
var htmlEncoder = new HtmlTestEncoder();
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(p => p.GetService(typeof(MyService)))
.Returns(myService);
serviceProvider.Setup(p => p.GetService(typeof(IHtmlHelper<object>)))
.Returns(helper);
serviceProvider.Setup(p => p.GetService(typeof(HtmlEncoder)))
.Returns(htmlEncoder);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.RequestServices)
.Returns(serviceProvider.Object);
var serviceProvider = new ServiceCollection()
.AddInstance(myService)
.AddInstance(helper)
.AddInstance<HtmlEncoder>(htmlEncoder)
.AddInstance<DiagnosticSource>(new DiagnosticListener("Microsoft.Aspnet.Mvc"))
.BuildServiceProvider();
var httpContext = new DefaultHttpContext
{
RequestServices = serviceProvider
};
var actionContext = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var viewData = new ViewDataDictionary<MyModel>(new EmptyModelMetadataProvider())
{
Model = new MyModel()
};
var viewContext = new ViewContext(actionContext,
Mock.Of<IView>(),
viewData,
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
var viewContext = new ViewContext(
actionContext,
Mock.Of<IView>(),
viewData,
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
// Act
activator.Activate(instance, viewContext);
@ -182,25 +188,26 @@ namespace Microsoft.AspNet.Mvc.Razor
var myService = new MyService();
var helper = Mock.Of<IHtmlHelper<object>>();
var htmlEncoder = new HtmlTestEncoder();
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(p => p.GetService(typeof(MyService)))
.Returns(myService);
serviceProvider.Setup(p => p.GetService(typeof(IHtmlHelper<object>)))
.Returns(helper);
serviceProvider.Setup(p => p.GetService(typeof(HtmlEncoder)))
.Returns(htmlEncoder);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.RequestServices)
.Returns(serviceProvider.Object);
var serviceProvider = new ServiceCollection()
.AddInstance(myService)
.AddInstance(helper)
.AddInstance<HtmlEncoder>(htmlEncoder)
.AddInstance<DiagnosticSource>(new DiagnosticListener("Microsoft.AspNet.Mvc"))
.BuildServiceProvider();
var httpContext = new DefaultHttpContext
{
RequestServices = serviceProvider
};
var actionContext = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var viewData = new ViewDataDictionary<object>(new EmptyModelMetadataProvider());
var viewContext = new ViewContext(actionContext,
Mock.Of<IView>(),
viewData,
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
var viewContext = new ViewContext(
actionContext,
Mock.Of<IView>(),
viewData,
Mock.Of<ITempDataDictionary>(),
TextWriter.Null,
new HtmlHelperOptions());
// Act
activator.Activate(instance, viewContext);

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
@ -745,6 +746,231 @@ namespace Microsoft.AspNet.Mvc.Razor
context.Verify();
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task WriteAttribute_CallsBeginAndEndContext_OnPageExecutionListenerContext(bool isPartial)
{
// Arrange
var path = "path-to-page";
var page = CreatePage(p =>
{
p.HtmlEncoder = new HtmlTestEncoder();
p.BeginWriteAttribute("href", "prefix", 0, "suffix", 34, 2);
p.WriteAttributeValue("prefix", 0, "attr1-value", 8, 14, true);
p.WriteAttributeValue("prefix2", 22, "attr2", 29, 5, false);
p.EndWriteAttribute();
});
page.Path = path;
page.IsPartial = isPartial;
var adapter = new TestDiagnosticListener();
var diagnosticListener = new DiagnosticListener("Microsoft.AspNet.Mvc.Razor");
diagnosticListener.SubscribeWithAdapter(adapter);
page.DiagnosticSource = diagnosticListener;
// Act
await page.ExecuteAsync();
// Assert
Func<object, TestDiagnosticListener.BeginPageInstrumentationData> assertStartEvent = data =>
{
var beginEvent = Assert.IsType<TestDiagnosticListener.BeginPageInstrumentationData>(data);
Assert.NotNull(beginEvent.HttpContext);
Assert.Equal(path, beginEvent.Path);
Assert.Equal(isPartial, beginEvent.IsPartial);
return beginEvent;
};
Action<object> assertEndEvent = data =>
{
var endEvent = Assert.IsType<TestDiagnosticListener.EndPageInstrumentationData>(data);
Assert.NotNull(endEvent.HttpContext);
Assert.Equal(path, endEvent.Path);
Assert.Equal(isPartial, endEvent.IsPartial);
};
Assert.Collection(adapter.PageInstrumentationData,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(0, beginEvent.Position);
Assert.Equal(6, beginEvent.Length);
Assert.True(beginEvent.IsLiteral);
},
assertEndEvent,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(0, beginEvent.Position);
Assert.Equal(6, beginEvent.Length);
Assert.True(beginEvent.IsLiteral);
},
assertEndEvent,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(8, beginEvent.Position);
Assert.Equal(14, beginEvent.Length);
Assert.True(beginEvent.IsLiteral);
},
assertEndEvent,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(22, beginEvent.Position);
Assert.Equal(7, beginEvent.Length);
Assert.True(beginEvent.IsLiteral);
},
assertEndEvent,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(29, beginEvent.Position);
Assert.Equal(5, beginEvent.Length);
Assert.False(beginEvent.IsLiteral);
},
assertEndEvent,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(34, beginEvent.Position);
Assert.Equal(6, beginEvent.Length);
Assert.True(beginEvent.IsLiteral);
},
assertEndEvent);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task WriteAttribute_WithBoolValue_CallsBeginAndEndContext_OnPageExecutionListenerContext(bool isPartial)
{
// Arrange
var path = "some-path";
var page = CreatePage(p =>
{
p.HtmlEncoder = new HtmlTestEncoder();
p.BeginWriteAttribute("href", "prefix", 0, "suffix", 10, 1);
p.WriteAttributeValue("", 6, "true", 6, 4, false);
p.EndWriteAttribute();
});
page.Path = path;
page.IsPartial = isPartial;
var adapter = new TestDiagnosticListener();
var diagnosticListener = new DiagnosticListener("Microsoft.AspNet.Mvc.Razor");
diagnosticListener.SubscribeWithAdapter(adapter);
page.DiagnosticSource = diagnosticListener;
// Act
await page.ExecuteAsync();
// Assert
Func<object, TestDiagnosticListener.BeginPageInstrumentationData> assertStartEvent = data =>
{
var beginEvent = Assert.IsType<TestDiagnosticListener.BeginPageInstrumentationData>(data);
Assert.NotNull(beginEvent.HttpContext);
Assert.Equal(path, beginEvent.Path);
Assert.Equal(isPartial, beginEvent.IsPartial);
return beginEvent;
};
Action<object> assertEndEvent = data =>
{
var endEvent = Assert.IsType<TestDiagnosticListener.EndPageInstrumentationData>(data);
Assert.NotNull(endEvent.HttpContext);
Assert.Equal(path, endEvent.Path);
Assert.Equal(isPartial, endEvent.IsPartial);
};
Assert.Collection(adapter.PageInstrumentationData,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(0, beginEvent.Position);
Assert.Equal(6, beginEvent.Length);
Assert.True(beginEvent.IsLiteral);
},
assertEndEvent,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(6, beginEvent.Position);
Assert.Equal(4, beginEvent.Length);
Assert.False(beginEvent.IsLiteral);
},
assertEndEvent,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(10, beginEvent.Position);
Assert.Equal(6, beginEvent.Length);
Assert.True(beginEvent.IsLiteral);
},
assertEndEvent);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task WriteAttribute_CallsBeginAndEndContext_OnPrefixAndSuffixValues(bool isPartial)
{
// Arrange
var path = "some-path";
var page = CreatePage(p =>
{
p.BeginWriteAttribute("href", "prefix", 0, "tail", 7, 0);
p.EndWriteAttribute();
});
page.Path = path;
page.IsPartial = isPartial;
var adapter = new TestDiagnosticListener();
var diagnosticListener = new DiagnosticListener("Microsoft.AspNet.Mvc.Razor");
diagnosticListener.SubscribeWithAdapter(adapter);
page.DiagnosticSource = diagnosticListener;
// Act
await page.ExecuteAsync();
// Assert
Func<object, TestDiagnosticListener.BeginPageInstrumentationData> assertStartEvent = data =>
{
var beginEvent = Assert.IsType<TestDiagnosticListener.BeginPageInstrumentationData>(data);
Assert.NotNull(beginEvent.HttpContext);
Assert.Equal(path, beginEvent.Path);
Assert.Equal(isPartial, beginEvent.IsPartial);
return beginEvent;
};
Action<object> assertEndEvent = data =>
{
var endEvent = Assert.IsType<TestDiagnosticListener.EndPageInstrumentationData>(data);
Assert.NotNull(endEvent.HttpContext);
Assert.Equal(path, endEvent.Path);
Assert.Equal(isPartial, endEvent.IsPartial);
};
Assert.Collection(adapter.PageInstrumentationData,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(0, beginEvent.Position);
Assert.Equal(6, beginEvent.Length);
Assert.True(beginEvent.IsLiteral);
},
assertEndEvent,
data =>
{
var beginEvent = assertStartEvent(data);
Assert.Equal(7, beginEvent.Position);
Assert.Equal(4, beginEvent.Length);
Assert.True(beginEvent.IsLiteral);
},
assertEndEvent);
}
public static TheoryData AddHtmlAttributeValues_ValueData
{
get

View File

@ -13,9 +13,14 @@
"version": "6.0.0-*",
"type": "build"
},
"Microsoft.AspNet.Mvc.TestDiagnosticListener.Sources": {
"version": "6.0.0-*",
"type": "build"
},
"Microsoft.AspNet.Testing": "1.0.0-*",
"Microsoft.Dnx.Runtime": "1.0.0-*",
"Microsoft.Extensions.DependencyInjection": "1.0.0-*",
"Microsoft.Extensions.DiagnosticAdapter": "1.0.0-*",
"xunit.runner.aspnet": "2.0.0-aspnet-*"
},
"commands": {

View File

@ -146,7 +146,7 @@ namespace Microsoft.AspNet.Mvc
string viewName,
IProxyView view)
{
ViewFound = new OnViewFoundEventData()
ViewFound = new OnViewFoundEventData()
{
ActionContext = actionContext,
IsPartial = isPartial,
@ -324,5 +324,68 @@ namespace Microsoft.AspNet.Mvc
View = view
};
}
public class BeginPageInstrumentationData
{
public IProxyHttpContext HttpContext { get; set; }
public string Path { get; set; }
public bool IsPartial { get; set; }
public int Position { get; set; }
public int Length { get; set; }
public bool IsLiteral { get; set; }
}
public class EndPageInstrumentationData
{
public IProxyHttpContext HttpContext { get; set; }
public string Path { get; set; }
public bool IsPartial { get; set; }
}
public List<object> PageInstrumentationData { get; set; } = new List<object>();
[DiagnosticName("Microsoft.AspNet.Mvc.Razor.BeginInstrumentationContext")]
public virtual void OnBeginPageInstrumentationContext(
IProxyHttpContext httpContext,
string path,
bool isPartial,
int position,
int length,
bool isLiteral)
{
PageInstrumentationData.Add(new BeginPageInstrumentationData
{
HttpContext = httpContext,
Path = path,
IsPartial = isPartial,
Position = position,
Length = length,
IsLiteral = isLiteral,
});
}
[DiagnosticName("Microsoft.AspNet.Mvc.Razor.EndInstrumentationContext")]
public virtual void OnEndPageInstrumentationContext(
IProxyHttpContext httpContext,
string path,
bool isPartial,
int position,
int length,
bool isLiteral)
{
PageInstrumentationData.Add(new EndPageInstrumentationData
{
HttpContext = httpContext,
Path = path,
IsPartial = isPartial,
});
}
}
}