From db6c3d8647745fffe270efef66217d689ceb184b Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Mon, 29 Apr 2024 15:33:35 -0700 Subject: [PATCH 1/4] Lazily import ILoggerProviders This change makes our AbstractLoggerFactory take a set of Lazy instances and only realize them the first time a message is logged. --- .../RazorLanguageServerBenchmarkBase.cs | 2 +- .../RazorLanguageServer.LoggerFactoryWrapper.cs | 2 +- .../AbstractLoggerFactory.AggregateLogger.cs | 16 +++++++++------- .../Logging/AbstractLoggerFactory.cs | 16 +++++++++------- .../Logging/VisualStudioLoggerFactory.cs | 3 ++- src/Razor/src/rzls/LoggerFactory.cs | 3 ++- .../Logging/TestOutputLoggerFactory.cs | 2 +- 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs index 50d6d8246f3..686c7e6434e 100644 --- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs +++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs @@ -108,7 +108,7 @@ public Task SendRequestAsync(string method, TPara } } - internal class NoopLoggerFactory() : AbstractLoggerFactory([new NoopLoggerProvider()]); + internal class NoopLoggerFactory() : AbstractLoggerFactory([new(() => new NoopLoggerProvider())]); internal class NoopLoggerProvider : ILoggerProvider { diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.LoggerFactoryWrapper.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.LoggerFactoryWrapper.cs index 479e9d09797..4899cad1fd8 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.LoggerFactoryWrapper.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.LoggerFactoryWrapper.cs @@ -14,7 +14,7 @@ internal partial class RazorLanguageServer /// private sealed class LoggerFactoryWrapper(ILoggerFactory loggerFactory) : ILoggerFactory { - private ILoggerFactory _loggerFactory = loggerFactory; + private readonly ILoggerFactory _loggerFactory = loggerFactory; public void AddLoggerProvider(ILoggerProvider provider) { diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs index 39a4ab4ecdc..51e42f20b4e 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs @@ -8,15 +8,15 @@ namespace Microsoft.CodeAnalysis.Razor.Logging; internal abstract partial class AbstractLoggerFactory { - private class AggregateLogger(ImmutableArray loggers) : ILogger + private class AggregateLogger(ImmutableArray> lazyLoggers) : ILogger { - private ImmutableArray _loggers = loggers; + private ImmutableArray> _lazyLoggers = lazyLoggers; public bool IsEnabled(LogLevel logLevel) { - foreach (var logger in _loggers) + foreach (var lazyLogger in _lazyLoggers) { - if (logger.IsEnabled(logLevel)) + if (lazyLogger.Value.IsEnabled(logLevel)) { return true; } @@ -27,8 +27,10 @@ public bool IsEnabled(LogLevel logLevel) public void Log(LogLevel logLevel, string message, Exception? exception) { - foreach (var logger in _loggers) + foreach (var lazyLogger in _lazyLoggers) { + var logger = lazyLogger.Value; + if (logger.IsEnabled(logLevel)) { logger.Log(logLevel, message, exception); @@ -36,9 +38,9 @@ public void Log(LogLevel logLevel, string message, Exception? exception) } } - internal void AddLogger(ILogger logger) + internal void AddLogger(Lazy lazyLogger) { - ImmutableInterlocked.Update(ref _loggers, (set, l) => set.Add(l), logger); + ImmutableInterlocked.Update(ref _lazyLoggers, (set, l) => set.Add(l), lazyLogger); } } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.cs index 70ae45dbf27..41dfd580f6e 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.cs @@ -13,10 +13,10 @@ namespace Microsoft.CodeAnalysis.Razor.Logging; internal abstract partial class AbstractLoggerFactory : ILoggerFactory { + private ImmutableArray> _providers; private ImmutableDictionary _loggers; - private ImmutableArray _providers; - protected AbstractLoggerFactory(ImmutableArray providers) + protected AbstractLoggerFactory(ImmutableArray> providers) { _providers = providers; _loggers = ImmutableDictionary.Create(StringComparer.OrdinalIgnoreCase); @@ -29,24 +29,26 @@ public ILogger GetOrCreateLogger(string categoryName) return logger; } - using var loggers = new PooledArrayBuilder(_providers.Length); + using var lazyLoggers = new PooledArrayBuilder>(_providers.Length); foreach (var provider in _providers) { - loggers.Add(provider.CreateLogger(categoryName)); + lazyLoggers.Add(new(() => provider.Value.CreateLogger(categoryName))); } - var result = new AggregateLogger(loggers.DrainToImmutable()); + var result = new AggregateLogger(lazyLoggers.DrainToImmutable()); return ImmutableInterlocked.AddOrUpdate(ref _loggers, categoryName, result, (k, v) => v); } public void AddLoggerProvider(ILoggerProvider provider) { - if (ImmutableInterlocked.Update(ref _providers, (set, p) => set.Add(p), provider)) + var lazyProvider = new Lazy(() => provider); + + if (ImmutableInterlocked.Update(ref _providers, (set, p) => set.Add(p), lazyProvider)) { foreach (var (category, logger) in _loggers) { - logger.AddLogger(provider.CreateLogger(category)); + logger.AddLogger(new(() => provider.CreateLogger(category))); } } } diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/VisualStudioLoggerFactory.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/VisualStudioLoggerFactory.cs index 2a68757a076..8688e9c60f2 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/VisualStudioLoggerFactory.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/VisualStudioLoggerFactory.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.Composition; @@ -10,7 +11,7 @@ namespace Microsoft.VisualStudio.Razor.Logging; [Export(typeof(ILoggerFactory))] [method: ImportingConstructor] -internal sealed class VisualStudioLoggerFactory([ImportMany] IEnumerable providers) +internal sealed class VisualStudioLoggerFactory([ImportMany] IEnumerable> providers) : AbstractLoggerFactory(providers.ToImmutableArray()) { } diff --git a/src/Razor/src/rzls/LoggerFactory.cs b/src/Razor/src/rzls/LoggerFactory.cs index d4c5d8425ff..15c3a3de945 100644 --- a/src/Razor/src/rzls/LoggerFactory.cs +++ b/src/Razor/src/rzls/LoggerFactory.cs @@ -1,12 +1,13 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. +using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Razor.Logging; namespace Microsoft.AspNetCore.Razor.LanguageServer; -internal sealed class LoggerFactory(ImmutableArray providers) +internal sealed class LoggerFactory(ImmutableArray> providers) : AbstractLoggerFactory(providers) { } diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLoggerFactory.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLoggerFactory.cs index b9af1ce005d..1e03d338dfd 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLoggerFactory.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLoggerFactory.cs @@ -7,6 +7,6 @@ namespace Microsoft.AspNetCore.Razor.Test.Common.Logging; internal sealed class TestOutputLoggerFactory(ITestOutputHelper output) - : AbstractLoggerFactory([new TestOutputLoggerProvider(output)]) + : AbstractLoggerFactory([new(() => new TestOutputLoggerProvider(output))]) { } From 414d4fe74ef28f1f4b48faae450c0179bafa21d2 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 30 Apr 2024 10:34:08 -0700 Subject: [PATCH 2/4] Remove unused and bugged(!) helper method --- .../DictionaryExtensions.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/DictionaryExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/DictionaryExtensions.cs index cb57d0c3aad..3abd74568d3 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/DictionaryExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/DictionaryExtensions.cs @@ -76,21 +76,4 @@ public static TValue GetOrAdd( return value; } } - - public static TValue GetValueOrDefault( - this Dictionary dictionary, - TKey key, - TValue defaultValue) - where TKey : notnull - { - if (dictionary.TryGetValue(key, out var existingValue)) - { - return existingValue; - } - else - { - dictionary.Add(key, defaultValue); - return defaultValue; - } - } } From b4bdfe0c4eb8fe1e8963901a994fb7b7dfc58a11 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 30 Apr 2024 10:53:04 -0700 Subject: [PATCH 3/4] Add export attribute to allow logger providers to optionally provider minimum log level --- .../RazorLanguageServerBenchmarkBase.cs | 3 +- .../AbstractLoggerFactory.AggregateLogger.cs | 34 ++++++++++++++----- .../Logging/AbstractLoggerFactory.cs | 19 +++++++---- .../Logging/LoggerProviderMetadata.cs | 25 ++++++++++++++ .../Logging/RazorLogHubLogger.cs | 12 ++----- .../Logging/RazorLogHubLoggerProvider.cs | 13 +++---- .../Logging/ActivityLogLoggerProvider.cs | 2 +- .../Logging/ExportLoggerProviderAttribute.cs | 27 +++++++++++++++ .../Logging/MemoryLoggerProvider.cs | 6 ++-- .../Logging/OutputWindowLoggerProvider.cs | 2 +- .../Logging/VisualStudioLoggerFactory.cs | 2 +- src/Razor/src/rzls/LoggerFactory.cs | 2 +- .../Logging/TestOutputLoggerFactory.cs | 2 +- 13 files changed, 105 insertions(+), 44 deletions(-) create mode 100644 src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/LoggerProviderMetadata.cs create mode 100644 src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/ExportLoggerProviderAttribute.cs diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs index 686c7e6434e..e4bb22ffc79 100644 --- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs +++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs @@ -19,7 +19,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.Extensions.DependencyInjection; -using Microsoft.VisualStudio.LanguageServer.Protocol; using Nerdbank.Streams; namespace Microsoft.AspNetCore.Razor.Microbenchmarks.LanguageServer; @@ -108,7 +107,7 @@ public Task SendRequestAsync(string method, TPara } } - internal class NoopLoggerFactory() : AbstractLoggerFactory([new(() => new NoopLoggerProvider())]); + internal class NoopLoggerFactory() : AbstractLoggerFactory([new NoopLoggerProvider()]); internal class NoopLoggerProvider : ILoggerProvider { diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs index 51e42f20b4e..5cf9ec9e950 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs @@ -8,15 +8,35 @@ namespace Microsoft.CodeAnalysis.Razor.Logging; internal abstract partial class AbstractLoggerFactory { - private class AggregateLogger(ImmutableArray> lazyLoggers) : ILogger + private sealed class LazyLogger(Lazy lazyProvider, string categoryName) { - private ImmutableArray> _lazyLoggers = lazyLoggers; + private readonly LoggerProviderMetadata _metadata = lazyProvider.Metadata; + private readonly Lazy _lazyLogger = new(() => lazyProvider.Value.CreateLogger(categoryName)); + + public ILogger Instance => _lazyLogger.Value; + + public bool IsEnabled(LogLevel logLevel) + { + // If the ILoggerProvider's metadata has a minimum log level, we can use that + // rather than forcing the ILoggerProvider to be created. + if (_metadata.MinimumLogLevel is LogLevel minimumLogLevel) + { + return logLevel.IsAtLeast(minimumLogLevel); + } + + return Instance.IsEnabled(logLevel); + } + } + + private class AggregateLogger(ImmutableArray lazyLoggers) : ILogger + { + private ImmutableArray _lazyLoggers = lazyLoggers; public bool IsEnabled(LogLevel logLevel) { foreach (var lazyLogger in _lazyLoggers) { - if (lazyLogger.Value.IsEnabled(logLevel)) + if (lazyLogger.IsEnabled(logLevel)) { return true; } @@ -29,16 +49,14 @@ public void Log(LogLevel logLevel, string message, Exception? exception) { foreach (var lazyLogger in _lazyLoggers) { - var logger = lazyLogger.Value; - - if (logger.IsEnabled(logLevel)) + if (lazyLogger.IsEnabled(logLevel)) { - logger.Log(logLevel, message, exception); + lazyLogger.Instance.Log(logLevel, message, exception); } } } - internal void AddLogger(Lazy lazyLogger) + internal void AddLogger(LazyLogger lazyLogger) { ImmutableInterlocked.Update(ref _lazyLoggers, (set, l) => set.Add(l), lazyLogger); } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.cs index 41dfd580f6e..5e4ff3f21c7 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.cs @@ -13,10 +13,15 @@ namespace Microsoft.CodeAnalysis.Razor.Logging; internal abstract partial class AbstractLoggerFactory : ILoggerFactory { - private ImmutableArray> _providers; + private ImmutableArray> _providers; private ImmutableDictionary _loggers; - protected AbstractLoggerFactory(ImmutableArray> providers) + protected AbstractLoggerFactory(ImmutableArray providers) + : this(providers.SelectAsArray(p => new Lazy(() => p, LoggerProviderMetadata.Empty))) + { + } + + protected AbstractLoggerFactory(ImmutableArray> providers) { _providers = providers; _loggers = ImmutableDictionary.Create(StringComparer.OrdinalIgnoreCase); @@ -29,11 +34,11 @@ public ILogger GetOrCreateLogger(string categoryName) return logger; } - using var lazyLoggers = new PooledArrayBuilder>(_providers.Length); + using var lazyLoggers = new PooledArrayBuilder(_providers.Length); foreach (var provider in _providers) { - lazyLoggers.Add(new(() => provider.Value.CreateLogger(categoryName))); + lazyLoggers.Add(new(provider, categoryName)); } var result = new AggregateLogger(lazyLoggers.DrainToImmutable()); @@ -42,13 +47,13 @@ public ILogger GetOrCreateLogger(string categoryName) public void AddLoggerProvider(ILoggerProvider provider) { - var lazyProvider = new Lazy(() => provider); + var lazyProvider = new Lazy(() => provider, LoggerProviderMetadata.Empty); if (ImmutableInterlocked.Update(ref _providers, (set, p) => set.Add(p), lazyProvider)) { - foreach (var (category, logger) in _loggers) + foreach (var (categoryName, logger) in _loggers) { - logger.AddLogger(new(() => provider.CreateLogger(category))); + logger.AddLogger(new(lazyProvider, categoryName)); } } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/LoggerProviderMetadata.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/LoggerProviderMetadata.cs new file mode 100644 index 00000000000..bc49df3801e --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/LoggerProviderMetadata.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.CodeAnalysis.Razor.Logging; + +internal sealed class LoggerProviderMetadata +{ + public static LoggerProviderMetadata Empty { get; } = new(); + + public LogLevel? MinimumLogLevel { get; } + + private LoggerProviderMetadata() + { + } + + public LoggerProviderMetadata(IDictionary data) + : this() + { + MinimumLogLevel = data.TryGetValue(nameof(MinimumLogLevel), out var minimumLogLevel) + ? (LogLevel?)minimumLogLevel + : null; + } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Logging/RazorLogHubLogger.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Logging/RazorLogHubLogger.cs index 2b30a860fa7..5831dfa19e5 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Logging/RazorLogHubLogger.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Logging/RazorLogHubLogger.cs @@ -9,16 +9,10 @@ namespace Microsoft.VisualStudio.Razor.LanguageClient.Logging; -internal sealed class RazorLogHubLogger : ILogger +internal sealed class RazorLogHubLogger(string categoryName, RazorLogHubTraceProvider traceProvider) : ILogger { - private string _categoryName; - private RazorLogHubTraceProvider _traceProvider; - - public RazorLogHubLogger(string categoryName, RazorLogHubTraceProvider traceProvider) - { - _categoryName = categoryName; - _traceProvider = traceProvider; - } + private readonly string _categoryName = categoryName; + private readonly RazorLogHubTraceProvider _traceProvider = traceProvider; public bool IsEnabled(LogLevel logLevel) { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Logging/RazorLogHubLoggerProvider.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Logging/RazorLogHubLoggerProvider.cs index 1625ff868dc..fee3b5a76ef 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Logging/RazorLogHubLoggerProvider.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Logging/RazorLogHubLoggerProvider.cs @@ -7,16 +7,11 @@ namespace Microsoft.VisualStudio.Razor.LanguageClient.Logging; -[Export(typeof(ILoggerProvider))] -internal sealed class RazorLogHubLoggerProvider : ILoggerProvider +[ExportLoggerProvider] +[method: ImportingConstructor] +internal sealed class RazorLogHubLoggerProvider(RazorLogHubTraceProvider traceProvider) : ILoggerProvider { - private readonly RazorLogHubTraceProvider _traceProvider; - - [ImportingConstructor] - public RazorLogHubLoggerProvider(RazorLogHubTraceProvider traceProvider) - { - _traceProvider = traceProvider; - } + private readonly RazorLogHubTraceProvider _traceProvider = traceProvider; public ILogger CreateLogger(string categoryName) { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/ActivityLogLoggerProvider.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/ActivityLogLoggerProvider.cs index 85f005fd87f..274f9953520 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/ActivityLogLoggerProvider.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/ActivityLogLoggerProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Logging; /// /// An that logs any warnings or errors to the Visual Studio Activity Log. /// -[Export(typeof(ILoggerProvider))] +[ExportLoggerProvider(minimumLogLevel: LogLevel.Warning)] [method: ImportingConstructor] internal sealed partial class ActivityLogLoggerProvider(RazorActivityLog activityLog) : ILoggerProvider { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/ExportLoggerProviderAttribute.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/ExportLoggerProviderAttribute.cs new file mode 100644 index 00000000000..804c99b4057 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/ExportLoggerProviderAttribute.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.ComponentModel.Composition; +using Microsoft.CodeAnalysis.Razor.Logging; + +namespace Microsoft.VisualStudio.Razor.Logging; + +[MetadataAttribute] +[AttributeUsage(AttributeTargets.Class)] +internal sealed class ExportLoggerProviderAttribute : ExportAttribute +{ + public LogLevel? MinimumLogLevel { get; } + + public ExportLoggerProviderAttribute() + : base(typeof(ILoggerProvider)) + { + MinimumLogLevel = null; + } + + public ExportLoggerProviderAttribute(LogLevel minimumLogLevel) + : base(typeof(ILoggerProvider)) + { + MinimumLogLevel = minimumLogLevel; + } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/MemoryLoggerProvider.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/MemoryLoggerProvider.cs index f60b0644857..1d06f244c8b 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/MemoryLoggerProvider.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/MemoryLoggerProvider.cs @@ -1,14 +1,12 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. -using System.ComponentModel.Composition; using Microsoft.CodeAnalysis.Razor.Logging; namespace Microsoft.VisualStudio.Razor.Logging; -[Export(typeof(ILoggerProvider))] -[method: ImportingConstructor] -internal partial class MemoryLoggerProvider() : ILoggerProvider +[ExportLoggerProvider] +internal partial class MemoryLoggerProvider : ILoggerProvider { // How many messages will the buffer contain private const int BufferSize = 5000; diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/OutputWindowLoggerProvider.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/OutputWindowLoggerProvider.cs index 0d84a995c64..70a775d6120 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/OutputWindowLoggerProvider.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/OutputWindowLoggerProvider.cs @@ -14,7 +14,7 @@ namespace Microsoft.VisualStudio.Razor.Logging; -[Export(typeof(ILoggerProvider))] +[ExportLoggerProvider] [method: ImportingConstructor] internal class OutputWindowLoggerProvider( // Anything this class imports would have a circular dependency if they tried to log anything, diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/VisualStudioLoggerFactory.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/VisualStudioLoggerFactory.cs index 8688e9c60f2..6475495b043 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/VisualStudioLoggerFactory.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Logging/VisualStudioLoggerFactory.cs @@ -11,7 +11,7 @@ namespace Microsoft.VisualStudio.Razor.Logging; [Export(typeof(ILoggerFactory))] [method: ImportingConstructor] -internal sealed class VisualStudioLoggerFactory([ImportMany] IEnumerable> providers) +internal sealed class VisualStudioLoggerFactory([ImportMany] IEnumerable> providers) : AbstractLoggerFactory(providers.ToImmutableArray()) { } diff --git a/src/Razor/src/rzls/LoggerFactory.cs b/src/Razor/src/rzls/LoggerFactory.cs index 15c3a3de945..d6213effb6a 100644 --- a/src/Razor/src/rzls/LoggerFactory.cs +++ b/src/Razor/src/rzls/LoggerFactory.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer; -internal sealed class LoggerFactory(ImmutableArray> providers) +internal sealed class LoggerFactory(ImmutableArray> providers) : AbstractLoggerFactory(providers) { } diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLoggerFactory.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLoggerFactory.cs index 1e03d338dfd..b9af1ce005d 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLoggerFactory.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Logging/TestOutputLoggerFactory.cs @@ -7,6 +7,6 @@ namespace Microsoft.AspNetCore.Razor.Test.Common.Logging; internal sealed class TestOutputLoggerFactory(ITestOutputHelper output) - : AbstractLoggerFactory([new(() => new TestOutputLoggerProvider(output))]) + : AbstractLoggerFactory([new TestOutputLoggerProvider(output)]) { } From 66566cd5ac9fcbdef21b64512d6ef3f261bae143 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 30 Apr 2024 14:10:20 -0700 Subject: [PATCH 4/4] Ensure that we still call ILogger.IsEnabled(...) --- .../Logging/AbstractLoggerFactory.AggregateLogger.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs index 5cf9ec9e950..30fb12947f5 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Logging/AbstractLoggerFactory.AggregateLogger.cs @@ -19,9 +19,10 @@ public bool IsEnabled(LogLevel logLevel) { // If the ILoggerProvider's metadata has a minimum log level, we can use that // rather than forcing the ILoggerProvider to be created. - if (_metadata.MinimumLogLevel is LogLevel minimumLogLevel) + if (_metadata.MinimumLogLevel is LogLevel minimumLogLevel && + !logLevel.IsAtLeast(minimumLogLevel)) { - return logLevel.IsAtLeast(minimumLogLevel); + return false; } return Instance.IsEnabled(logLevel);