Expand Blazor logging (#11919)

This commit is contained in:
Steve Sanderson 2019-07-08 13:50:19 +01:00 committed by GitHub
parent 0049c5899c
commit 6534bcff32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 322 additions and 53 deletions

View File

@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
{
public partial class WebAssemblyRenderer : Microsoft.AspNetCore.Components.Rendering.Renderer
{
public WebAssemblyRenderer(System.IServiceProvider serviceProvider) : base (default(System.IServiceProvider)) { }
public WebAssemblyRenderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) : base (default(System.IServiceProvider), default(Microsoft.Extensions.Logging.ILoggerFactory)) { }
public System.Threading.Tasks.Task AddComponentAsync(System.Type componentType, string domElementSelector) { throw null; }
public System.Threading.Tasks.Task AddComponentAsync<TComponent>(string domElementSelector) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
public override System.Threading.Tasks.Task DispatchEventAsync(int eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo eventFieldInfo, Microsoft.AspNetCore.Components.UIEventArgs eventArgs) { throw null; }

View File

@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Blazor.Rendering;
using Microsoft.AspNetCore.Components.Builder;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.Blazor.Hosting
{
@ -38,7 +40,8 @@ namespace Microsoft.AspNetCore.Blazor.Hosting
public async Task<WebAssemblyRenderer> CreateRendererAsync()
{
var renderer = new WebAssemblyRenderer(Services);
var loggerFactory = (ILoggerFactory)Services.GetService(typeof(ILoggerFactory));
var renderer = new WebAssemblyRenderer(Services, loggerFactory);
for (var i = 0; i < Entries.Count; i++)
{
var (componentType, domElementSelector) = Entries[i];

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Blazor.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Blazor.Rendering
{
@ -26,8 +27,9 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
/// Constructs an instance of <see cref="WebAssemblyRenderer"/>.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to use when initializing components.</param>
public WebAssemblyRenderer(IServiceProvider serviceProvider)
: base(serviceProvider)
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public WebAssemblyRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
: base(serviceProvider, loggerFactory)
{
// The browser renderer registers and unregisters itself with the static
// registry. This works well with the WebAssembly runtime, and is simple for the

View File

@ -20,6 +20,7 @@ using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
@ -441,7 +442,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
protected class TestRenderer : Renderer
{
public TestRenderer() : base(new TestServiceProvider(), CreateDefaultDispatcher())
public TestRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance, CreateDefaultDispatcher())
{
}

View File

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.Components.Performance
{
@ -87,7 +88,7 @@ namespace Microsoft.AspNetCore.Components.Performance
private class FakeRenderer : Renderer
{
public FakeRenderer()
: base(new TestServiceProvider(), new RendererSynchronizationContext())
: base(new TestServiceProvider(), NullLoggerFactory.Instance, new RendererSynchronizationContext())
{
}

View File

@ -674,7 +674,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
public partial class HtmlRenderer : Microsoft.AspNetCore.Components.Rendering.Renderer
{
public HtmlRenderer(System.IServiceProvider serviceProvider, System.Func<string, string> htmlEncoder, Microsoft.AspNetCore.Components.IDispatcher dispatcher) : base (default(System.IServiceProvider)) { }
public HtmlRenderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.Components.IDispatcher dispatcher, System.Func<string, string> htmlEncoder) : base (default(System.IServiceProvider), default(Microsoft.Extensions.Logging.ILoggerFactory)) { }
protected override void HandleException(System.Exception exception) { }
[System.Diagnostics.DebuggerStepThroughAttribute]
public System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.Rendering.ComponentRenderedText> RenderComponentAsync(System.Type componentType, Microsoft.AspNetCore.Components.ParameterCollection initialParameters) { throw null; }
@ -692,8 +692,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
public abstract partial class Renderer : System.IDisposable
{
public Renderer(System.IServiceProvider serviceProvider) { }
public Renderer(System.IServiceProvider serviceProvider, Microsoft.AspNetCore.Components.IDispatcher dispatcher) { }
public Renderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
public Renderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.Components.IDispatcher dispatcher) { }
public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } }
protected internal virtual void AddToRenderQueue(int componentId, Microsoft.AspNetCore.Components.RenderFragment renderFragment) { }
protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; }

View File

@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Components.Rendering
{
@ -26,10 +27,11 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// Initializes a new instance of <see cref="HtmlRenderer"/>.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to use to instantiate components.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <param name="dispatcher">The <see cref="IDispatcher"/> to be for invoking user actions into the <see cref="Renderer"/> context.</param>
/// <param name="htmlEncoder">A <see cref="Func{T, TResult}"/> that will HTML encode the given string.</param>
/// <param name="dispatcher"></param>
public HtmlRenderer(IServiceProvider serviceProvider, Func<string, string> htmlEncoder, IDispatcher dispatcher)
: base(serviceProvider, dispatcher)
public HtmlRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IDispatcher dispatcher, Func<string, string> htmlEncoder)
: base(serviceProvider, loggerFactory, dispatcher)
{
_htmlEncoder = htmlEncoder;
}

View File

@ -0,0 +1,65 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Components.Rendering
{
public abstract partial class Renderer
{
internal static class Log
{
private static readonly Action<ILogger, int, Type, int, Type, Exception> _initializingChildComponent =
LoggerMessage.Define<int, Type, int, Type>(LogLevel.Debug, new EventId(1, "InitializingChildComponent"), "Initializing component {ComponentId} ({ComponentType}) as child of {ParentComponentId} ({ParentComponentId})");
private static readonly Action<ILogger, int, Type, Exception> _initializingRootComponent =
LoggerMessage.Define<int, Type>(LogLevel.Debug, new EventId(2, "InitializingRootComponent"), "Initializing root component {ComponentId} ({ComponentType})");
private static readonly Action<ILogger, int, Type, Exception> _renderingComponent =
LoggerMessage.Define<int, Type>(LogLevel.Debug, new EventId(3, "RenderingComponent"), "Rendering component {ComponentId} of type {ComponentType}");
private static readonly Action<ILogger, int, Type, Exception> _disposingComponent =
LoggerMessage.Define<int, Type>(LogLevel.Debug, new EventId(4, "DisposingComponent"), "Disposing component {ComponentId} of type {ComponentType}");
private static readonly Action<ILogger, int, string, Exception> _handlingEvent =
LoggerMessage.Define<int, string>(LogLevel.Debug, new EventId(5, "HandlingEvent"), "Handling event {EventId} of type '{EventType}'");
public static void InitializingComponent(ILogger logger, ComponentState componentState, ComponentState parentComponentState)
{
if (logger.IsEnabled(LogLevel.Debug)) // This is almost always false, so skip the evaluations
{
if (parentComponentState == null)
{
_initializingRootComponent(logger, componentState.ComponentId, componentState.Component.GetType(), null);
}
else
{
_initializingChildComponent(logger, componentState.ComponentId, componentState.Component.GetType(), parentComponentState.ComponentId, parentComponentState.Component.GetType(), null);
}
}
}
public static void RenderingComponent(ILogger logger, ComponentState componentState)
{
if (logger.IsEnabled(LogLevel.Debug)) // This is almost always false, so skip the evaluations
{
_renderingComponent(logger, componentState.ComponentId, componentState.Component.GetType(), null);
}
}
internal static void DisposingComponent(ILogger<Renderer> logger, ComponentState componentState)
{
if (logger.IsEnabled(LogLevel.Debug)) // This is almost always false, so skip the evaluations
{
_disposingComponent(logger, componentState.ComponentId, componentState.Component.GetType(), null);
}
}
internal static void HandlingEvent(ILogger<Renderer> logger, int eventHandlerId, UIEventArgs eventArgs)
{
_handlingEvent(logger, eventHandlerId, eventArgs?.Type ?? "null", null);
}
}
}
}

View File

@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Components.Rendering
{
@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// Provides mechanisms for rendering hierarchies of <see cref="IComponent"/> instances,
/// dispatching events to them, and notifying when the user interface is being updated.
/// </summary>
public abstract class Renderer : IDisposable
public abstract partial class Renderer : IDisposable
{
private readonly ComponentFactory _componentFactory;
private readonly Dictionary<int, ComponentState> _componentStateById = new Dictionary<int, ComponentState>();
@ -22,6 +23,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
private readonly Dictionary<int, EventCallback> _eventBindings = new Dictionary<int, EventCallback>();
private readonly Dictionary<int, int> _eventHandlerIdReplacements = new Dictionary<int, int>();
private readonly IDispatcher _dispatcher;
private readonly ILogger<Renderer> _logger;
private int _nextComponentId = 0; // TODO: change to 'long' when Mono .NET->JS interop supports it
private bool _isBatchInProgress;
@ -55,19 +57,33 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// Constructs an instance of <see cref="Renderer"/>.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to be used when initializing components.</param>
public Renderer(IServiceProvider serviceProvider)
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
{
if (serviceProvider is null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}
if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_componentFactory = new ComponentFactory(serviceProvider);
_logger = loggerFactory.CreateLogger<Renderer>();
}
/// <summary>
/// Constructs an instance of <see cref="Renderer"/>.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to be used when initializing components.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <param name="dispatcher">The <see cref="IDispatcher"/> to be for invoking user actions into the <see cref="Renderer"/> context.</param>
public Renderer(IServiceProvider serviceProvider, IDispatcher dispatcher) : this(serviceProvider)
public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IDispatcher dispatcher)
: this(serviceProvider, loggerFactory)
{
_dispatcher = dispatcher;
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
}
/// <summary>
@ -189,6 +205,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
var componentId = _nextComponentId++;
var parentComponentState = GetOptionalComponentState(parentComponentId);
var componentState = new ComponentState(this, componentId, component, parentComponentState);
Log.InitializingComponent(_logger, componentState, parentComponentState);
_componentStateById.Add(componentId, componentState);
component.Configure(new RenderHandle(this, componentId));
return componentState;
@ -220,6 +237,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
throw new ArgumentException($"There is no event handler with ID {eventHandlerId}");
}
Log.HandlingEvent(_logger, eventHandlerId, eventArgs);
if (fieldInfo != null)
{
var latestEquivalentEventHandlerId = FindLatestEventHandlerIdInChain(eventHandlerId);
@ -613,14 +632,17 @@ namespace Microsoft.AspNetCore.Components.Rendering
private void RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
{
renderQueueEntry.ComponentState
.RenderIntoBatch(_batchBuilder, renderQueueEntry.RenderFragment);
var componentState = renderQueueEntry.ComponentState;
Log.RenderingComponent(_logger, componentState);
componentState.RenderIntoBatch(_batchBuilder, renderQueueEntry.RenderFragment);
// Process disposal queue now in case it causes further component renders to be enqueued
while (_batchBuilder.ComponentDisposalQueue.Count > 0)
{
var disposeComponentId = _batchBuilder.ComponentDisposalQueue.Dequeue();
GetRequiredComponentState(disposeComponentId).DisposeInBatch(_batchBuilder);
var disposeComponentState = GetRequiredComponentState(disposeComponentId);
Log.DisposingComponent(_logger, disposeComponentState);
disposeComponentState.DisposeInBatch(_batchBuilder);
_componentStateById.Remove(disposeComponentId);
_batchBuilder.DisposedComponentIds.Append(disposeComponentId);
}
@ -710,6 +732,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
{
foreach (var componentState in _componentStateById.Values)
{
Log.DisposingComponent(_logger, componentState);
if (componentState.Component is IDisposable disposable)
{
try

View File

@ -5,8 +5,9 @@ using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Layouts;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.Components.Routing
{
@ -22,6 +23,7 @@ namespace Microsoft.AspNetCore.Components.Routing
string _baseUri;
string _locationAbsolute;
bool _navigationInterceptionEnabled;
ILogger<Router> _logger;
[Inject] private IUriHelper UriHelper { get; set; }
@ -29,6 +31,8 @@ namespace Microsoft.AspNetCore.Components.Routing
[Inject] private IComponentContext ComponentContext { get; set; }
[Inject] private ILoggerFactory LoggerFactory { get; set; }
/// <summary>
/// Gets or sets the assembly that should be searched, along with its referenced
/// assemblies, for components matching the URI.
@ -55,6 +59,7 @@ namespace Microsoft.AspNetCore.Components.Routing
/// <inheritdoc />
public void Configure(RenderHandle renderHandle)
{
_logger = LoggerFactory.CreateLogger<Router>();
_renderHandle = renderHandle;
_baseUri = UriHelper.GetBaseUri();
_locationAbsolute = UriHelper.GetAbsoluteUri();
@ -111,12 +116,16 @@ namespace Microsoft.AspNetCore.Components.Routing
$"does not implement {typeof(IComponent).FullName}.");
}
Log.NavigatingToComponent(_logger, context.Handler, locationPath, _baseUri);
_renderHandle.Render(builder => Render(builder, context.Handler, context.Parameters));
}
else
{
if (!isNavigationIntercepted && NotFoundContent != null)
{
Log.DisplayingNotFoundContent(_logger, locationPath, _baseUri);
// We did not find a Component that matches the route.
// Only show the NotFoundContent if the application developer programatically got us here i.e we did not
// intercept the navigation. In all other cases, force a browser navigation since this could be non-Blazor content.
@ -124,6 +133,7 @@ namespace Microsoft.AspNetCore.Components.Routing
}
else
{
Log.NavigatingToExternalUri(_logger, _locationAbsolute, locationPath, _baseUri);
UriHelper.NavigateTo(_locationAbsolute, forceLoad: true);
}
}
@ -148,5 +158,32 @@ namespace Microsoft.AspNetCore.Components.Routing
return Task.CompletedTask;
}
private static class Log
{
private static readonly Action<ILogger, string, string, Exception> _displayingNotFoundContent =
LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(1, "DisplayingNotFoundContent"), $"Displaying {nameof(NotFoundContent)} because path '{{Path}}' with base URI '{{BaseUri}}' does not match any component route");
private static readonly Action<ILogger, Type, string, string, Exception> _navigatingToComponent =
LoggerMessage.Define<Type, string, string>(LogLevel.Debug, new EventId(2, "NavigatingToComponent"), "Navigating to component {ComponentType} in response to path '{Path}' with base URI '{BaseUri}'");
private static readonly Action<ILogger, string, string, string, Exception> _navigatingToExternalUri =
LoggerMessage.Define<string, string, string>(LogLevel.Debug, new EventId(3, "NavigatingToExternalUri"), "Navigating to non-component URI '{ExternalUri}' in response to path '{Path}' with base URI '{BaseUri}'");
internal static void DisplayingNotFoundContent(ILogger logger, string path, string baseUri)
{
_displayingNotFoundContent(logger, path, baseUri, null);
}
internal static void NavigatingToComponent(ILogger logger, Type componentType, string path, string baseUri)
{
_navigatingToComponent(logger, componentType, path, baseUri, null);
}
internal static void NavigatingToExternalUri(ILogger logger, string externalUri, string path, string baseUri)
{
_navigatingToExternalUri(logger, externalUri, path, baseUri, null);
}
}
}
}

View File

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
@ -1794,7 +1795,7 @@ namespace Microsoft.AspNetCore.Components.Test
private class TestRenderer : Renderer
{
public TestRenderer() : base(new TestServiceProvider(), new RendererSynchronizationContext())
public TestRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance, new RendererSynchronizationContext())
{
}

View File

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.Components.Test
@ -2207,7 +2208,7 @@ namespace Microsoft.AspNetCore.Components.Test
private class FakeRenderer : Renderer
{
public FakeRenderer() : base(new TestServiceProvider(), new RendererSynchronizationContext())
public FakeRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance, new RendererSynchronizationContext())
{
}

View File

@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Microsoft.AspNetCore.Testing;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.Components.Test
@ -3372,7 +3373,7 @@ namespace Microsoft.AspNetCore.Components.Test
private class NoOpRenderer : Renderer
{
public NoOpRenderer() : base(new TestServiceProvider(), new RendererSynchronizationContext())
public NoOpRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance, new RendererSynchronizationContext())
{
}

View File

@ -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 Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.Components.Rendering
{
@ -9,7 +10,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
{
protected override HtmlRenderer GetHtmlRenderer(IServiceProvider serviceProvider)
{
return new HtmlRenderer(serviceProvider, _encoder, Dispatcher);
return new HtmlRenderer(serviceProvider, NullLoggerFactory.Instance, Dispatcher, _encoder);
}
}
}

View File

@ -351,7 +351,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
"Unhandled error invoking circuit handler type {handlerType}.{handlerMethod}: {Message}");
_disposingCircuit = LoggerMessage.Define<string>(
LogLevel.Trace,
LogLevel.Debug,
EventIds.DisposingCircuit,
"Disposing circuit with identifier {CircuitId}");

View File

@ -340,7 +340,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
"Unhandled exception disposing circuit host: {Message}");
_unhandledExceptionDisposingTokenSource = LoggerMessage.Define<string>(
LogLevel.Trace,
LogLevel.Debug,
EventIds.ExceptionDisposingTokenSource,
"Exception thrown when disposing token source: {Message}");

View File

@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger _logger;
private readonly CircuitIdFactory _circuitIdFactory;
public DefaultCircuitFactory(
@ -30,6 +31,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
{
_scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
_loggerFactory = loggerFactory;
_logger = _loggerFactory.CreateLogger<CircuitFactory>();
_circuitIdFactory = circuitIdFactory ?? throw new ArgumentNullException(nameof(circuitIdFactory));
}
@ -72,6 +74,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
var dispatcher = Renderer.CreateDefaultDispatcher();
var renderer = new RemoteRenderer(
scope.ServiceProvider,
_loggerFactory,
rendererRegistry,
jsRuntime,
client,
@ -94,6 +97,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
jsRuntime,
circuitHandlers,
_loggerFactory.CreateLogger<CircuitHost>());
Log.CreatedCircuit(_logger, circuitHost);
// Initialize per - circuit data that services need
(circuitHost.Services.GetRequiredService<ICircuitAccessor>() as DefaultCircuitAccessor).Circuit = circuitHost.Circuit;
@ -124,5 +128,26 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
return componentsMetadata;
}
}
private static class Log
{
private static readonly Action<ILogger, string, string, Exception> _createdConnectedCircuit =
LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(1, "CreatedConnectedCircuit"), "Created circuit {CircuitId} for connection {ConnectionId}");
private static readonly Action<ILogger, string, Exception> _createdDisconnectedCircuit =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(2, "CreatedDisconnectedCircuit"), "Created circuit {CircuitId} for disconnected client");
internal static void CreatedCircuit(ILogger logger, CircuitHost circuitHost)
{
if (circuitHost.Client.Connected)
{
_createdConnectedCircuit(logger, circuitHost.CircuitId, circuitHost.Client.ConnectionId, null);
}
else
{
_createdDisconnectedCircuit(logger, circuitHost.CircuitId, null);
}
}
}
}
}

