Remove WebAssemblyLoggerFactory and refactor logging setup (#20432)
**Changes in this PR** - Replaces `WebAssemblyLoggerFactory` with `LoggerFactory` from logging extensions package - Moves WebAssemblyConsoleLogger and PrependMessageLogger to provider model Now that we are using the standard `LoggerFactory` support for config options like `SetMinimumLevel` and `AddProvider` is available. Compared to what is currently in the `blazor-wasm` branch, the changes in this PR add an additional 12 kb to the total compressed size. Addresses #19737
This commit is contained in:
parent
41a9588353
commit
e67e7a08ca
|
|
@ -56,7 +56,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
|
|||
"Microsoft.Extensions.FileProviders.Abstractions.dll",
|
||||
"Microsoft.Extensions.FileProviders.Physical.dll",
|
||||
"Microsoft.Extensions.FileSystemGlobbing.dll",
|
||||
"Microsoft.Extensions.Logging.dll",
|
||||
"Microsoft.Extensions.Logging.Abstractions.dll",
|
||||
"Microsoft.Extensions.Options.dll",
|
||||
"Microsoft.Extensions.Primitives.dll",
|
||||
"Microsoft.JSInterop.dll",
|
||||
"Microsoft.JSInterop.WebAssembly.dll",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
||||
{
|
||||
internal class LoggingBuilder : ILoggingBuilder
|
||||
{
|
||||
public LoggingBuilder(IServiceCollection services)
|
||||
{
|
||||
Services = services;
|
||||
}
|
||||
|
||||
public IServiceCollection Services { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -55,6 +55,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
|||
Configuration = new ConfigurationBuilder();
|
||||
RootComponents = new RootComponentMappingCollection();
|
||||
Services = new ServiceCollection();
|
||||
Logging = new LoggingBuilder(Services);
|
||||
|
||||
Logging.SetMinimumLevel(LogLevel.Warning);
|
||||
|
||||
// Retrieve required attributes from JSRuntimeInvoker
|
||||
InitializeNavigationManager(jsRuntimeInvoker);
|
||||
|
|
@ -128,6 +131,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
|||
/// </summary>
|
||||
public IWebAssemblyHostEnvironment HostEnvironment { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logging builder for configuring logging services.
|
||||
/// </summary>
|
||||
public ILoggingBuilder Logging { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="IServiceProviderFactory{TBuilder}" /> instance to be used to create the <see cref="IServiceProvider" />.
|
||||
/// </summary>
|
||||
|
|
@ -186,8 +194,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
|||
Services.AddSingleton<IJSRuntime>(DefaultWebAssemblyJSRuntime.Instance);
|
||||
Services.AddSingleton<NavigationManager>(WebAssemblyNavigationManager.Instance);
|
||||
Services.AddSingleton<INavigationInterception>(WebAssemblyNavigationInterception.Instance);
|
||||
Services.AddSingleton<ILoggerFactory, WebAssemblyLoggerFactory>();
|
||||
Services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(WebAssemblyConsoleLogger<>)));
|
||||
Services.AddLogging(builder => {
|
||||
builder.AddProvider(new WebAssemblyConsoleLoggerProvider(DefaultWebAssemblyJSRuntime.Instance));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore.Components.Web" />
|
||||
<Reference Include="Microsoft.Extensions.Configuration.Json" />
|
||||
<Reference Include="Microsoft.Extensions.Logging" />
|
||||
<Reference Include="Microsoft.JSInterop.WebAssembly" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -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.Concurrent;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.JSInterop;
|
||||
|
|
@ -43,7 +44,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
|
|||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return logLevel >= LogLevel.Warning && logLevel != LogLevel.None;
|
||||
return logLevel != LogLevel.None;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.WebAssembly.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// A provider of <see cref="WebAssemblyConsoleLogger{T}"/> instances.
|
||||
/// </summary>
|
||||
internal class WebAssemblyConsoleLoggerProvider : ILoggerProvider
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, WebAssemblyConsoleLogger<object>> _loggers;
|
||||
private readonly IJSInProcessRuntime _jsRuntime;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of <see cref="WebAssemblyConsoleLoggerProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="options">The options to create <see cref="WebAssemblyConsoleLogger"/> instances with.</param>
|
||||
public WebAssemblyConsoleLoggerProvider(IJSInProcessRuntime jsRuntime)
|
||||
{
|
||||
_loggers = new ConcurrentDictionary<string, WebAssemblyConsoleLogger<object>>();
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ILogger CreateLogger(string name)
|
||||
{
|
||||
return _loggers.GetOrAdd(name, loggerName => new WebAssemblyConsoleLogger<object>(name, _jsRuntime));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// 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.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.WebAssembly.Services
|
||||
{
|
||||
internal class WebAssemblyLoggerFactory : ILoggerFactory
|
||||
{
|
||||
private readonly IJSInProcessRuntime _jsRuntime;
|
||||
|
||||
public WebAssemblyLoggerFactory(IServiceProvider services)
|
||||
{
|
||||
_jsRuntime = (IJSInProcessRuntime)services.GetRequiredService<IJSRuntime>();
|
||||
}
|
||||
|
||||
// We might implement this in the future, but it's not required currently
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
=> new WebAssemblyConsoleLogger<object>(categoryName, _jsRuntime);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -230,12 +230,28 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
|||
// Arrange & Act
|
||||
var builder = new WebAssemblyHostBuilder(new TestWebAssemblyJSRuntimeInvoker());
|
||||
|
||||
// Assert
|
||||
Assert.Equal(DefaultServiceTypes.Count, builder.Services.Count);
|
||||
foreach (var type in DefaultServiceTypes)
|
||||
{
|
||||
Assert.Single(builder.Services, d => d.ServiceType == type);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Builder_SupportsConfiguringLogging()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new WebAssemblyHostBuilder(new TestWebAssemblyJSRuntimeInvoker());
|
||||
var provider = new Mock<ILoggerProvider>();
|
||||
|
||||
// Act
|
||||
builder.Logging.AddProvider(provider.Object);
|
||||
var host = builder.Build();
|
||||
|
||||
// Assert
|
||||
var loggerProvider = host.Services.GetRequiredService<ILoggerProvider>();
|
||||
Assert.NotNull(loggerProvider);
|
||||
Assert.Equal<ILoggerProvider>(provider.Object, loggerProvider);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,10 +85,11 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
|||
Browser.FindElement(By.Id("log-none")).Click();
|
||||
Browser.FindElement(By.Id("log-trace")).Click();
|
||||
Browser.FindElement(By.Id("log-debug")).Click();
|
||||
Browser.FindElement(By.Id("log-information")).Click();
|
||||
AssertLastLogMessage(LogLevel.Info, "Test log message");
|
||||
|
||||
// These severity levels are displayed
|
||||
Browser.FindElement(By.Id("log-information")).Click();
|
||||
AssertLastLogMessage(LogLevel.Info, "[Custom logger] This is a Information message with count=4");
|
||||
Browser.FindElement(By.Id("log-warning")).Click();
|
||||
AssertLastLogMessage(LogLevel.Warning, "[Custom logger] This is a Warning message with count=5");
|
||||
Browser.FindElement(By.Id("log-error")).Click();
|
||||
|
|
|
|||
|
|
@ -3,33 +3,42 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Services;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BasicTestApp
|
||||
{
|
||||
// The goal for this class is to make it possible for E2E tests to observe that a custom
|
||||
// logger factory can be plugged in and gets used when logging unhandled exceptions.
|
||||
// However, it's valuable to pass through all calls to the default implementation too
|
||||
// so that if any defect in the underlying implementation would break tests, we still see it.
|
||||
|
||||
public class PrependMessageLoggerFactory : ILoggerFactory
|
||||
internal class PrependMessageLoggerProvider : ILoggerProvider
|
||||
{
|
||||
private readonly string _message;
|
||||
private readonly ILoggerFactory _underlyingFactory;
|
||||
ILogger _logger;
|
||||
string _message;
|
||||
ILogger _defaultLogger;
|
||||
private bool _disposed = false;
|
||||
|
||||
public PrependMessageLoggerFactory(string message, ILoggerFactory underlyingFactory)
|
||||
public PrependMessageLoggerProvider(string message, IJSRuntime runtime)
|
||||
{
|
||||
_message = message;
|
||||
_underlyingFactory = underlyingFactory;
|
||||
_defaultLogger = new WebAssemblyConsoleLogger<object>(runtime);
|
||||
}
|
||||
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
=> _underlyingFactory.AddProvider(provider);
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
=> new PrependMessageLogger(_message, _underlyingFactory.CreateLogger(categoryName));
|
||||
{
|
||||
if (_logger == null)
|
||||
{
|
||||
_logger = new PrependMessageLogger(_message, _defaultLogger);
|
||||
}
|
||||
return _logger;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> _underlyingFactory.Dispose();
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_logger = null;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private class PrependMessageLogger : ILogger
|
||||
{
|
||||
|
|
@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Components.WebAssembly.Http;
|
|||
using Microsoft.AspNetCore.Components.WebAssembly.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BasicTestApp
|
||||
{
|
||||
|
|
@ -44,15 +45,8 @@ namespace BasicTestApp
|
|||
policy.RequireAssertion(ctx => ctx.User.Identity.Name?.StartsWith("B") ?? false));
|
||||
});
|
||||
|
||||
// Replace the default logger with a custom one that wraps it
|
||||
var originalLoggerDescriptor = builder.Services.Single(d => d.ServiceType == typeof(ILoggerFactory));
|
||||
builder.Services.AddSingleton<ILoggerFactory>(services =>
|
||||
{
|
||||
var originalLogger = (ILoggerFactory)Activator.CreateInstance(
|
||||
originalLoggerDescriptor.ImplementationType,
|
||||
new object[] { services });
|
||||
return new PrependMessageLoggerFactory("Custom logger", originalLogger);
|
||||
});
|
||||
builder.Logging.Services.AddSingleton<ILoggerProvider, PrependMessageLoggerProvider>(s => new PrependMessageLoggerProvider("Custom logger", s.GetService<IJSRuntime>()));
|
||||
builder.Logging.SetMinimumLevel(LogLevel.Information);
|
||||
|
||||
var host = builder.Build();
|
||||
ConfigureCulture(host);
|
||||
|
|
|
|||
Loading…
Reference in New Issue