View File

@ -37,13 +37,14 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
/// </summary>
public RemoteRenderer(
IServiceProvider serviceProvider,
ILoggerFactory loggerFactory,
RendererRegistry rendererRegistry,
IJSRuntime jsRuntime,
CircuitClientProxy client,
IDispatcher dispatcher,
HtmlEncoder encoder,
ILogger logger)
: base(serviceProvider, encoder.Encode, dispatcher)
: base(serviceProvider, loggerFactory, dispatcher, encoder.Encode)
{
_rendererRegistry = rendererRegistry;
_jsRuntime = jsRuntime;
@ -179,7 +180,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
return;
}
Log.BeginUpdateDisplayAsync(_logger, _client.ConnectionId);
Log.BeginUpdateDisplayAsync(_logger, _client.ConnectionId, pending.BatchId, pending.Data.Length);
await _client.SendAsync("JS.RenderBatch", Id, pending.BatchId, pending.Data);
}
catch (Exception e)
@ -212,10 +213,15 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
}
else
{
var message = $"Completing batch {entry.BatchId} " +
(errorMessageOrNull == null ? "without error." : "with error.");
if (errorMessageOrNull == null)
{
Log.CompletingBatchWithoutError(_logger, entry.BatchId);
}
else
{
Log.CompletingBatchWithError(_logger, entry.BatchId, errorMessageOrNull);
}
_logger.LogDebug(message);
CompleteRender(entry.CompletionSource, errorMessageOrNull);
}
}
@ -260,9 +266,11 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
private static class Log
{
private static readonly Action<ILogger, string, Exception> _unhandledExceptionRenderingComponent;
private static readonly Action<ILogger, string, Exception> _beginUpdateDisplayAsync;
private static readonly Action<ILogger, long, int, string, Exception> _beginUpdateDisplayAsync;
private static readonly Action<ILogger, string, Exception> _bufferingRenderDisconnectedClient;
private static readonly Action<ILogger, string, Exception> _sendBatchDataFailed;
private static readonly Action<ILogger, long, string, Exception> _completingBatchWithError;
private static readonly Action<ILogger, long, Exception> _completingBatchWithoutError;
private static class EventIds
{
@ -270,6 +278,8 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
public static readonly EventId BeginUpdateDisplayAsync = new EventId(101, "BeginUpdateDisplayAsync");
public static readonly EventId SkipUpdateDisplayAsync = new EventId(102, "SkipUpdateDisplayAsync");
public static readonly EventId SendBatchDataFailed = new EventId(103, "SendBatchDataFailed");
public static readonly EventId CompletingBatchWithError = new EventId(104, "CompletingBatchWithError");
public static readonly EventId CompletingBatchWithoutError = new EventId(105, "CompletingBatchWithoutError");
}
static Log()
@ -279,13 +289,13 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
EventIds.UnhandledExceptionRenderingComponent,
"Unhandled exception rendering component: {Message}");
_beginUpdateDisplayAsync = LoggerMessage.Define<string>(
LogLevel.Trace,
_beginUpdateDisplayAsync = LoggerMessage.Define<long, int, string>(
LogLevel.Debug,
EventIds.BeginUpdateDisplayAsync,
"Begin remote rendering of components on client {ConnectionId}.");
"Sending render batch {BatchId} of size {DataLength} bytes to client {ConnectionId}.");
_bufferingRenderDisconnectedClient = LoggerMessage.Define<string>(
LogLevel.Trace,
LogLevel.Debug,
EventIds.SkipUpdateDisplayAsync,
"Buffering remote render because the client on connection {ConnectionId} is disconnected.");
@ -293,6 +303,16 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
LogLevel.Information,
EventIds.SendBatchDataFailed,
"Sending data for batch failed: {Message}");
_completingBatchWithError = LoggerMessage.Define<long, string>(
LogLevel.Debug,
EventIds.CompletingBatchWithError,
"Completing batch {BatchId} with error: {ErrorMessage}");
_completingBatchWithoutError = LoggerMessage.Define<long>(
LogLevel.Debug,
EventIds.CompletingBatchWithoutError,
"Completing batch {BatchId} without error");
}
public static void SendBatchDataFailed(ILogger logger, Exception exception)
@ -308,10 +328,12 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
exception);
}
public static void BeginUpdateDisplayAsync(ILogger logger, string connectionId)
public static void BeginUpdateDisplayAsync(ILogger logger, string connectionId, long batchId, int dataLength)
{
_beginUpdateDisplayAsync(
logger,
batchId,
dataLength,
connectionId,
null);
}
@ -323,6 +345,23 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
connectionId,
null);
}
public static void CompletingBatchWithError(ILogger logger, long batchId, string errorMessage)
{
_completingBatchWithError(
logger,
batchId,
errorMessage,
null);
}
public static void CompletingBatchWithoutError(ILogger logger, long batchId)
{
_completingBatchWithoutError(
logger,
batchId,
null);
}
}
}
}

View File

@ -59,8 +59,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
Interop.ListenForNavigationEvents,
typeof(RemoteUriHelper).Assembly.GetName().Name,
nameof(NotifyLocationChanged));
_logger.LogDebug($"{nameof(RemoteUriHelper)} initialized.");
}
/// <summary>
@ -75,18 +73,18 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
var message = $"{nameof(NotifyLocationChanged)} called without a circuit.";
throw new InvalidOperationException(message);
}
var uriHelper = (RemoteUriHelper)circuit.Services.GetRequiredService<IUriHelper>();
Log.ReceivedLocationChangedNotification(uriHelper._logger, uriAbsolute, isInterceptedLink);
uriHelper.SetAbsoluteUri(uriAbsolute);
uriHelper._logger.LogDebug($"Location changed to '{uriAbsolute}'.");
uriHelper.TriggerOnLocationChanged(isInterceptedLink);
}
/// <inheritdoc />
protected override void NavigateToCore(string uri, bool forceLoad)
{
_logger.LogDebug($"{uri} force load {forceLoad}.");
Log.RequestingNavigation(_logger, uri, forceLoad);
if (_jsRuntime == null)
{
@ -94,5 +92,24 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
}
_jsRuntime.InvokeAsync<object>(Interop.NavigateTo, uri, forceLoad);
}
private static class Log
{
private static readonly Action<ILogger, string, bool, Exception> _requestingNavigation =
LoggerMessage.Define<string, bool>(LogLevel.Debug, new EventId(1, "RequestingNavigation"), "Requesting navigation to URI {Uri} with forceLoad={ForceLoad}");
private static readonly Action<ILogger, string, bool, Exception> _receivedLocationChangedNotification =
LoggerMessage.Define<string, bool>(LogLevel.Debug, new EventId(2, "ReceivedLocationChangedNotification"), "Received notification that the URI has changed to {Uri} with isIntercepted={IsIntercepted}");
public static void RequestingNavigation(ILogger logger, string uri, bool forceLoad)
{
_requestingNavigation(logger, uri, forceLoad, null);
}
public static void ReceivedLocationChangedNotification(ILogger logger, string uri, bool isIntercepted)
{
_receivedLocationChangedNotification(logger, uri, isIntercepted, null);
}
}
}
}

View File

@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.Components.Server
var endpointFeature = Context.GetHttpContext().Features.Get<IEndpointFeature>();
var endpoint = endpointFeature?.Endpoint;
_logger.LogDebug($"No components registered in the current endpoint '{endpoint.DisplayName}'.");
Log.NoComponentsRegisteredInEndpoint(_logger, endpoint.DisplayName);
// No components preregistered so return. This is totally normal if the components were prerendered.
return null;
@ -131,16 +131,18 @@ namespace Microsoft.AspNetCore.Components.Server
/// </summary>
public void OnRenderCompleted(long renderId, string errorMessageOrNull)
{
_logger.LogDebug($"Received confirmation for batch {renderId}.");
Log.ReceivedConfirmationForBatch(_logger, renderId);
EnsureCircuitHost().Renderer.OnRenderCompleted(renderId, errorMessageOrNull);
}
private async void CircuitHost_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var circuitHost = (CircuitHost)sender;
var circuitId = circuitHost?.CircuitId;
try
{
_logger.LogWarning((Exception)e.ExceptionObject, "Unhandled Server-Side exception");
Log.UnhandledExceptionInCircuit(_logger, circuitId, (Exception)e.ExceptionObject);
await circuitHost.Client.SendAsync("JS.Error", e.ExceptionObject);
// We generally can't abort the connection here since this is an async
@ -149,7 +151,7 @@ namespace Microsoft.AspNetCore.Components.Server
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to transmit exception to client");
Log.FailedToTransmitException(_logger, circuitId, ex);
}
}
@ -164,5 +166,40 @@ namespace Microsoft.AspNetCore.Components.Server
return circuitHost;
}
private static class Log
{
private static readonly Action<ILogger, string, Exception> _noComponentsRegisteredInEndpoint =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(1, "NoComponentsRegisteredInEndpoint"), "No components registered in the current endpoint '{Endpoint}'");
private static readonly Action<ILogger, long, Exception> _receivedConfirmationForBatch =
LoggerMessage.Define<long>(LogLevel.Debug, new EventId(2, "ReceivedConfirmationForBatch"), "Received confirmation for batch {BatchId}");
private static readonly Action<ILogger, string, Exception> _unhandledExceptionInCircuit =
LoggerMessage.Define<string>(LogLevel.Warning, new EventId(3, "UnhandledExceptionInCircuit"), "Unhandled exception in circuit {CircuitId}");
private static readonly Action<ILogger, string, Exception> _failedToTransmitException =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(4, "FailedToTransmitException"), "Failed to transmit exception to client in circuit {CircuitId}");
public static void NoComponentsRegisteredInEndpoint(ILogger logger, string endpointDisplayName)
{
_noComponentsRegisteredInEndpoint(logger, endpointDisplayName, null);
}
public static void ReceivedConfirmationForBatch(ILogger logger, long batchId)
{
_receivedConfirmationForBatch(logger, batchId, null);
}
public static void UnhandledExceptionInCircuit(ILogger logger, string circuitId, Exception exception)
{
_unhandledExceptionInCircuit(logger, circuitId, exception);
}
public static void FailedToTransmitException(ILogger logger, string circuitId, Exception transmissionException)
{
_failedToTransmitException(logger, circuitId, transmissionException);
}
}
}
}

View File

@ -242,7 +242,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
private class TestRemoteRenderer : RemoteRenderer
{
public TestRemoteRenderer(IServiceProvider serviceProvider, RendererRegistry rendererRegistry, IDispatcher dispatcher, IJSRuntime jsRuntime, IClientProxy client)
: base(serviceProvider, rendererRegistry, jsRuntime, new CircuitClientProxy(client, "connection"), dispatcher, HtmlEncoder.Default, NullLogger.Instance)
: base(serviceProvider, NullLoggerFactory.Instance, rendererRegistry, jsRuntime, new CircuitClientProxy(client, "connection"), dispatcher, HtmlEncoder.Default, NullLogger.Instance)
{
}

View File

@ -264,6 +264,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
return new RemoteRenderer(
serviceProvider,
NullLoggerFactory.Instance,
new RendererRegistry(),
jsRuntime.Object,
circuitClientProxy,

View File

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Collections.Generic;
using System.IO;
@ -375,7 +376,7 @@ namespace Microsoft.AspNetCore.Components.Server
class FakeRenderer : Renderer
{
public FakeRenderer()
: base(new ServiceCollection().BuildServiceProvider(), new RendererSynchronizationContext())
: base(new ServiceCollection().BuildServiceProvider(), NullLoggerFactory.Instance, new RendererSynchronizationContext())
{
}

View File

@ -46,6 +46,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
{
remoteRenderer = new RemoteRenderer(
serviceScope.ServiceProvider ?? Mock.Of<IServiceProvider>(),
NullLoggerFactory.Instance,
new RendererRegistry(),
jsRuntime,
clientProxy,

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.Components.Test.Helpers
@ -17,11 +18,11 @@ namespace Microsoft.AspNetCore.Components.Test.Helpers
{
}
public TestRenderer(IDispatcher dispatcher) : base(new TestServiceProvider(), dispatcher)
public TestRenderer(IDispatcher dispatcher) : base(new TestServiceProvider(), NullLoggerFactory.Instance, dispatcher)
{
}
public TestRenderer(IServiceProvider serviceProvider) : base(serviceProvider, new RendererSynchronizationContext())
public TestRenderer(IServiceProvider serviceProvider) : base(serviceProvider, NullLoggerFactory.Instance, new RendererSynchronizationContext())
{
}

View File

@ -6,11 +6,13 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Castle.Core.Logging;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Ignitor
@ -333,7 +335,7 @@ namespace Ignitor
class FakeRenderer : Renderer
{
public FakeRenderer()
: base(new ServiceCollection().BuildServiceProvider(), new RendererSynchronizationContext())
: base(new ServiceCollection().BuildServiceProvider(), NullLoggerFactory.Instance, new RendererSynchronizationContext())
{
}

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
namespace Ignitor
{
@ -288,7 +289,7 @@ namespace Ignitor
public class FakeRenderer : Renderer
{
public FakeRenderer()
: base(new ServiceCollection().BuildServiceProvider(), new RendererSynchronizationContext())
: base(new ServiceCollection().BuildServiceProvider(), NullLoggerFactory.Instance, new RendererSynchronizationContext())
{
}

View File

@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.RazorComponents
{
@ -32,7 +33,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.RazorComponents
var dispatcher = Renderer.CreateDefaultDispatcher();
InitializeUriHelper(httpContext);
using (var htmlRenderer = new HtmlRenderer(httpContext.RequestServices, _encoder.Encode, dispatcher))
var loggerFactory = (ILoggerFactory)httpContext.RequestServices.GetService(typeof (ILoggerFactory));
using (var htmlRenderer = new HtmlRenderer(httpContext.RequestServices, loggerFactory, dispatcher, _encoder.Encode))
{
ComponentRenderedText result = default;
try

View File

@ -13,6 +13,8 @@ using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures.RazorComponents;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.JSInterop;
using Microsoft.Net.Http.Headers;
using Moq;
@ -224,6 +226,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
services.AddSingleton<IJSRuntime,UnsupportedJavaScriptRuntime>();
services.AddSingleton<IUriHelper,HttpUriHelper>();
services.AddSingleton<StaticComponentRenderer>();
services.AddSingleton<ILoggerFactory, NullLoggerFactory>();
configureServices?.Invoke(services);