From af24628b577d53d4cb6a72f325a139b299c72ebe Mon Sep 17 00:00:00 2001 From: stbychkov Date: Sun, 4 May 2025 17:59:10 +0800 Subject: [PATCH 1/9] Implemented generation of LoggerMessage.DefineScope functors to replace standard ILogger.BeginScope invocations --- ...ADR-07_Generation_of_BeginScope_methods.md | 46 + .../GenericLoggerExtensions.g.cs | 997 ++++++++++++++++++ .../GenericLoggerScopeExtensions.g.cs | 49 + .../AutoLoggerMessageGenerator.Sandbox.csproj | 1 + .../Program.cs | 16 +- .../InvalidTemplateParameterNameAnalyser.cs | 36 +- .../AutoLoggerMessageGenerator.Build.targets | 3 + ...toLoggerMessageGenerator.Roslyn4_11.csproj | 9 + .../Caching/InputSourceComparer.cs | 18 - .../Caching/LogCallInputSourceComparer.cs | 23 + .../LogScopeDefinitionInputSourceComparer.cs | 19 + .../Configuration/GeneratorOptionsProvider.cs | 4 +- .../SourceGeneratorConfiguration.cs | 3 +- src/AutoLoggerMessageGenerator/Constants.cs | 8 +- ...ter.cs => LogMessageDiagnosticReporter.cs} | 8 +- ...r.cs => GenericLoggerExtensionsEmitter.cs} | 2 +- .../GenericLoggerScopeExtensionsEmitter.cs | 74 ++ .../Emitters/LoggerInterceptorsEmitter.cs | 11 +- .../LoggerScopeInterceptorsEmitter.cs | 58 + .../Emitters/LoggerScopesEmitter.cs | 63 ++ ...xtractor.cs => EnclosingClassExtractor.cs} | 8 +- .../Extractors/LogCallExtractor.cs | 10 +- .../Extractors/LogCallParametersExtractor.cs | 21 +- .../Extractors/LoggerScopeCallExtractor.cs | 34 + ...r.cs => MessageParameterNamesExtractor.cs} | 2 +- ...or.cs => MessageParameterTextExtractor.cs} | 2 +- ...gCallFilter.cs => LogMessageCallFilter.cs} | 11 +- .../Filters/LoggerScopeFilter.cs | 25 + ...utoLoggerMessageGenerator.LoggerMessage.cs | 118 +++ ...AutoLoggerMessageGenerator.LoggerScopes.cs | 64 ++ .../Generators/AutoLoggerMessageGenerator.cs | 111 +- ...ocationMapper.cs => CallLocationMapper.cs} | 8 +- .../{LogCallLocation.cs => CallLocation.cs} | 4 +- .../{LogCallParameter.cs => CallParameter.cs} | 4 +- ...lParameterType.cs => CallParameterType.cs} | 2 +- .../Models/{LogCall.cs => LogMessageCall.cs} | 20 +- .../Models/LoggerScopeCall.cs | 42 + .../Utilities/TypeAccessibilityChecker.cs | 10 + ...ionMap.cs => LogMessageCallLocationMap.cs} | 12 +- .../VirtualLoggerMessageClassBuilder.cs | 22 +- ...erMessageGenerator.IntegrationTests.csproj | 4 +- .../BeginScopeWithAllParameterRangeTests.cs | 78 ++ ...atchProxyExecutionVerificationDecorator.cs | 8 +- .../ExecutionSourceTests.cs | 137 --- .../LogPropertiesAttributeTests.cs | 4 +- .../LogWithAllParameterRangeTests.cs | 127 +++ ...erMessageGenerator.UnitTests.Build.targets | 6 +- .../BaseSourceGeneratorTest.cs | 2 +- .../Caching/InputSourceComparerTests.cs | 33 +- ...lidLoggingExtensionsOverrides.verified.txt | 4 +- ...ggingScopeExtensionsOverrides.verified.txt | 50 + .../Emitters/LoggerExtensionsEmitterTests.cs | 6 +- .../LoggerInterceptorsEmitterTests.cs | 28 +- .../LoggerScopeExtensionsEmitterTests.cs | 14 + ...eValidLoggerScopeInterceptors.verified.txt | 36 + .../LoggerScopeInterceptorsEmitterTests.cs | 53 + ...lidLoggerDefineScopedFunctors.verified.txt | 31 + .../Emitters/LoggerScopesEmitterTests.cs | 53 + ...sts.cs => EnclosingClassExtractorTests.cs} | 10 +- ...rceCode=HashBasedInterceptor.verified.txt} | 9 +- ...rceCode=PathBasedInterceptor.verified.txt} | 9 +- ...urceCode=HashBasedInterceptor.verified.txt | 43 + ...urceCode=PathBasedInterceptor.verified.txt | 43 + .../Extractors/LogCallExtractorTests.cs | 13 +- .../LogCallParametersExtractorTests.cs | 46 +- .../Extractors/LogLevelExtractorTests.cs | 4 +- ...rceCode=HashBasedInterceptor.verified.txt} | 17 +- ...urceCode=PathBasedInterceptor.verified.txt | 55 + .../LoggerScopeCallExtractorTests.cs | 29 + ...=> MessageParameterNamesExtractorTests.cs} | 4 +- ... => MessageParameterTextExtractorTests.cs} | 26 +- ...rTests.cs => LogMessageCallFilterTests.cs} | 12 +- .../Filters/LoggerScopeFilterTests.cs | 94 ++ .../InstanceCallVsExtensionCallTests.cs | 29 + .../Utilities/IdentifierHelperTests.cs | 1 + .../MockLogCallLocationBuilder.cs | 4 +- .../LogCallExtractorTests.cs | 22 +- ...eDeclaration_310af49b1b682fdf.verified.txt | 12 + ...ssageDeclaration_all_disabled.verified.txt | 12 + ...essageDeclaration_all_enabled.verified.txt | 12 + ...eDeclaration_b6a745e4a78dd4ac.verified.txt | 12 + ...eDeclaration_e7fa423334e4b1a0.verified.txt | 12 + ...eDeclaration_ed18b4fdbf0bda16.verified.txt | 12 + ...ration_only_telemetry_enabled.verified.txt | 12 + ...eclaration_telemetry_disabled.verified.txt | 12 + ...peSequences_ShouldBuildAsItIs.verified.txt | 4 +- .../VirtualLoggerMessageClassBuilderTests.cs | 76 +- 87 files changed, 2799 insertions(+), 499 deletions(-) create mode 100644 docs/ADR/ADR-07_Generation_of_BeginScope_methods.md create mode 100644 src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerExtensions.g.cs create mode 100644 src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerScopeExtensions.g.cs delete mode 100644 src/AutoLoggerMessageGenerator/Caching/InputSourceComparer.cs create mode 100644 src/AutoLoggerMessageGenerator/Caching/LogCallInputSourceComparer.cs create mode 100644 src/AutoLoggerMessageGenerator/Caching/LogScopeDefinitionInputSourceComparer.cs rename src/AutoLoggerMessageGenerator/Diagnostics/{DiagnosticReporter.cs => LogMessageDiagnosticReporter.cs} (76%) rename src/AutoLoggerMessageGenerator/Emitters/{LoggerExtensionMethodsEmitter.cs => GenericLoggerExtensionsEmitter.cs} (99%) create mode 100644 src/AutoLoggerMessageGenerator/Emitters/GenericLoggerScopeExtensionsEmitter.cs create mode 100644 src/AutoLoggerMessageGenerator/Emitters/LoggerScopeInterceptorsEmitter.cs create mode 100644 src/AutoLoggerMessageGenerator/Emitters/LoggerScopesEmitter.cs rename src/AutoLoggerMessageGenerator/Extractors/{LogCallCallerExtractor.cs => EnclosingClassExtractor.cs} (92%) create mode 100644 src/AutoLoggerMessageGenerator/Extractors/LoggerScopeCallExtractor.cs rename src/AutoLoggerMessageGenerator/Extractors/{LogCallMessageParameterNamesExtractor.cs => MessageParameterNamesExtractor.cs} (88%) rename src/AutoLoggerMessageGenerator/Extractors/{LogCallMessageExtractor.cs => MessageParameterTextExtractor.cs} (97%) rename src/AutoLoggerMessageGenerator/Filters/{LogCallFilter.cs => LogMessageCallFilter.cs} (75%) create mode 100644 src/AutoLoggerMessageGenerator/Filters/LoggerScopeFilter.cs create mode 100644 src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.LoggerMessage.cs create mode 100644 src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.LoggerScopes.cs rename src/AutoLoggerMessageGenerator/Mappers/{LogCallLocationMapper.cs => CallLocationMapper.cs} (85%) rename src/AutoLoggerMessageGenerator/Models/{LogCallLocation.cs => CallLocation.cs} (87%) rename src/AutoLoggerMessageGenerator/Models/{LogCallParameter.cs => CallParameter.cs} (64%) rename src/AutoLoggerMessageGenerator/Models/{LogCallParameterType.cs => CallParameterType.cs} (79%) rename src/AutoLoggerMessageGenerator/Models/{LogCall.cs => LogMessageCall.cs} (64%) create mode 100644 src/AutoLoggerMessageGenerator/Models/LoggerScopeCall.cs create mode 100644 src/AutoLoggerMessageGenerator/Utilities/TypeAccessibilityChecker.cs rename src/AutoLoggerMessageGenerator/VirtualLoggerMessage/{LogCallLocationMap.cs => LogMessageCallLocationMap.cs} (56%) create mode 100644 tests/AutoLoggerMessageGenerator.IntegrationTests/BeginScopeWithAllParameterRangeTests.cs delete mode 100644 tests/AutoLoggerMessageGenerator.IntegrationTests/ExecutionSourceTests.cs create mode 100644 tests/AutoLoggerMessageGenerator.IntegrationTests/LogWithAllParameterRangeTests.cs rename src/AutoLoggerMessageGenerator.BuildOutput/LoggerExtensions.g.cs => tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingExtensionsOverrides.verified.txt (99%) create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerScopeExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingScopeExtensionsOverrides.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeExtensionsEmitterTests.cs create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerScopeInterceptors.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.cs create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopesEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerDefineScopedFunctors.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopesEmitterTests.cs rename tests/AutoLoggerMessageGenerator.UnitTests/Extractors/{LogCallCallerExtractorTests.cs => EnclosingClassExtractorTests.cs} (79%) rename tests/AutoLoggerMessageGenerator.UnitTests/Extractors/{LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_HashBasedInterceptor.verified.txt => LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt} (93%) rename tests/AutoLoggerMessageGenerator.UnitTests/Extractors/{LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_PathBasedInterceptor.verified.txt => LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt} (93%) create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=without parameters_sourceCode=HashBasedInterceptor.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=without parameters_sourceCode=PathBasedInterceptor.verified.txt rename tests/AutoLoggerMessageGenerator.UnitTests/Extractors/{LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_sourceCode=logger.LogInformation(-Hello world {arg1} {arg2}-, 1, true);_isValidCall=True.verified.txt => LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt} (76%) create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.cs rename tests/AutoLoggerMessageGenerator.UnitTests/Extractors/{LogCallMessageParameterNamesExtractorTests.cs => MessageParameterNamesExtractorTests.cs} (88%) rename tests/AutoLoggerMessageGenerator.UnitTests/Extractors/{LogCallMessageExtractorTests.cs => MessageParameterTextExtractorTests.cs} (75%) rename tests/AutoLoggerMessageGenerator.UnitTests/Filters/{LogCallFilterTests.cs => LogMessageCallFilterTests.cs} (88%) create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/Filters/LoggerScopeFilterTests.cs create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/MethodSpecificityRules/InstanceCallVsExtensionCallTests.cs rename tests/AutoLoggerMessageGenerator.UnitTests/{ => Utilities}/MockLogCallLocationBuilder.cs (73%) create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_310af49b1b682fdf.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_all_disabled.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_all_enabled.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_b6a745e4a78dd4ac.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_e7fa423334e4b1a0.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_ed18b4fdbf0bda16.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_only_telemetry_enabled.verified.txt create mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_telemetry_disabled.verified.txt diff --git a/docs/ADR/ADR-07_Generation_of_BeginScope_methods.md b/docs/ADR/ADR-07_Generation_of_BeginScope_methods.md new file mode 100644 index 0000000..34be801 --- /dev/null +++ b/docs/ADR/ADR-07_Generation_of_BeginScope_methods.md @@ -0,0 +1,46 @@ +### Title: ADR-07 Generation of `BeginScope` methods +### Status: Accepted + +### Context: + +The existing `ILogger.BeginScope` method has the same problems as the `ILogger.Log*` methods: +1. Lack of compile-time checks for mismatches between template parameters and the actual arguments, which can lead to runtime exceptions. +2. Performance overhead due to the need to create a new scope object every time. +3. Unnecessary allocations due to boxing of template parameters, even with identical arguments. + +`ILogger.BeginScope` is backed by `LoggerMessage.DefineScope`, which allows generation of strongly-typed, precompiled delegates with up to 6 parameters. This enables high-performance and allocation-free scope creation, similar to how `LoggerMessage.Define` is used for logging methods. + +The main difference is that `ILogger.BeginScope` **with only one parameter** is an instance method, not an extension method. This means we cannot intercept or replace calls that only use a single message argument (i.e., `BeginScope("Starting operation")`) because the instance method takes precedence over extension methods. Therefore, generation will be limited to `BeginScope` calls with one or more structured parameters (i.e., key-value pairs or anonymous objects), which typically benefit the most from strongly-typed scope generation. +[Reference test](https://github.com/stbychkov/AutoLoggerMessage/blob/main/tests/AutoLoggerMessageGenerator.UnitTests/MethodSpecificityRules/InstanceCallVsExtensionCallTests.cs) + +### Decision: + +Extend the AutoLoggerMessage source generator to support generation of strongly-typed scope delegates using `LoggerMessage.DefineScope`. + +Specifically: +- Identify all usages of `ILogger.BeginScope` in the codebase where the call includes **at least one structured argument** (excluding pure string messages). +- For each identified scope usage: + - Generate a static readonly field that contains the compiled scope delegate using `LoggerMessage.DefineScope`. + - Generate an extension method (or internal interceptor method) that redirects the original `BeginScope` call to the generated delegate, preserving structure and performance. +- Ensure that the generated methods follow the same naming, visibility, and partial class strategy as existing `Log*` method interceptors. + +### Consequences: + +* **Short-term**: + - Improves performance and reduces allocations for scoped logging with parameters. + - Introduces new source generation complexity; testing must be extended to validate generated scopes. + +* **Long-term**: + - Moves the library closer to complete compile-time safety for all common logging patterns (`Log*` and `BeginScope`). + +* **Risks**: + - May cause confusion if developers attempt to use `BeginScope(string message)` expecting interception (which is not supported). + - Reliance on exact call shapes (number and types of arguments) may introduce fragility unless thoroughly tested. + +* **Maintenance**: + - Must track and test against future changes in `LoggerMessage.DefineScope` API (currently supports up to 6 parameters). + - Increases the surface area of generated code, potentially impacting future refactors or compatibility with downstream tools. + +### Alternatives Considered + +* **Do nothing**: Keep relying on `ILogger.BeginScope` as is. This maintains simplicity but misses out on performance and compile-time safety. diff --git a/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerExtensions.g.cs b/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerExtensions.g.cs new file mode 100644 index 0000000..bdd555c --- /dev/null +++ b/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerExtensions.g.cs @@ -0,0 +1,997 @@ +// +#nullable enable + +using System; + +namespace Microsoft.Extensions.Logging +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("AutoLoggerMessageGenerator", "1.0.10.0")] + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [System.Diagnostics.DebuggerStepThrough] + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public static class GenericLoggerExtensions + { + public static void LogTrace(this ILogger @logger, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @message); + } + + public static void LogDebug(this ILogger @logger, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @message); + } + + public static void LogInformation(this ILogger @logger, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @message); + } + + public static void LogWarning(this ILogger @logger, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @message); + } + + public static void LogError(this ILogger @logger, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @message); + } + + public static void LogCritical(this ILogger @logger, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @message); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @message); + } + + public static void LogTrace(this ILogger @logger, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @message, new object?[] { @arg0 }); + } + + public static void LogDebug(this ILogger @logger, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @message, new object?[] { @arg0 }); + } + + public static void LogInformation(this ILogger @logger, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @message, new object?[] { @arg0 }); + } + + public static void LogWarning(this ILogger @logger, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @message, new object?[] { @arg0 }); + } + + public static void LogError(this ILogger @logger, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @message, new object?[] { @arg0 }); + } + + public static void LogCritical(this ILogger @logger, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @message, new object?[] { @arg0 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @message, new object?[] { @arg0 }); + } + + public static void LogTrace(this ILogger @logger, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogDebug(this ILogger @logger, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogInformation(this ILogger @logger, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogWarning(this ILogger @logger, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogError(this ILogger @logger, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogCritical(this ILogger @logger, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @message, new object?[] { @arg0, @arg1 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogTrace(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogDebug(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogInformation(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogWarning(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogError(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogCritical(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogTrace(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogDebug(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogInformation(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogWarning(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogError(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogCritical(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogTrace(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogDebug(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogInformation(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogWarning(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogError(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogCritical(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogTrace(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogDebug(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogInformation(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogWarning(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogError(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogCritical(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + + public static void LogTrace(this ILogger @logger, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @exception, @message); + } + + public static void LogDebug(this ILogger @logger, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @exception, @message); + } + + public static void LogInformation(this ILogger @logger, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @exception, @message); + } + + public static void LogWarning(this ILogger @logger, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @exception, @message); + } + + public static void LogError(this ILogger @logger, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @exception, @message); + } + + public static void LogCritical(this ILogger @logger, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @exception, @message); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @exception, @message); + } + + public static void LogTrace(this ILogger @logger, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogDebug(this ILogger @logger, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogInformation(this ILogger @logger, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogWarning(this ILogger @logger, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogError(this ILogger @logger, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogCritical(this ILogger @logger, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @exception, @message, new object?[] { @arg0 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogTrace(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogDebug(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogInformation(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogWarning(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogError(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogCritical(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogTrace(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogDebug(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogInformation(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogWarning(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogError(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogCritical(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogTrace(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogDebug(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogInformation(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogWarning(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogError(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogCritical(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogTrace(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogDebug(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogInformation(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogWarning(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogError(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogCritical(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogTrace(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogDebug(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogInformation(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogWarning(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogError(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogCritical(this ILogger @logger, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + + public static void LogTrace(this ILogger @logger, EventId @eventId, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @message); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @message); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @message); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @message); + } + + public static void LogError(this ILogger @logger, EventId @eventId, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @message); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @message); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @message); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @message, new object?[] { @arg0 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @message, new object?[] { @arg0 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @message, new object?[] { @arg0 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @message, new object?[] { @arg0 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @message, new object?[] { @arg0 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @message, new object?[] { @arg0 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @message, new object?[] { @arg0 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @message, new object?[] { @arg0, @arg1 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + + public static void LogTrace(this ILogger @logger, EventId @eventId, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @exception, @message); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @exception, @message); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @exception, @message); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @exception, @message); + } + + public static void LogError(this ILogger @logger, EventId @eventId, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @exception, @message); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @exception, @message); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, Exception? @exception, string @message) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @exception, @message); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @exception, @message, new object?[] { @arg0 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, Exception? @exception, string @message, T0 @arg0) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @exception, @message, new object?[] { @arg0 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @exception, @message, new object?[] { @arg0, @arg1 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static void LogTrace(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogTrace(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogDebug(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogDebug(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogInformation(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogWarning(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogError(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogError(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void LogCritical(this ILogger @logger, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.LogCritical(@logger, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + public static void Log(this ILogger @logger, Microsoft.Extensions.Logging.LogLevel @logLevel, EventId @eventId, Exception? @exception, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + Microsoft.Extensions.Logging.LoggerExtensions.Log(@logger, @logLevel, @eventId, @exception, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + } +} diff --git a/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerScopeExtensions.g.cs b/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerScopeExtensions.g.cs new file mode 100644 index 0000000..fc52c4e --- /dev/null +++ b/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerScopeExtensions.g.cs @@ -0,0 +1,49 @@ +// +#nullable enable + +using System; + +namespace Microsoft.Extensions.Logging +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("AutoLoggerMessageGenerator", "1.0.10.0")] + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [System.Diagnostics.DebuggerStepThrough] + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public static class GenericLoggerScopeExtensions + { + public static IDisposable? BeginScope(this ILogger @logger, string @message) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + } +} diff --git a/src/AutoLoggerMessageGenerator.Sandbox/AutoLoggerMessageGenerator.Sandbox.csproj b/src/AutoLoggerMessageGenerator.Sandbox/AutoLoggerMessageGenerator.Sandbox.csproj index 26e9c77..fecc1cc 100644 --- a/src/AutoLoggerMessageGenerator.Sandbox/AutoLoggerMessageGenerator.Sandbox.csproj +++ b/src/AutoLoggerMessageGenerator.Sandbox/AutoLoggerMessageGenerator.Sandbox.csproj @@ -9,6 +9,7 @@ + - builder.AddSimpleConsole().SetMinimumLevel(LogLevel.Trace) + builder.AddSimpleConsole(options => + { + options.IncludeScopes = true; + }).SetMinimumLevel(LogLevel.Trace) ); var logger = loggerFactory.CreateLogger(); +using var parentScope = logger.BeginScope("Scope {Level}", 1); + logger.LogTrace("Log message without parameters"); logger.LogDebug("Log message with parameters: {Param1}, {Param2}", 42, "Hello, World!"); -logger.LogInformation("Log message with 6 parameters: {Arg1}, {Arg2}, {Arg3}, {Arg4}, {Arg5} {Arg6}", 1, 2, 3, 4, 5, 6); -logger.LogWarning(new EventId(42, "Event1"), "Event1 happened"); + +using (logger.BeginScope("Scope {Level}", 2)) +{ + logger.LogInformation("Log message with 6 parameters: {Arg1}, {Arg2}, {Arg3}, {Arg4}, {Arg5} {Arg6}", 1, 2, 3, 4, 5, 6); + logger.LogWarning(new EventId(42, "Event1"), "Event1 happened"); +} + logger.LogError(new EventId(42, "Event1"), new Exception("Event1 error"), "Event1 happened"); logger.LogCritical(new EventId(42, "Event2"), new Exception("Event2 error"), "Event2 happened {Arg1}", new EventData(123, "Event details")); diff --git a/src/AutoLoggerMessageGenerator/Analysers/InvalidTemplateParameterNameAnalyser.cs b/src/AutoLoggerMessageGenerator/Analysers/InvalidTemplateParameterNameAnalyser.cs index 104c51b..305a252 100644 --- a/src/AutoLoggerMessageGenerator/Analysers/InvalidTemplateParameterNameAnalyser.cs +++ b/src/AutoLoggerMessageGenerator/Analysers/InvalidTemplateParameterNameAnalyser.cs @@ -31,33 +31,55 @@ public override void Initialize(AnalysisContext context) context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); - context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); + context.RegisterSyntaxNodeAction(AnalyzeLoggerMessageNode, SyntaxKind.InvocationExpression); + context.RegisterSyntaxNodeAction(AnalyzeLoggerScopeNode, SyntaxKind.InvocationExpression); } - private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + private static void AnalyzeLoggerMessageNode(SyntaxNodeAnalysisContext context) { var invocationExpression = (InvocationExpressionSyntax)context.Node; - if (!LogCallFilter.IsLogCallInvocation(invocationExpression, context.CancellationToken)) + if (!LogMessageCallFilter.IsLogCallInvocation(invocationExpression, context.CancellationToken)) return; var semanticModel = context.SemanticModel; var methodSymbol = semanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol; - if (methodSymbol is null || !LogCallFilter.IsLoggerMethod(methodSymbol)) + if (methodSymbol is null || !LogMessageCallFilter.IsLoggerMethod(methodSymbol)) return; - var message = LogCallMessageExtractor.Extract(methodSymbol, invocationExpression, semanticModel); + AnalyzeNode(context, methodSymbol, invocationExpression, semanticModel); + } + + private static void AnalyzeLoggerScopeNode(SyntaxNodeAnalysisContext context) + { + var invocationExpression = (InvocationExpressionSyntax)context.Node; + + if (!LoggerScopeFilter.IsLoggerScopeInvocation(invocationExpression, context.CancellationToken)) + return; + + var semanticModel = context.SemanticModel; + var methodSymbol = semanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol; + if (methodSymbol is null || !LoggerScopeFilter.IsLoggerScopeMethod(methodSymbol)) + return; + + AnalyzeNode(context, methodSymbol, invocationExpression, semanticModel); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context, IMethodSymbol methodSymbol, + InvocationExpressionSyntax invocationExpression, SemanticModel semanticModel) + { + var message = MessageParameterTextExtractor.Extract(methodSymbol, invocationExpression, semanticModel); if (message is null) return; - var templateParameterNames = LogCallMessageParameterNamesExtractor.Extract(message) + var templateParameterNames = MessageParameterNamesExtractor.Extract(message) .Where(parameterName => !IdentifierHelper.IsValidCSharpParameterName(parameterName)) .ToImmutableArray(); if (!templateParameterNames.Any()) return; - var location = LogCallLocationMapper.Map(semanticModel, invocationExpression); + var location = CallLocationMapper.Map(semanticModel, invocationExpression); if (location is null) return; diff --git a/src/AutoLoggerMessageGenerator/AutoLoggerMessageGenerator.Build.targets b/src/AutoLoggerMessageGenerator/AutoLoggerMessageGenerator.Build.targets index 0ca7096..629fee6 100644 --- a/src/AutoLoggerMessageGenerator/AutoLoggerMessageGenerator.Build.targets +++ b/src/AutoLoggerMessageGenerator/AutoLoggerMessageGenerator.Build.targets @@ -54,6 +54,9 @@ <_Parameter1>$(AssemblyName).UnitTests.Roslyn4_11 + + <_Parameter1>$(AssemblyName).IntegrationTests + <_Parameter1>$(AssemblyName).Benchmarks diff --git a/src/AutoLoggerMessageGenerator/AutoLoggerMessageGenerator.Roslyn4_11.csproj b/src/AutoLoggerMessageGenerator/AutoLoggerMessageGenerator.Roslyn4_11.csproj index b92484b..c445972 100644 --- a/src/AutoLoggerMessageGenerator/AutoLoggerMessageGenerator.Roslyn4_11.csproj +++ b/src/AutoLoggerMessageGenerator/AutoLoggerMessageGenerator.Roslyn4_11.csproj @@ -7,5 +7,14 @@ false + + + AutoLoggerMessageGenerator.cs + + + AutoLoggerMessageGenerator.cs + + + diff --git a/src/AutoLoggerMessageGenerator/Caching/InputSourceComparer.cs b/src/AutoLoggerMessageGenerator/Caching/InputSourceComparer.cs deleted file mode 100644 index 4bb42a4..0000000 --- a/src/AutoLoggerMessageGenerator/Caching/InputSourceComparer.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Immutable; -using AutoLoggerMessageGenerator.Configuration; -using AutoLoggerMessageGenerator.Models; -using AutoLoggerMessageGenerator.ReferenceAnalyzer; -using Microsoft.CodeAnalysis; - -namespace AutoLoggerMessageGenerator.Caching; - -internal class InputSourceComparer : IEqualityComparer<(Compilation Compilation, (SourceGeneratorConfiguration Configuration, (ImmutableArray References, ImmutableArray LogCalls) Others) Others)> -{ - public bool Equals((Compilation Compilation, (SourceGeneratorConfiguration Configuration, (ImmutableArray References, ImmutableArray LogCalls) Others) Others) x, (Compilation Compilation, (SourceGeneratorConfiguration Configuration, (ImmutableArray References, ImmutableArray LogCalls) Others) Others) y) => - x.Others.Configuration == y.Others.Configuration && - x.Others.Others.References.SequenceEqual(y.Others.Others.References) && - x.Others.Others.LogCalls.SequenceEqual(y.Others.Others.LogCalls); - - public int GetHashCode((Compilation Compilation, (SourceGeneratorConfiguration Configuration, (ImmutableArray References, ImmutableArray LogCalls) Others) Others) obj) => - obj.Item2.GetHashCode(); -} diff --git a/src/AutoLoggerMessageGenerator/Caching/LogCallInputSourceComparer.cs b/src/AutoLoggerMessageGenerator/Caching/LogCallInputSourceComparer.cs new file mode 100644 index 0000000..a4a308f --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Caching/LogCallInputSourceComparer.cs @@ -0,0 +1,23 @@ +using InputSource = ( + Microsoft.CodeAnalysis.Compilation Compilation, + ( + AutoLoggerMessageGenerator.Configuration.SourceGeneratorConfiguration Configuration, + ( + System.Collections.Immutable.ImmutableArray References, + System.Collections.Immutable.ImmutableArray LogCalls + ) Others + ) Others +); + +namespace AutoLoggerMessageGenerator.Caching; + +internal class LogCallInputSourceComparer : IEqualityComparer +{ + public bool Equals(InputSource x, InputSource y) => + x.Others.Configuration == y.Others.Configuration && + x.Others.Others.References.SequenceEqual(y.Others.Others.References) && + x.Others.Others.LogCalls.SequenceEqual(y.Others.Others.LogCalls); + + public int GetHashCode(InputSource obj) => + obj.Item2.GetHashCode(); +} diff --git a/src/AutoLoggerMessageGenerator/Caching/LogScopeDefinitionInputSourceComparer.cs b/src/AutoLoggerMessageGenerator/Caching/LogScopeDefinitionInputSourceComparer.cs new file mode 100644 index 0000000..c2e1480 --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Caching/LogScopeDefinitionInputSourceComparer.cs @@ -0,0 +1,19 @@ +using InputSource = ( + Microsoft.CodeAnalysis.Compilation Compilation, + ( + AutoLoggerMessageGenerator.Configuration.SourceGeneratorConfiguration Configuration, + System.Collections.Immutable.ImmutableArray LoggerScopes + ) Others +); + +namespace AutoLoggerMessageGenerator.Caching; + +internal class LogScopeDefinitionInputSourceComparer : IEqualityComparer +{ + public bool Equals(InputSource x, InputSource y) => + x.Others.Configuration == y.Others.Configuration && + x.Others.LoggerScopes.SequenceEqual(y.Others.LoggerScopes); + + public int GetHashCode(InputSource obj) => + obj.Item2.GetHashCode(); +} diff --git a/src/AutoLoggerMessageGenerator/Configuration/GeneratorOptionsProvider.cs b/src/AutoLoggerMessageGenerator/Configuration/GeneratorOptionsProvider.cs index b1549fa..0d74cc7 100644 --- a/src/AutoLoggerMessageGenerator/Configuration/GeneratorOptionsProvider.cs +++ b/src/AutoLoggerMessageGenerator/Configuration/GeneratorOptionsProvider.cs @@ -11,6 +11,7 @@ internal static class GeneratorOptionsProvider private const string GenerateOmitReferenceNameKey = $"{CommonPrefix}_{nameof(SourceGeneratorConfiguration.GenerateOmitReferenceName)}"; private const string GenerateSkipNullPropertiesKey = $"{CommonPrefix}_{nameof(SourceGeneratorConfiguration.GenerateSkipNullProperties)}"; private const string GenerateTransitiveKey = $"{CommonPrefix}_{nameof(SourceGeneratorConfiguration.GenerateTransitive)}"; + private const string OverrideBeginScopeBehavior = $"{CommonPrefix}_{nameof(SourceGeneratorConfiguration.OverrideBeginScopeBehavior)}"; public static IncrementalValueProvider Provide(IncrementalGeneratorInitializationContext context) => context.AnalyzerConfigOptionsProvider.Select((options, _) => new SourceGeneratorConfiguration( @@ -18,7 +19,8 @@ public static IncrementalValueProvider Provide(Inc GenerateSkipEnabledCheck: GetValue(options.GlobalOptions, GenerateSkipEnabledCheckKey, true), GenerateOmitReferenceName: GetValue(options.GlobalOptions, GenerateOmitReferenceNameKey, false), GenerateSkipNullProperties: GetValue(options.GlobalOptions, GenerateSkipNullPropertiesKey, false), - GenerateTransitive: GetValue(options.GlobalOptions, GenerateTransitiveKey, false) + GenerateTransitive: GetValue(options.GlobalOptions, GenerateTransitiveKey, false), + OverrideBeginScopeBehavior: GetValue(options.GlobalOptions, OverrideBeginScopeBehavior, true) )); private static bool GetValue(AnalyzerConfigOptions options, string key, bool defaultValue = true) => diff --git a/src/AutoLoggerMessageGenerator/Configuration/SourceGeneratorConfiguration.cs b/src/AutoLoggerMessageGenerator/Configuration/SourceGeneratorConfiguration.cs index a843faf..af6424b 100644 --- a/src/AutoLoggerMessageGenerator/Configuration/SourceGeneratorConfiguration.cs +++ b/src/AutoLoggerMessageGenerator/Configuration/SourceGeneratorConfiguration.cs @@ -8,5 +8,6 @@ internal record struct SourceGeneratorConfiguration bool GenerateSkipEnabledCheck, bool GenerateOmitReferenceName, bool GenerateSkipNullProperties, - bool GenerateTransitive + bool GenerateTransitive, + bool OverrideBeginScopeBehavior ); diff --git a/src/AutoLoggerMessageGenerator/Constants.cs b/src/AutoLoggerMessageGenerator/Constants.cs index 794a047..d56ba8c 100644 --- a/src/AutoLoggerMessageGenerator/Constants.cs +++ b/src/AutoLoggerMessageGenerator/Constants.cs @@ -11,6 +11,7 @@ internal static class Constants public const string InterceptorAttributeName = "InterceptsLocationAttribute"; public const string LogMethodPrefix = "Log_"; + public const string LogScopeMethodPrefix = "LogScope_"; public const string LoggerClassName = "AutoLoggerMessage"; public const string ParameterName = "@arg"; @@ -20,6 +21,9 @@ internal static class Constants public const string ExceptionParameterName = "@exception"; public const string MessageParameterName = "@message"; + public const string LoggerMessageGeneratorName = "LoggerMessage"; + public const string LoggerScopesGeneratorName = "LoggerScopes"; + public static readonly HashSet ReservedParameterNames = [ LoggerParameterName, LogLevelParameterName, @@ -28,8 +32,8 @@ internal static class Constants ]; // List of parameters that will be moved to LoggerMessage attribute arguments - public static readonly HashSet LoggerMessageAttributeParameterTypes = - [LogCallParameterType.LogLevel, LogCallParameterType.Message]; + public static readonly HashSet LoggerMessageAttributeParameterTypes = + [CallParameterType.LogLevel, CallParameterType.Message]; /// /// Support for an arbitrary number of logging parameters. LoggerMessage.Define supports a maximum of six. diff --git a/src/AutoLoggerMessageGenerator/Diagnostics/DiagnosticReporter.cs b/src/AutoLoggerMessageGenerator/Diagnostics/LogMessageDiagnosticReporter.cs similarity index 76% rename from src/AutoLoggerMessageGenerator/Diagnostics/DiagnosticReporter.cs rename to src/AutoLoggerMessageGenerator/Diagnostics/LogMessageDiagnosticReporter.cs index a57af95..bac9e0f 100644 --- a/src/AutoLoggerMessageGenerator/Diagnostics/DiagnosticReporter.cs +++ b/src/AutoLoggerMessageGenerator/Diagnostics/LogMessageDiagnosticReporter.cs @@ -5,12 +5,12 @@ namespace AutoLoggerMessageGenerator.Diagnostics; -internal class DiagnosticReporter +internal class LogMessageDiagnosticReporter { private readonly SourceProductionContext _context; - private readonly Dictionary _logCallsIndex; + private readonly Dictionary _logCallsIndex; - public DiagnosticReporter(SourceProductionContext context, IEnumerable logCalls) + public LogMessageDiagnosticReporter(SourceProductionContext context, IEnumerable logCalls) { _context = context; _logCallsIndex = logCalls.ToDictionary(c => c.Id); @@ -24,7 +24,7 @@ public void Report(Diagnostic diagnostic) var sourceText = diagnostic.Location.SourceTree?.GetText(_context.CancellationToken); var location = sourceText is not null && - LogCallLocationMap.TryMapBack(sourceText, diagnostic.Location, out var logCallId) && + LogMessageCallLocationMap.TryMapBack(sourceText, diagnostic.Location, out var logCallId) && _logCallsIndex.TryGetValue(logCallId, out var logCall) ? logCall.Location.Context : Location.None; diff --git a/src/AutoLoggerMessageGenerator/Emitters/LoggerExtensionMethodsEmitter.cs b/src/AutoLoggerMessageGenerator/Emitters/GenericLoggerExtensionsEmitter.cs similarity index 99% rename from src/AutoLoggerMessageGenerator/Emitters/LoggerExtensionMethodsEmitter.cs rename to src/AutoLoggerMessageGenerator/Emitters/GenericLoggerExtensionsEmitter.cs index ffdaabe..ea5f29a 100644 --- a/src/AutoLoggerMessageGenerator/Emitters/LoggerExtensionMethodsEmitter.cs +++ b/src/AutoLoggerMessageGenerator/Emitters/GenericLoggerExtensionsEmitter.cs @@ -9,7 +9,7 @@ namespace AutoLoggerMessageGenerator.Emitters; /// The class is pre-generated and saved in the solution /// to prevent excessive code duplication when multiple projects use the same library /// To run this emitter you need to run LoggerExtensionEmitterTests and take it from the snapshot/> -internal static class LoggerExtensionsEmitter +internal static class GenericLoggerExtensionsEmitter { public const string ClassName = "GenericLoggerExtensions"; diff --git a/src/AutoLoggerMessageGenerator/Emitters/GenericLoggerScopeExtensionsEmitter.cs b/src/AutoLoggerMessageGenerator/Emitters/GenericLoggerScopeExtensionsEmitter.cs new file mode 100644 index 0000000..e078f29 --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Emitters/GenericLoggerScopeExtensionsEmitter.cs @@ -0,0 +1,74 @@ +using System.CodeDom.Compiler; +using Microsoft.Extensions.Logging; +using static AutoLoggerMessageGenerator.Constants; + +namespace AutoLoggerMessageGenerator.Emitters; + +/// Generates the class. +/// Provides type-specific logging method overrides to avoid boxing. +/// The class is pre-generated and saved in the solution +/// to prevent excessive code duplication when multiple projects use the same library +/// To run this emitter you need to run LoggerScopeExtensionEmitterTests and take it from the snapshot +internal static class GenericLoggerScopeExtensionsEmitter +{ + public const string ClassName = "GenericLoggerScopeExtensions"; + + public static string Emit() + { + using var sb = new IndentedTextWriter(new StringWriter()); + + sb.WriteLine(GeneratedFileHeader); + + sb.WriteLine($"namespace {DefaultLoggingNamespace}"); + sb.WriteLine('{'); + sb.Indent++; + + sb.WriteLine(Constants.GeneratedCodeAttribute); + sb.WriteLine(EditorNotBrowsableAttribute); + sb.WriteLine(DebuggerStepThroughAttribute); + sb.WriteLine(ExcludeFromCoverageAttribute); + sb.WriteLine($"public static class {ClassName}"); + sb.WriteLine('{'); + sb.Indent++; + + for (int i = 0; i <= MaxLogParameters; i++) + { + var parameters = Enumerable.Range(0, i).ToArray(); + + var genericTypesDefinition = string.Join(", ", parameters.Select(ix => $"T{ix}")); + genericTypesDefinition = string.IsNullOrEmpty(genericTypesDefinition) + ? string.Empty + : $"<{genericTypesDefinition}>"; + + var genericParametersDefinition = + string.Join(", ", parameters.Select(ix => $"T{ix} {ParameterName}{ix}")); + genericParametersDefinition = string.IsNullOrEmpty(genericParametersDefinition) + ? string.Empty + : $", {genericParametersDefinition}"; + + var objectParameters = string.Join(", ", parameters.Select(ix => $"{ParameterName}{ix}")); + objectParameters = string.IsNullOrEmpty(objectParameters) + ? string.Empty + : $", new object?[] {{ {objectParameters} }}"; + + sb.WriteLine($"public static IDisposable? BeginScope{genericTypesDefinition}(this ILogger {LoggerParameterName}, string {MessageParameterName}{genericParametersDefinition})"); + sb.WriteLine('{'); + sb.Indent++; + + sb.WriteLine($"return {DefaultLoggingNamespace}.{nameof(LoggerExtensions)}.{nameof(LoggerExtensions.BeginScope)}({LoggerParameterName}, {MessageParameterName}{objectParameters});"); + + sb.Indent--; + sb.WriteLine('}'); + + sb.WriteLine(); + } + + sb.Indent--; + sb.WriteLine('}'); + + sb.Indent--; + sb.WriteLine('}'); + + return sb.InnerWriter.ToString()!; + } +} diff --git a/src/AutoLoggerMessageGenerator/Emitters/LoggerInterceptorsEmitter.cs b/src/AutoLoggerMessageGenerator/Emitters/LoggerInterceptorsEmitter.cs index b1414d2..acf2b24 100644 --- a/src/AutoLoggerMessageGenerator/Emitters/LoggerInterceptorsEmitter.cs +++ b/src/AutoLoggerMessageGenerator/Emitters/LoggerInterceptorsEmitter.cs @@ -1,12 +1,11 @@ using System.CodeDom.Compiler; using AutoLoggerMessageGenerator.Models; -using AutoLoggerMessageGenerator.Utilities; namespace AutoLoggerMessageGenerator.Emitters; internal static class LoggerInterceptorsEmitter { - public static string Emit(IEnumerable logCalls) + public static string Emit(IEnumerable logCalls) { using var sb = new IndentedTextWriter(new StringWriter()); @@ -37,15 +36,11 @@ public static string Emit(IEnumerable logCalls) .Select((c, i) => c.Name)); parameterValues = string.IsNullOrEmpty(parameterValues) ? string.Empty : $", {parameterValues}"; - var methodName = IdentifierHelper.ToValidCSharpMethodName( - $"{Constants.LogMethodPrefix}{logCall.Namespace}{logCall.ClassName}_{logCall.Location.Line}_{logCall.Location.Character}" - ); - - sb.WriteLine($"public static void {methodName}(this ILogger {Constants.LoggerParameterName}{parameters})"); + sb.WriteLine($"public static void {logCall.GeneratedMethodName}(this ILogger {Constants.LoggerParameterName}{parameters})"); sb.WriteLine('{'); sb.Indent++; - sb.WriteLine($"{Constants.GeneratorNamespace}.{Constants.LoggerClassName}.{methodName}({Constants.LoggerParameterName}{parameterValues});"); + sb.WriteLine($"{Constants.GeneratorNamespace}.{Constants.LoggerClassName}.{logCall.GeneratedMethodName}({Constants.LoggerParameterName}{parameterValues});"); sb.Indent--; sb.WriteLine('}'); diff --git a/src/AutoLoggerMessageGenerator/Emitters/LoggerScopeInterceptorsEmitter.cs b/src/AutoLoggerMessageGenerator/Emitters/LoggerScopeInterceptorsEmitter.cs new file mode 100644 index 0000000..5692afb --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Emitters/LoggerScopeInterceptorsEmitter.cs @@ -0,0 +1,58 @@ +using System.CodeDom.Compiler; +using AutoLoggerMessageGenerator.Models; + +namespace AutoLoggerMessageGenerator.Emitters; + +internal static class LoggerScopeInterceptorsEmitter +{ + public static string Emit(IEnumerable loggerScopes) + { + using var sb = new IndentedTextWriter(new StringWriter()); + + sb.WriteLine(Constants.GeneratedFileHeader); + + sb.WriteLine($"namespace {Constants.GeneratorNamespace}"); + sb.WriteLine('{'); + sb.Indent++; + + sb.WriteLine(Constants.GeneratedCodeAttribute); + sb.WriteLine(Constants.EditorNotBrowsableAttribute); + sb.WriteLine(Constants.ExcludeFromCoverageAttribute); + sb.WriteLine(Constants.DebuggerStepThroughAttribute); + sb.WriteLine("internal static class BeginScopeInterceptors"); + sb.WriteLine('{'); + sb.Indent++; + + foreach (var loggerScope in loggerScopes) + { + sb.WriteLine(Constants.EditorNotBrowsableAttribute); + sb.WriteLine(loggerScope.Location.InterceptableLocationSyntax); + + var parameters = string.Join(", ", loggerScope.Parameters.Select((c, i) => $"{c.NativeType} {c.Name}")); + parameters = string.IsNullOrEmpty(parameters) ? string.Empty : $", {parameters}"; + + var parameterValues = string.Join(", ", loggerScope.Parameters + .Where(c => c.Name != Constants.MessageParameterName) + .Select((c, i) => c.Name)); + parameterValues = string.IsNullOrEmpty(parameterValues) ? string.Empty : $", {parameterValues}"; + + sb.WriteLine($"public static IDisposable? {loggerScope.GeneratedMethodName}(this ILogger {Constants.LoggerParameterName}{parameters})"); + sb.WriteLine('{'); + sb.Indent++; + + sb.WriteLine($"return {Constants.GeneratorNamespace}.{Constants.LoggerScopesGeneratorName}.{loggerScope.GeneratedMethodName}({Constants.LoggerParameterName}{parameterValues});"); + + sb.Indent--; + sb.WriteLine('}'); + sb.WriteLine(); + } + + sb.Indent--; + sb.WriteLine('}'); + + sb.Indent--; + sb.WriteLine('}'); + + return sb.InnerWriter.ToString()!; + } +} diff --git a/src/AutoLoggerMessageGenerator/Emitters/LoggerScopesEmitter.cs b/src/AutoLoggerMessageGenerator/Emitters/LoggerScopesEmitter.cs new file mode 100644 index 0000000..6bd9780 --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Emitters/LoggerScopesEmitter.cs @@ -0,0 +1,63 @@ +using System.CodeDom.Compiler; +using System.Collections.Immutable; +using AutoLoggerMessageGenerator.Models; +using static AutoLoggerMessageGenerator.Constants; + +namespace AutoLoggerMessageGenerator.Emitters; + +internal static class LoggerScopesEmitter +{ + public static string Emit(ImmutableArray loggerScopes) + { + using var sb = new IndentedTextWriter(new StringWriter()); + + sb.WriteLine(GeneratedFileHeader); + + sb.WriteLine($"namespace {GeneratorNamespace}"); + sb.WriteLine('{'); + sb.Indent++; + + sb.WriteLine(Constants.GeneratedCodeAttribute); + sb.WriteLine(EditorNotBrowsableAttribute); + sb.WriteLine($"public static class {LoggerScopesGeneratorName}"); + sb.WriteLine('{'); + sb.Indent++; + + foreach (var loggerScope in loggerScopes) + { + var parameters = loggerScope.Parameters.Where(c => c.Name != MessageParameterName).ToArray(); + + var genericTypes = string.Join(", ", parameters.Select(c => c.NativeType)); + var defineScopeGenericTypes = genericTypes == string.Empty ? string.Empty : $"<{genericTypes}>"; + genericTypes = string.IsNullOrEmpty(genericTypes) ? string.Empty : $", {genericTypes}"; + + var parameterList = string.Join(", ", parameters.Select(c => $"{c.NativeType} {c.Name}")); + parameterList = string.IsNullOrEmpty(parameterList) ? string.Empty : $", {parameterList}"; + + var parameterValues = string.Join(", ", parameters.Where(c => c.Name != MessageParameterName).Select(c => c.Name)); + parameterValues = string.IsNullOrEmpty(parameterValues) ? string.Empty : $", {parameterValues}"; + + var loggerDefineFunctorName = $"_{loggerScope.GeneratedMethodName}"; + sb.WriteLine($"private static readonly Func {loggerDefineFunctorName} = LoggerMessage.DefineScope{defineScopeGenericTypes}(\"{loggerScope.Message}\");"); + + sb.WriteLine($"public static IDisposable? {loggerScope.GeneratedMethodName}(ILogger @logger{parameterList})"); + sb.WriteLine('{'); + sb.Indent++; + + sb.WriteLine($"return {loggerDefineFunctorName}(@logger{parameterValues});"); + + sb.Indent--; + sb.WriteLine('}'); + + sb.WriteLine(); + } + + sb.Indent--; + sb.WriteLine('}'); + + sb.Indent--; + sb.WriteLine('}'); + + return sb.InnerWriter.ToString()!; + } +} diff --git a/src/AutoLoggerMessageGenerator/Extractors/LogCallCallerExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/EnclosingClassExtractor.cs similarity index 92% rename from src/AutoLoggerMessageGenerator/Extractors/LogCallCallerExtractor.cs rename to src/AutoLoggerMessageGenerator/Extractors/EnclosingClassExtractor.cs index b0e4bc8..1f2ca2f 100644 --- a/src/AutoLoggerMessageGenerator/Extractors/LogCallCallerExtractor.cs +++ b/src/AutoLoggerMessageGenerator/Extractors/EnclosingClassExtractor.cs @@ -3,21 +3,21 @@ namespace AutoLoggerMessageGenerator.Extractors; -internal static class LogCallCallerExtractor +internal static class EnclosingClassExtractor { public static (string Namespace, string ClassName) Extract(InvocationExpressionSyntax invocationExpression) { string className = string.Empty, ns = string.Empty; - + SyntaxNode syntaxNode = invocationExpression; while (syntaxNode.Parent is not null) { if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax) className = classDeclarationSyntax.Identifier.Text; - + if (syntaxNode is NamespaceDeclarationSyntax namespaceDeclarationSyntax) ns = namespaceDeclarationSyntax.Name.ToString(); - + if (syntaxNode is FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax) ns = fileScopedNamespaceDeclarationSyntax.Name.ToString(); diff --git a/src/AutoLoggerMessageGenerator/Extractors/LogCallExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/LogCallExtractor.cs index 3cbc39b..8aa9da6 100644 --- a/src/AutoLoggerMessageGenerator/Extractors/LogCallExtractor.cs +++ b/src/AutoLoggerMessageGenerator/Extractors/LogCallExtractor.cs @@ -8,13 +8,13 @@ namespace AutoLoggerMessageGenerator.Extractors; internal static class LogCallExtractor { - public static LogCall? Extract(IMethodSymbol methodSymbol, + public static LogMessageCall? Extract(IMethodSymbol methodSymbol, InvocationExpressionSyntax invocationExpression, SemanticModel semanticModel) { - var (ns, className) = LogCallCallerExtractor.Extract(invocationExpression); + var (ns, className) = EnclosingClassExtractor.Extract(invocationExpression); - var location = LogCallLocationMapper.Map(semanticModel, invocationExpression); + var location = CallLocationMapper.Map(semanticModel, invocationExpression); if (location is null) return default; @@ -22,7 +22,7 @@ internal static class LogCallExtractor if (logLevel is null) return default; - var message = LogCallMessageExtractor.Extract(methodSymbol, invocationExpression, semanticModel); + var message = MessageParameterTextExtractor.Extract(methodSymbol, invocationExpression, semanticModel); if (message is null) return default; @@ -33,6 +33,6 @@ internal static class LogCallExtractor if (parameters is null) return default; - return new LogCall(Guid.NewGuid(), location.Value, ns, className, methodSymbol.Name, logLevel, message, parameters.Value); + return new LogMessageCall(Guid.NewGuid(), location.Value, ns, className, methodSymbol.Name, logLevel, message, parameters.Value); } } diff --git a/src/AutoLoggerMessageGenerator/Extractors/LogCallParametersExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/LogCallParametersExtractor.cs index 6144d1c..fc3cad8 100644 --- a/src/AutoLoggerMessageGenerator/Extractors/LogCallParametersExtractor.cs +++ b/src/AutoLoggerMessageGenerator/Extractors/LogCallParametersExtractor.cs @@ -10,9 +10,9 @@ namespace AutoLoggerMessageGenerator.Extractors; internal class LogCallParametersExtractor(LogPropertiesCheck? logPropertiesCheck = null) { - public ImmutableArray? Extract(string message, IMethodSymbol methodSymbol) + public ImmutableArray? Extract(string message, IMethodSymbol methodSymbol) { - var templateParametersNames = LogCallMessageParameterNamesExtractor.Extract(message) + var templateParametersNames = MessageParameterNamesExtractor.Extract(message) .Select(IdentifierHelper.AddAtPrefixIfNotExists) .ToArray(); @@ -40,12 +40,12 @@ internal class LogCallParametersExtractor(LogPropertiesCheck? logPropertiesCheck var parameterName = TransformParameterName(parameter.Name); var type = parameterName switch { - LoggerParameterName => LogCallParameterType.Logger, - LogLevelParameterName => LogCallParameterType.LogLevel, - ExceptionParameterName => LogCallParameterType.Exception, - EventIdParameterName => LogCallParameterType.EventId, - MessageParameterName => LogCallParameterType.Message, - _ => LogCallParameterType.None + LoggerParameterName => CallParameterType.Logger, + LogLevelParameterName => CallParameterType.LogLevel, + ExceptionParameterName => CallParameterType.Exception, + EventIdParameterName => CallParameterType.EventId, + MessageParameterName => CallParameterType.Message, + _ => CallParameterType.None }; return CreateLogCallParameter(parameter.Type, $"{parameterName}{uniqueNameSuffix}", type, false); }); @@ -54,7 +54,7 @@ internal class LogCallParametersExtractor(LogPropertiesCheck? logPropertiesCheck .Select((parameter, ix) => CreateLogCallParameter( nativeType: parameter.Type, name: templateParametersNames[ix], - type: LogCallParameterType.Others, + type: CallParameterType.Others, hasPropertiesToLog: logPropertiesCheck?.IsApplicable(parameter.Type) ?? false ) ); @@ -62,8 +62,7 @@ internal class LogCallParametersExtractor(LogPropertiesCheck? logPropertiesCheck return utilityParameters.Concat(messageParameters).ToImmutableArray(); } - private static LogCallParameter CreateLogCallParameter(ITypeSymbol @nativeType, string name, - LogCallParameterType type, bool hasPropertiesToLog) => + private static CallParameter CreateLogCallParameter(ITypeSymbol @nativeType, string name, CallParameterType type, bool hasPropertiesToLog) => new( NativeType: nativeType.ToDisplayString( SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions diff --git a/src/AutoLoggerMessageGenerator/Extractors/LoggerScopeCallExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/LoggerScopeCallExtractor.cs new file mode 100644 index 0000000..704fb88 --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Extractors/LoggerScopeCallExtractor.cs @@ -0,0 +1,34 @@ +using AutoLoggerMessageGenerator.Import.Microsoft.Extensions.Telemetry.LoggerMessage; +using AutoLoggerMessageGenerator.Mappers; +using AutoLoggerMessageGenerator.Models; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace AutoLoggerMessageGenerator.Extractors; + +internal static class LoggerScopeCallExtractor +{ + public static LoggerScopeCall? Extract(IMethodSymbol methodSymbol, + InvocationExpressionSyntax invocationExpression, + SemanticModel semanticModel) + { + var (ns, className) = EnclosingClassExtractor.Extract(invocationExpression); + + var location = CallLocationMapper.Map(semanticModel, invocationExpression); + if (location is null) + return default; + + var message = MessageParameterTextExtractor.Extract(methodSymbol, invocationExpression, semanticModel); + if (message is null) + return default; + + var logPropertiesCheck = new LogPropertiesCheck(semanticModel.Compilation); + var parameters = new LogCallParametersExtractor(logPropertiesCheck) + .Extract(message, methodSymbol); + + if (parameters is null) + return default; + + return new LoggerScopeCall(location.Value, ns, className, methodSymbol.Name, message, parameters.Value); + } +} diff --git a/src/AutoLoggerMessageGenerator/Extractors/LogCallMessageParameterNamesExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/MessageParameterNamesExtractor.cs similarity index 88% rename from src/AutoLoggerMessageGenerator/Extractors/LogCallMessageParameterNamesExtractor.cs rename to src/AutoLoggerMessageGenerator/Extractors/MessageParameterNamesExtractor.cs index 5ede393..7a51c9b 100644 --- a/src/AutoLoggerMessageGenerator/Extractors/LogCallMessageParameterNamesExtractor.cs +++ b/src/AutoLoggerMessageGenerator/Extractors/MessageParameterNamesExtractor.cs @@ -3,7 +3,7 @@ namespace AutoLoggerMessageGenerator.Extractors; -internal static class LogCallMessageParameterNamesExtractor +internal static class MessageParameterNamesExtractor { public static ImmutableArray Extract(string? message) => MessageArgumentRegex.Matches(message ?? string.Empty) diff --git a/src/AutoLoggerMessageGenerator/Extractors/LogCallMessageExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/MessageParameterTextExtractor.cs similarity index 97% rename from src/AutoLoggerMessageGenerator/Extractors/LogCallMessageExtractor.cs rename to src/AutoLoggerMessageGenerator/Extractors/MessageParameterTextExtractor.cs index 28e30e6..36f2d57 100644 --- a/src/AutoLoggerMessageGenerator/Extractors/LogCallMessageExtractor.cs +++ b/src/AutoLoggerMessageGenerator/Extractors/MessageParameterTextExtractor.cs @@ -4,7 +4,7 @@ namespace AutoLoggerMessageGenerator.Extractors; -internal static class LogCallMessageExtractor +internal static class MessageParameterTextExtractor { public static string? Extract(IMethodSymbol methodSymbol, InvocationExpressionSyntax invocationExpressionSyntax, SemanticModel semanticModel) diff --git a/src/AutoLoggerMessageGenerator/Filters/LogCallFilter.cs b/src/AutoLoggerMessageGenerator/Filters/LogMessageCallFilter.cs similarity index 75% rename from src/AutoLoggerMessageGenerator/Filters/LogCallFilter.cs rename to src/AutoLoggerMessageGenerator/Filters/LogMessageCallFilter.cs index 7a34ffc..5927313 100644 --- a/src/AutoLoggerMessageGenerator/Filters/LogCallFilter.cs +++ b/src/AutoLoggerMessageGenerator/Filters/LogMessageCallFilter.cs @@ -1,11 +1,12 @@ +using AutoLoggerMessageGenerator.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Logging; -using static AutoLoggerMessageGenerator.Emitters.LoggerExtensionsEmitter; +using static AutoLoggerMessageGenerator.Emitters.GenericLoggerExtensionsEmitter; namespace AutoLoggerMessageGenerator.Filters; -internal static class LogCallFilter +internal static class LogMessageCallFilter { private static readonly HashSet LogMethodNames = [ @@ -24,7 +25,7 @@ public static bool IsLoggerMethod(IMethodSymbol methodSymbol) => methodSymbol.IsExtensionMethod && methodSymbol.Parameters.All(c => c.Type.IsAnonymousType is not true && c.Type.TypeKind is not TypeKind.TypeParameter && - RecursivelyCheckTypeAccessibility(c.Type) + TypeAccessibilityChecker.IsAccessible(c.Type) ); public static bool IsLogCallInvocation(SyntaxNode node, CancellationToken cts) => @@ -33,8 +34,4 @@ public static bool IsLogCallInvocation(SyntaxNode node, CancellationToken cts) = !cts.IsCancellationRequested && invocationExpression.Expression.DescendantNodes() .Any(c => c is IdentifierNameSyntax identifierNameSyntax && LogMethodNames.Contains(identifierNameSyntax.Identifier.Text)); - - private static bool RecursivelyCheckTypeAccessibility(ITypeSymbol typeSymbol) => - typeSymbol.DeclaredAccessibility is Accessibility.Friend or Accessibility.Public or Accessibility.Internal && - (typeSymbol.ContainingType is null || RecursivelyCheckTypeAccessibility(typeSymbol.ContainingType)); } diff --git a/src/AutoLoggerMessageGenerator/Filters/LoggerScopeFilter.cs b/src/AutoLoggerMessageGenerator/Filters/LoggerScopeFilter.cs new file mode 100644 index 0000000..bcc8299 --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Filters/LoggerScopeFilter.cs @@ -0,0 +1,25 @@ +using AutoLoggerMessageGenerator.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static AutoLoggerMessageGenerator.Emitters.GenericLoggerScopeExtensionsEmitter; + +namespace AutoLoggerMessageGenerator.Filters; + +internal static class LoggerScopeFilter +{ + public static bool IsLoggerScopeMethod(IMethodSymbol methodSymbol) => + methodSymbol is { ContainingType: not null, ReceiverType.Name: "ILogger", ReturnsVoid: false, ReturnType.Name: "IDisposable" } && + methodSymbol.ContainingType.ToDisplayString() is $"{Constants.DefaultLoggingNamespace}.{ClassName}" && + methodSymbol.IsExtensionMethod && + methodSymbol.Parameters.All(c => + c.Type.IsAnonymousType is not true && c.Type.TypeKind is not TypeKind.TypeParameter && + TypeAccessibilityChecker.IsAccessible(c.Type) + ); + + public static bool IsLoggerScopeInvocation(SyntaxNode node, CancellationToken cts) => + !node.SyntaxTree.FilePath.EndsWith(".g.cs") && + node is InvocationExpressionSyntax { ArgumentList.Arguments.Count: > 1 } invocationExpression && + !cts.IsCancellationRequested && + invocationExpression.Expression.DescendantNodes() + .Any(c => c is IdentifierNameSyntax { Identifier.Text: "BeginScope" }); +} diff --git a/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.LoggerMessage.cs b/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.LoggerMessage.cs new file mode 100644 index 0000000..6d8b9eb --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.LoggerMessage.cs @@ -0,0 +1,118 @@ +using System.Collections.Immutable; +using System.Text; +using AutoLoggerMessageGenerator.Caching; +using AutoLoggerMessageGenerator.Configuration; +using AutoLoggerMessageGenerator.Diagnostics; +using AutoLoggerMessageGenerator.Emitters; +using AutoLoggerMessageGenerator.Extractors; +using AutoLoggerMessageGenerator.Filters; +using AutoLoggerMessageGenerator.Models; +using AutoLoggerMessageGenerator.PostProcessing; +using AutoLoggerMessageGenerator.ReferenceAnalyzer; +using AutoLoggerMessageGenerator.VirtualLoggerMessage; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; +using Microsoft.Extensions.Logging.Generators; +using Microsoft.Gen.Logging.Parsing; + +namespace AutoLoggerMessageGenerator.Generators; + +public partial class AutoLoggerMessageGenerator +{ + private static void GenerateLoggerMessages(IncrementalGeneratorInitializationContext context, + IncrementalValueProvider configuration, IncrementalValueProvider> modulesProvider) + { + var logCallsProvider = context.SyntaxProvider.CreateSyntaxProvider( + LogMessageCallFilter.IsLogCallInvocation, + static (ctx, cts) => ParseLogCall(ctx, cts) + ) + .Where(static t => t.HasValue) + .Select(static (t, _) => t!.Value) + .Collect() + .WithTrackingName("Searching for log calls"); + + var inputSource = context.CompilationProvider.Combine(configuration.Combine(modulesProvider.Combine(logCallsProvider))) + .WithComparer(new LogCallInputSourceComparer()); + + context.RegisterImplementationSourceOutput(inputSource, + static (ctx, t) => GenerateCode(ctx, t.Item1, t.Item2.Item1, t.Item2.Item2.Item1, t.Item2.Item2.Item2)); + } + + private static LogMessageCall? ParseLogCall(GeneratorSyntaxContext context, CancellationToken cts) + { + var semanticModel = context.SemanticModel; + var invocationExpression = (InvocationExpressionSyntax)context.Node; + var symbolInfo = semanticModel.GetSymbolInfo(invocationExpression); + + if (symbolInfo.Symbol is not IMethodSymbol methodSymbol || + !LogMessageCallFilter.IsLoggerMethod(methodSymbol) || + cts.IsCancellationRequested) + return default; + + return LogCallExtractor.Extract(methodSymbol, invocationExpression, semanticModel); + } + + private static void GenerateCode(SourceProductionContext context, Compilation compilation, + SourceGeneratorConfiguration configuration, ImmutableArray modules, + ImmutableArray logCalls) + { + if (logCalls.IsDefaultOrEmpty || context.CancellationToken.IsCancellationRequested) return; + + var telemetryAbstractions = "Microsoft.Extensions.Telemetry.Abstractions.dll"; + var useTelemetryExtensions = modules.Any(c => c.Name == telemetryAbstractions); + + // We need to keep the current LoggerMessage source generator as it makes future updates easier. + // All [LoggerMessage] attributes are generated to match the current implementation. + // These attributes are only used to trigger the source generator, so we can create them virtually and add them to the compilation unit. + var virtualClassDeclaration = new VirtualLoggerMessageClassBuilder(configuration, useTelemetryExtensions) + .Build(logCalls); + compilation = VirtualMembersInjector.InjectMembers(compilation, virtualClassDeclaration); + + var classDeclarations = compilation.SyntaxTrees.Last() + .GetRoot() + .DescendantNodes() + .OfType() + .ToImmutableHashSet(); + + var diagnosticReporter = new LogMessageDiagnosticReporter(context, logCalls); + + var loggerMessageCode = useTelemetryExtensions + ? GenerateNewLoggerMessage(diagnosticReporter, compilation, classDeclarations, context.CancellationToken) + : GenerateOldLoggerMessage(diagnosticReporter, compilation, classDeclarations, context.CancellationToken); + + if (context.CancellationToken.IsCancellationRequested) return; + + loggerMessageCode = LoggerMessageResultAdjuster.Adjust(loggerMessageCode); + if (!string.IsNullOrEmpty(loggerMessageCode)) + context.AddSource($"{Constants.LoggerMessageGeneratorName}.g.cs", SourceText.From(loggerMessageCode!, Encoding.UTF8)); + + context.AddSource("LogCallInterceptors.g.cs", SourceText.From(LoggerInterceptorsEmitter.Emit(logCalls), Encoding.UTF8)); + } + + private static string? GenerateNewLoggerMessage( + LogMessageDiagnosticReporter diagnosticReporter, Compilation compilation, + ImmutableHashSet classDeclarationSyntaxes, CancellationToken cts) + { + var p = new Parser(compilation, diagnosticReporter.Report, cts); + var logTypes = p.GetLogTypes(classDeclarationSyntaxes); + + if (logTypes.Count <= 0) return null; + + var e = new Microsoft.Gen.Logging.Emission.Emitter(); + return e.Emit(logTypes, cts); + } + + private static string? GenerateOldLoggerMessage( + LogMessageDiagnosticReporter diagnosticReporter, Compilation compilation, + ImmutableHashSet classDeclarationSyntaxes, CancellationToken cts) + { + var loggerMessageParser = new LoggerMessageGenerator.Parser(compilation, diagnosticReporter.Report, cts); + var logClasses = loggerMessageParser.GetLogClasses(classDeclarationSyntaxes); + + if (logClasses.Count <= 0) return null; + + var loggerMessageEmitter = new LoggerMessageGenerator.Emitter(); + return loggerMessageEmitter.Emit(logClasses, cts); + } +} diff --git a/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.LoggerScopes.cs b/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.LoggerScopes.cs new file mode 100644 index 0000000..f1af1aa --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.LoggerScopes.cs @@ -0,0 +1,64 @@ +using System.Collections.Immutable; +using System.Text; +using AutoLoggerMessageGenerator.Caching; +using AutoLoggerMessageGenerator.Configuration; +using AutoLoggerMessageGenerator.Emitters; +using AutoLoggerMessageGenerator.Extractors; +using AutoLoggerMessageGenerator.Filters; +using AutoLoggerMessageGenerator.Models; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace AutoLoggerMessageGenerator.Generators; + +public partial class AutoLoggerMessageGenerator +{ + private static void GenerateLoggerScopes(IncrementalGeneratorInitializationContext context, IncrementalValueProvider configuration) + { + var loggerScopeProvider = context.SyntaxProvider.CreateSyntaxProvider( + LoggerScopeFilter.IsLoggerScopeInvocation, + static (ctx, cts) => ParseLoggerScope(ctx, cts) + ) + .Where(static t => t.HasValue) + .Select(static (t, _) => t!.Value) + .Collect() + .WithTrackingName("Searching for log scope definitions"); + + var inputSource = context.CompilationProvider.Combine(configuration.Combine(loggerScopeProvider)) + .WithComparer(new LogScopeDefinitionInputSourceComparer()); + + context.RegisterImplementationSourceOutput(inputSource, static (ctx, t) => + GenerateCode(ctx, t.Item2.Item1, t.Item2.Item2)); + } + + private static LoggerScopeCall? ParseLoggerScope(GeneratorSyntaxContext context, CancellationToken cts) + { + var semanticModel = context.SemanticModel; + var invocationExpression = (InvocationExpressionSyntax)context.Node; + var symbolInfo = semanticModel.GetSymbolInfo(invocationExpression); + + if (symbolInfo.Symbol is not IMethodSymbol methodSymbol || + !LoggerScopeFilter.IsLoggerScopeMethod(methodSymbol) || + cts.IsCancellationRequested) + return default; + + return LoggerScopeCallExtractor.Extract(methodSymbol, invocationExpression, semanticModel); + } + + private static void GenerateCode(SourceProductionContext context, + SourceGeneratorConfiguration configuration, ImmutableArray loggerScopes) + { + if (context.CancellationToken.IsCancellationRequested || + loggerScopes.IsDefaultOrEmpty || + !configuration.OverrideBeginScopeBehavior) + return; + + var generatedLoggerScopes = LoggerScopesEmitter.Emit(loggerScopes); + + if (!string.IsNullOrEmpty(generatedLoggerScopes)) + context.AddSource($"{Constants.LoggerScopesGeneratorName}.g.cs", SourceText.From(generatedLoggerScopes, Encoding.UTF8)); + + context.AddSource("LoggerScopeInterceptors.g.cs", SourceText.From(LoggerScopeInterceptorsEmitter.Emit(loggerScopes), Encoding.UTF8)); + } +} diff --git a/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.cs b/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.cs index 17a7330..3feb67f 100644 --- a/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.cs +++ b/src/AutoLoggerMessageGenerator/Generators/AutoLoggerMessageGenerator.cs @@ -1,25 +1,14 @@ -using System.Collections.Immutable; using System.Text; -using AutoLoggerMessageGenerator.Caching; using AutoLoggerMessageGenerator.Configuration; -using AutoLoggerMessageGenerator.Diagnostics; using AutoLoggerMessageGenerator.Emitters; -using AutoLoggerMessageGenerator.Extractors; -using AutoLoggerMessageGenerator.Filters; -using AutoLoggerMessageGenerator.Models; -using AutoLoggerMessageGenerator.PostProcessing; using AutoLoggerMessageGenerator.ReferenceAnalyzer; -using AutoLoggerMessageGenerator.VirtualLoggerMessage; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; -using Microsoft.Extensions.Logging.Generators; -using Microsoft.Gen.Logging.Parsing; namespace AutoLoggerMessageGenerator.Generators; [Generator] -public class AutoLoggerMessageGenerator : IIncrementalGenerator +public partial class AutoLoggerMessageGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { @@ -30,102 +19,18 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Collect() .WithTrackingName("Scanning project references"); - var logCallsProvider = context.SyntaxProvider.CreateSyntaxProvider( - LogCallFilter.IsLogCallInvocation, - static (ctx, cts) => GetLogCalls(ctx, cts) - ) - .Where(static t => t.HasValue) - .Select(static (t, _) => t!.Value) - .Collect() - .WithTrackingName("Searching for log calls"); - - var inputSource = context.CompilationProvider.Combine(configuration.Combine(modulesProvider.Combine(logCallsProvider))) - .WithComparer(new InputSourceComparer()); - - context.RegisterImplementationSourceOutput(inputSource, - static (ctx, t) => GenerateCode(ctx, t.Item1, t.Item2.Item1, t.Item2.Item2.Item1, t.Item2.Item2.Item2)); + GenerateLoggerMessages(context, configuration, modulesProvider); + GenerateLoggerScopes(context, configuration); + GenerateInterceptorAttribute(context, configuration); + } + private static void GenerateInterceptorAttribute(IncrementalGeneratorInitializationContext context, + IncrementalValueProvider configuration) + { context.RegisterImplementationSourceOutput(configuration, static (ctx, configuration) => { if (configuration.GenerateInterceptorAttribute) ctx.AddSource("InterceptorAttribute.g.cs", SourceText.From(InterceptorAttributeEmitter.Emit(), Encoding.UTF8)); }); } - - private static LogCall? GetLogCalls(GeneratorSyntaxContext context, CancellationToken cts) - { - var semanticModel = context.SemanticModel; - var invocationExpression = (InvocationExpressionSyntax)context.Node; - var symbolInfo = semanticModel.GetSymbolInfo(invocationExpression); - - if (symbolInfo.Symbol is not IMethodSymbol methodSymbol || - !LogCallFilter.IsLoggerMethod(methodSymbol) || - cts.IsCancellationRequested) - return default; - - return LogCallExtractor.Extract(methodSymbol, invocationExpression, semanticModel); - } - - private static void GenerateCode(SourceProductionContext context, Compilation compilation, - SourceGeneratorConfiguration configuration, ImmutableArray modules, - ImmutableArray logCalls) - { - if (logCalls.IsDefaultOrEmpty || context.CancellationToken.IsCancellationRequested) return; - - var telemetryAbstractions = "Microsoft.Extensions.Telemetry.Abstractions.dll"; - var useTelemetryExtensions = modules.Any(c => c.Name == telemetryAbstractions); - - // We need to keep the current LoggerMessage source generator as it makes future updates easier. - // All [LoggerMessage] attributes are generated to match the current implementation. - // These attributes are only used to trigger the source generator, so we can create them virtually and add them to the compilation unit. - var virtualClassDeclaration = new VirtualLoggerMessageClassBuilder(configuration, useTelemetryExtensions) - .Build(logCalls); - compilation = VirtualMembersInjector.InjectMembers(compilation, virtualClassDeclaration); - - var classDeclarations = compilation.SyntaxTrees.Last() - .GetRoot() - .DescendantNodes() - .OfType() - .ToImmutableHashSet(); - - var diagnosticReporter = new DiagnosticReporter(context, logCalls); - - var loggerMessageCode = useTelemetryExtensions - ? GenerateNewLoggerMessage(diagnosticReporter, compilation, classDeclarations, context.CancellationToken) - : GenerateOldLoggerMessage(diagnosticReporter, compilation, classDeclarations, context.CancellationToken); - - if (context.CancellationToken.IsCancellationRequested) return; - - loggerMessageCode = LoggerMessageResultAdjuster.Adjust(loggerMessageCode); - if (!string.IsNullOrEmpty(loggerMessageCode)) - context.AddSource("LoggerMessage.g.cs", SourceText.From(loggerMessageCode!, Encoding.UTF8)); - - context.AddSource("Interceptors.g.cs", SourceText.From(LoggerInterceptorsEmitter.Emit(logCalls), Encoding.UTF8)); - } - - private static string? GenerateNewLoggerMessage( - DiagnosticReporter diagnosticReporter, Compilation compilation, - ImmutableHashSet classDeclarationSyntaxes, CancellationToken cts) - { - var p = new Parser(compilation, diagnosticReporter.Report, cts); - var logTypes = p.GetLogTypes(classDeclarationSyntaxes); - - if (logTypes.Count <= 0) return null; - - var e = new Microsoft.Gen.Logging.Emission.Emitter(); - return e.Emit(logTypes, cts); - } - - private static string? GenerateOldLoggerMessage( - DiagnosticReporter diagnosticReporter, Compilation compilation, - ImmutableHashSet classDeclarationSyntaxes, CancellationToken cts) - { - var loggerMessageParser = new LoggerMessageGenerator.Parser(compilation, diagnosticReporter.Report, cts); - var logClasses = loggerMessageParser.GetLogClasses(classDeclarationSyntaxes); - - if (logClasses.Count <= 0) return null; - - var loggerMessageEmitter = new LoggerMessageGenerator.Emitter(); - return loggerMessageEmitter.Emit(logClasses, cts); - } } diff --git a/src/AutoLoggerMessageGenerator/Mappers/LogCallLocationMapper.cs b/src/AutoLoggerMessageGenerator/Mappers/CallLocationMapper.cs similarity index 85% rename from src/AutoLoggerMessageGenerator/Mappers/LogCallLocationMapper.cs rename to src/AutoLoggerMessageGenerator/Mappers/CallLocationMapper.cs index e6354dd..a15dd14 100644 --- a/src/AutoLoggerMessageGenerator/Mappers/LogCallLocationMapper.cs +++ b/src/AutoLoggerMessageGenerator/Mappers/CallLocationMapper.cs @@ -1,5 +1,3 @@ -using System.Diagnostics; -using System.Text; using AutoLoggerMessageGenerator.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -7,9 +5,9 @@ namespace AutoLoggerMessageGenerator.Mappers; -internal static class LogCallLocationMapper +internal static class CallLocationMapper { - public static LogCallLocation? Map(SemanticModel semanticModel, InvocationExpressionSyntax invocationExpression) + public static CallLocation? Map(SemanticModel semanticModel, InvocationExpressionSyntax invocationExpression) { var memberAccessExpression = invocationExpression.Expression as MemberAccessExpressionSyntax; if (memberAccessExpression?.Expression is not IdentifierNameSyntax identifierName) @@ -36,7 +34,7 @@ internal static class LogCallLocationMapper if (interceptableLocation is null) return null; - return new LogCallLocation(filePath, line, character, interceptableLocation, location); + return new CallLocation(filePath, line, character, interceptableLocation, location); } #if PATH_BASED_INTERCEPTORS diff --git a/src/AutoLoggerMessageGenerator/Models/LogCallLocation.cs b/src/AutoLoggerMessageGenerator/Models/CallLocation.cs similarity index 87% rename from src/AutoLoggerMessageGenerator/Models/LogCallLocation.cs rename to src/AutoLoggerMessageGenerator/Models/CallLocation.cs index 44ac9b5..38fcacd 100644 --- a/src/AutoLoggerMessageGenerator/Models/LogCallLocation.cs +++ b/src/AutoLoggerMessageGenerator/Models/CallLocation.cs @@ -2,7 +2,7 @@ namespace AutoLoggerMessageGenerator.Models; -internal readonly record struct LogCallLocation( +internal readonly record struct CallLocation( string FilePath, int Line, int Character, @@ -10,7 +10,7 @@ internal readonly record struct LogCallLocation( Location Context ) { - public readonly bool Equals(LogCallLocation other) => + public bool Equals(CallLocation other) => FilePath == other.FilePath && Line == other.Line && Character == other.Character && diff --git a/src/AutoLoggerMessageGenerator/Models/LogCallParameter.cs b/src/AutoLoggerMessageGenerator/Models/CallParameter.cs similarity index 64% rename from src/AutoLoggerMessageGenerator/Models/LogCallParameter.cs rename to src/AutoLoggerMessageGenerator/Models/CallParameter.cs index adb23d5..db710ed 100644 --- a/src/AutoLoggerMessageGenerator/Models/LogCallParameter.cs +++ b/src/AutoLoggerMessageGenerator/Models/CallParameter.cs @@ -1,9 +1,9 @@ namespace AutoLoggerMessageGenerator.Models; -internal record struct LogCallParameter +internal record struct CallParameter ( string NativeType, string Name, - LogCallParameterType Type, + CallParameterType Type, bool HasPropertiesToLog = false ); diff --git a/src/AutoLoggerMessageGenerator/Models/LogCallParameterType.cs b/src/AutoLoggerMessageGenerator/Models/CallParameterType.cs similarity index 79% rename from src/AutoLoggerMessageGenerator/Models/LogCallParameterType.cs rename to src/AutoLoggerMessageGenerator/Models/CallParameterType.cs index 9fa8cec..8d33634 100644 --- a/src/AutoLoggerMessageGenerator/Models/LogCallParameterType.cs +++ b/src/AutoLoggerMessageGenerator/Models/CallParameterType.cs @@ -1,6 +1,6 @@ namespace AutoLoggerMessageGenerator.Models; -internal enum LogCallParameterType +internal enum CallParameterType { None, Logger, diff --git a/src/AutoLoggerMessageGenerator/Models/LogCall.cs b/src/AutoLoggerMessageGenerator/Models/LogMessageCall.cs similarity index 64% rename from src/AutoLoggerMessageGenerator/Models/LogCall.cs rename to src/AutoLoggerMessageGenerator/Models/LogMessageCall.cs index 5144773..6927e15 100644 --- a/src/AutoLoggerMessageGenerator/Models/LogCall.cs +++ b/src/AutoLoggerMessageGenerator/Models/LogMessageCall.cs @@ -1,24 +1,30 @@ using System.Collections.Immutable; +using AutoLoggerMessageGenerator.Utilities; namespace AutoLoggerMessageGenerator.Models; -internal readonly record struct LogCall( +internal readonly record struct LogMessageCall( // Need this property for backtracking diagnostic reports Guid Id, - LogCallLocation Location, + CallLocation Location, string Namespace, string ClassName, - string Name, + string MethodName, string LogLevel, string Message, - ImmutableArray Parameters + ImmutableArray Parameters ) { - public bool Equals(LogCall other) => + public string GeneratedMethodName => + IdentifierHelper.ToValidCSharpMethodName( + $"{Constants.LogMethodPrefix}{Namespace}{ClassName}_{Location.Line}_{Location.Character}" + ); + + public bool Equals(LogMessageCall other) => Location.Equals(other.Location) && Namespace == other.Namespace && ClassName == other.ClassName && - Name == other.Name && + MethodName == other.MethodName && LogLevel == other.LogLevel && Message == other.Message && Parameters.SequenceEqual(other.Parameters); @@ -30,7 +36,7 @@ public override int GetHashCode() var hashCode = Location.GetHashCode(); hashCode = (hashCode * 397) ^ Namespace.GetHashCode(); hashCode = (hashCode * 397) ^ ClassName.GetHashCode(); - hashCode = (hashCode * 397) ^ Name.GetHashCode(); + hashCode = (hashCode * 397) ^ MethodName.GetHashCode(); hashCode = (hashCode * 397) ^ LogLevel.GetHashCode(); hashCode = (hashCode * 397) ^ Message.GetHashCode(); hashCode = (hashCode * 397) ^ Parameters.GetHashCode(); diff --git a/src/AutoLoggerMessageGenerator/Models/LoggerScopeCall.cs b/src/AutoLoggerMessageGenerator/Models/LoggerScopeCall.cs new file mode 100644 index 0000000..467cb6e --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Models/LoggerScopeCall.cs @@ -0,0 +1,42 @@ +using System.Collections.Immutable; +using AutoLoggerMessageGenerator.Utilities; + +namespace AutoLoggerMessageGenerator.Models; + +internal readonly record struct LoggerScopeCall( + CallLocation Location, + string Namespace, + string ClassName, + string MethodName, + string Message, + ImmutableArray Parameters +) +{ + public string GeneratedMethodName => + IdentifierHelper.ToValidCSharpMethodName( + $"{Constants.LogScopeMethodPrefix}{Namespace}{ClassName}_{Location.Line}_{Location.Character}" + ); + + public bool Equals(LoggerScopeCall other) => + Location.Equals(other.Location) && + Namespace == other.Namespace && + ClassName == other.ClassName && + MethodName == other.MethodName && + Message == other.Message && + Parameters.SequenceEqual(other.Parameters); + + public override int GetHashCode() + { + unchecked + { + var hashCode = Location.GetHashCode(); + hashCode = (hashCode * 397) ^ Namespace.GetHashCode(); + hashCode = (hashCode * 397) ^ ClassName.GetHashCode(); + hashCode = (hashCode * 397) ^ MethodName.GetHashCode(); + hashCode = (hashCode * 397) ^ Message.GetHashCode(); + hashCode = (hashCode * 397) ^ Parameters.GetHashCode(); + return hashCode; + } + } +}; + diff --git a/src/AutoLoggerMessageGenerator/Utilities/TypeAccessibilityChecker.cs b/src/AutoLoggerMessageGenerator/Utilities/TypeAccessibilityChecker.cs new file mode 100644 index 0000000..8471c8b --- /dev/null +++ b/src/AutoLoggerMessageGenerator/Utilities/TypeAccessibilityChecker.cs @@ -0,0 +1,10 @@ +using Microsoft.CodeAnalysis; + +namespace AutoLoggerMessageGenerator.Utilities; + +internal static class TypeAccessibilityChecker +{ + public static bool IsAccessible(ITypeSymbol typeSymbol) => + typeSymbol.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal && + (typeSymbol.ContainingType is null || IsAccessible(typeSymbol.ContainingType)); +} diff --git a/src/AutoLoggerMessageGenerator/VirtualLoggerMessage/LogCallLocationMap.cs b/src/AutoLoggerMessageGenerator/VirtualLoggerMessage/LogMessageCallLocationMap.cs similarity index 56% rename from src/AutoLoggerMessageGenerator/VirtualLoggerMessage/LogCallLocationMap.cs rename to src/AutoLoggerMessageGenerator/VirtualLoggerMessage/LogMessageCallLocationMap.cs index 75dd885..0bb4c25 100644 --- a/src/AutoLoggerMessageGenerator/VirtualLoggerMessage/LogCallLocationMap.cs +++ b/src/AutoLoggerMessageGenerator/VirtualLoggerMessage/LogMessageCallLocationMap.cs @@ -4,12 +4,12 @@ namespace AutoLoggerMessageGenerator.VirtualLoggerMessage; -internal static class LogCallLocationMap +internal static class LogMessageCallLocationMap { - private const string LogCallMappingLocationFlag = "// : "; + private const string LogMessageCallMappingLocationFlag = "// : "; - public static string CreateMapping(LogCall logCall) => - $"{LogCallMappingLocationFlag}{logCall.Id}"; + public static string CreateMapping(LogMessageCall logMessageCall) => + $"{LogMessageCallMappingLocationFlag}{logMessageCall.Id}"; public static bool TryMapBack(SourceText syntaxTree, Location currentLocation, out Guid logCallId) { @@ -20,9 +20,9 @@ public static bool TryMapBack(SourceText syntaxTree, Location currentLocation, o for (var lineIndex = subText.Lines.Count - 1; lineIndex >= 0; lineIndex--) { var line = subText.Lines[lineIndex].ToString().TrimStart(); - if (!line.StartsWith(LogCallMappingLocationFlag)) continue; + if (!line.StartsWith(LogMessageCallMappingLocationFlag)) continue; - return Guid.TryParse(line.Substring(LogCallMappingLocationFlag.Length), out logCallId); + return Guid.TryParse(line.Substring(LogMessageCallMappingLocationFlag.Length), out logCallId); } return false; diff --git a/src/AutoLoggerMessageGenerator/VirtualLoggerMessage/VirtualLoggerMessageClassBuilder.cs b/src/AutoLoggerMessageGenerator/VirtualLoggerMessage/VirtualLoggerMessageClassBuilder.cs index 3453e01..a7daf71 100644 --- a/src/AutoLoggerMessageGenerator/VirtualLoggerMessage/VirtualLoggerMessageClassBuilder.cs +++ b/src/AutoLoggerMessageGenerator/VirtualLoggerMessage/VirtualLoggerMessageClassBuilder.cs @@ -1,7 +1,6 @@ using System.Collections.Immutable; using AutoLoggerMessageGenerator.Configuration; using AutoLoggerMessageGenerator.Models; -using AutoLoggerMessageGenerator.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -17,7 +16,7 @@ internal class VirtualLoggerMessageClassBuilder( private const string LoggerMessageAttributeName = LoggerMessageGenerator.Parser.LoggerMessageAttribute; private const string LogPropertiesAttribute = "Microsoft.Extensions.Logging.LogPropertiesAttribute"; - public MemberDeclarationSyntax Build(ImmutableArray logCalls) + public MemberDeclarationSyntax Build(ImmutableArray logCalls) { var methods = logCalls.Select(GenerateMethod).OfType().ToArray(); @@ -31,14 +30,14 @@ public MemberDeclarationSyntax Build(ImmutableArray logCalls) return namespaceDeclaration; } - private MethodDeclarationSyntax GenerateMethod(LogCall logCall) + private MethodDeclarationSyntax GenerateMethod(LogMessageCall logMessageCall) { - var attributeList = GenerateLoggerMessageAttribute(logCall); + var attributeList = GenerateLoggerMessageAttribute(logMessageCall); var loggerParameter = Parameter(Identifier("Logger")) .WithType(IdentifierName($"{Constants.DefaultLoggingNamespace}.ILogger")); - var parameters = logCall.Parameters + var parameters = logMessageCall.Parameters .Where(c => !Constants.LoggerMessageAttributeParameterTypes.Contains(c.Type)) .Select(c => { @@ -50,11 +49,8 @@ private MethodDeclarationSyntax GenerateMethod(LogCall logCall) return parameter; }).ToArray(); - var methodName = IdentifierHelper.ToValidCSharpMethodName( - $"{Constants.LogMethodPrefix}{logCall.Namespace}{logCall.ClassName}_{logCall.Location.Line}_{logCall.Location.Character}"); - - var logCallMappingLocation = ParseLeadingTrivia(LogCallLocationMap.CreateMapping(logCall)); - var methodDeclaration = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), methodName) + var logCallMappingLocation = ParseLeadingTrivia(LogMessageCallLocationMap.CreateMapping(logMessageCall)); + var methodDeclaration = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), logMessageCall.GeneratedMethodName) .AddModifiers( Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword), @@ -69,7 +65,7 @@ private MethodDeclarationSyntax GenerateMethod(LogCall logCall) return methodDeclaration; } - private AttributeListSyntax GenerateLoggerMessageAttribute(LogCall logCall) + private AttributeListSyntax GenerateLoggerMessageAttribute(LogMessageCall logMessageCall) { var attribute = Attribute(ParseName(LoggerMessageAttributeName)) .WithArgumentList( @@ -78,11 +74,11 @@ private AttributeListSyntax GenerateLoggerMessageAttribute(LogCall logCall) { AttributeArgument( ParseExpression( - $"Level = {Constants.DefaultLoggingNamespace}.LogLevel.{logCall.LogLevel}")), + $"Level = {Constants.DefaultLoggingNamespace}.LogLevel.{logMessageCall.LogLevel}")), AttributeArgument( AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("Message"), - LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(logCall.Message)))), + LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(logMessageCall.Message)))), AttributeArgument( ParseExpression( $"SkipEnabledCheck = {configuration.GenerateSkipEnabledCheck.ToLowerBooleanString()}")) diff --git a/tests/AutoLoggerMessageGenerator.IntegrationTests/AutoLoggerMessageGenerator.IntegrationTests.csproj b/tests/AutoLoggerMessageGenerator.IntegrationTests/AutoLoggerMessageGenerator.IntegrationTests.csproj index 930b801..302de68 100644 --- a/tests/AutoLoggerMessageGenerator.IntegrationTests/AutoLoggerMessageGenerator.IntegrationTests.csproj +++ b/tests/AutoLoggerMessageGenerator.IntegrationTests/AutoLoggerMessageGenerator.IntegrationTests.csproj @@ -28,7 +28,7 @@ - - + + diff --git a/tests/AutoLoggerMessageGenerator.IntegrationTests/BeginScopeWithAllParameterRangeTests.cs b/tests/AutoLoggerMessageGenerator.IntegrationTests/BeginScopeWithAllParameterRangeTests.cs new file mode 100644 index 0000000..1cc4c57 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.IntegrationTests/BeginScopeWithAllParameterRangeTests.cs @@ -0,0 +1,78 @@ + +using Microsoft.Extensions.Logging; + +namespace AutoLoggerMessageGenerator.IntegrationTests; + +internal class BeginScopeWithAllParameterRangeTests +{ + [Test] + [MethodDataSource(nameof(BeginScopeMethodsWithDifferentParameters))] + public async Task WithAllLogMethods_RequestShouldBeForwardedToLoggerMessageSourceGenerator(Func beginScopeCall) + { + ILogger logger = LoggerFactory.Create(c => + c.AddSimpleConsole(options => + { + options.IncludeScopes = true; + }) + ).CreateLogger(); + + var proxy = DispatchProxyExecutionVerificationDecorator.Decorate(logger, + Constants.LoggerScopesGeneratorName, methodName => methodName == "BeginScope"); + + using var _ = beginScopeCall((ILogger) proxy); + logger.LogInformation("Hello world"); + + await Assert.That(proxy.ExecutionsWithoutGenerator).IsEmpty(); + await Assert.That(proxy.ExecutionsFromGenerator.Count).IsEqualTo(1); + } + + [Test] + [MethodDataSource(nameof(BeginScopeMethodsWithUnsupportedParametersCount))] + public async Task WithUnsupportedParametersCount_RequestShouldBeForwardedToOriginalImplementation(Func beginScopeCall) + { + ILogger logger = LoggerFactory.Create(c => + c.AddSimpleConsole().SetMinimumLevel(LogLevel.Trace) + ).CreateLogger(); + + var proxy = DispatchProxyExecutionVerificationDecorator.Decorate(logger, + Constants.LoggerScopesGeneratorName, methodName => methodName == "BeginScope"); + + using var _ = beginScopeCall((ILogger) proxy); + logger.LogInformation("Hello world"); + + await Assert.That(proxy.ExecutionsFromGenerator).IsEmpty(); + await Assert.That(proxy.ExecutionsWithoutGenerator.Count).IsEqualTo(1); + } + + public static IEnumerable>> BeginScopeMethodsWithDifferentParameters() + { + const string messageWith1Parameters = "Message Scope: {arg1}"; + const string messageWith2Parameters = "Message Scope: {arg1} {arg2}"; + const string messageWith3Parameters = "Message Scope: {arg1} {arg2} {arg3}"; + const string messageWith4Parameters = "Message Scope: {arg1} {arg2} {arg3} {arg4}"; + const string messageWith5Parameters = "Message Scope: {arg1} {arg2} {arg3} {arg4} {arg5}"; + const string messageWith6Parameters = "Message Scope: {arg1} {arg2} {arg3} {arg4} {arg5} {arg6}"; + + return + [ + () => l => l.BeginScope(messageWith1Parameters, 1), + () => l => l.BeginScope(messageWith2Parameters, 1, 2), + () => l => l.BeginScope(messageWith3Parameters, 1, 2, 3), + () => l => l.BeginScope(messageWith4Parameters, 1, 2, 3, 4), + () => l => l.BeginScope(messageWith5Parameters, 1, 2, 3, 4, 5), + () => l => l.BeginScope(messageWith6Parameters, 1, 2, 3, 4, 5, 6), + ]; + } + + public static IEnumerable>> BeginScopeMethodsWithUnsupportedParametersCount() + { + const string messageWithoutParameters = "Message Scope"; + const string messageWith7Parameters = "Message Scope: {arg1} {arg2} {arg3} {arg4} {arg5} {arg6} {arg7}"; + + return + [ + () => l => l.BeginScope(messageWithoutParameters), + () => l => l.BeginScope(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), + ]; + } +} diff --git a/tests/AutoLoggerMessageGenerator.IntegrationTests/DispatchProxyExecutionVerificationDecorator.cs b/tests/AutoLoggerMessageGenerator.IntegrationTests/DispatchProxyExecutionVerificationDecorator.cs index de5d351..2db5db4 100644 --- a/tests/AutoLoggerMessageGenerator.IntegrationTests/DispatchProxyExecutionVerificationDecorator.cs +++ b/tests/AutoLoggerMessageGenerator.IntegrationTests/DispatchProxyExecutionVerificationDecorator.cs @@ -5,7 +5,7 @@ namespace AutoLoggerMessageGenerator.IntegrationTests; public class DispatchProxyExecutionVerificationDecorator : DispatchProxy { private T? Target { get; set; } - + private string? GeneratorName { get; set; } private Func? MethodFilter { get; set; } private readonly List _executionsFromGenerator = []; @@ -14,13 +14,15 @@ public class DispatchProxyExecutionVerificationDecorator : DispatchProxy public IReadOnlyList ExecutionsFromGenerator => _executionsFromGenerator.AsReadOnly(); public IReadOnlyList ExecutionsWithoutGenerator => _executionsWithoutGenerator.AsReadOnly(); - public static DispatchProxyExecutionVerificationDecorator Decorate(T target, Func? methodFilter = default) + public static DispatchProxyExecutionVerificationDecorator Decorate(T target, string generatorName, Func? methodFilter = default) { if (Create>() is not DispatchProxyExecutionVerificationDecorator proxy) throw new InvalidOperationException($"Unable to create DispatchProxyExecutionVerificationDecorator for {typeof(T).FullName}"); proxy.Target = target; + proxy.GeneratorName = generatorName; proxy.MethodFilter = methodFilter; + return proxy; } @@ -35,7 +37,7 @@ public static DispatchProxyExecutionVerificationDecorator Decorate(T target, private void CaptureExecutionCall() { var stackTrace = Environment.StackTrace; - var callFromGenerator = stackTrace.Contains("LoggerMessage.g.cs"); + var callFromGenerator = stackTrace.Contains($"{GeneratorName}.g.cs"); var executionList = callFromGenerator ? _executionsFromGenerator : _executionsWithoutGenerator; executionList.Add(stackTrace); diff --git a/tests/AutoLoggerMessageGenerator.IntegrationTests/ExecutionSourceTests.cs b/tests/AutoLoggerMessageGenerator.IntegrationTests/ExecutionSourceTests.cs deleted file mode 100644 index 2da8129..0000000 --- a/tests/AutoLoggerMessageGenerator.IntegrationTests/ExecutionSourceTests.cs +++ /dev/null @@ -1,137 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace AutoLoggerMessageGenerator.IntegrationTests; - -internal class ExecutionSourceTests -{ - [Test] - [MethodDataSource(nameof(LogMethodsWithDifferentParameters))] - public async Task WithAllLogMethods_RequestShouldBeForwardedToLoggerMessageSourceGenerator(Action[] logCalls) - { - ILogger logger = LoggerFactory.Create(c => - c.AddSimpleConsole().SetMinimumLevel(LogLevel.Trace) - ).CreateLogger(); - - var proxy = DispatchProxyExecutionVerificationDecorator.Decorate(logger, methodName => methodName == "Log"); - - foreach (var logCall in logCalls) - logCall((ILogger) proxy); - - await Assert.That(proxy.ExecutionsWithoutGenerator).IsEmpty(); - await Assert.That(proxy.ExecutionsFromGenerator.Count).IsEqualTo(logCalls.Length); - } - - [Test] - public async Task WithMoreThan6Parameters_RequestShouldBeForwardedToOriginalImplementation() - { - ILogger logger = LoggerFactory.Create(c => - c.AddSimpleConsole().SetMinimumLevel(LogLevel.Trace) - ).CreateLogger(); - - var proxy = DispatchProxyExecutionVerificationDecorator.Decorate(logger, methodName => methodName == "Log"); - - const string messageWith7Parameters = "Event received: {arg1} {arg2} {arg3} {arg4} {arg5} {arg6} {arg7}"; - var logCalls = new Action[] - { - l => l.LogTrace(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), - l => l.LogDebug(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), - l => l.LogInformation(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), - l => l.LogWarning(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), - l => l.LogError(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), - l => l.LogCritical(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), - l => l.Log(LogLevel.Information, messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7) - }; - - foreach (var logCall in logCalls) - logCall((ILogger) proxy); - - await Assert.That(proxy.ExecutionsFromGenerator).IsEmpty(); - await Assert.That(proxy.ExecutionsWithoutGenerator.Count).IsEqualTo(logCalls.Length); - await Assert.That(proxy.ExecutionsWithoutGenerator.Where(c => !c.Contains("LoggerExtensions.Log"))).IsEmpty(); - } - - public static IEnumerable[]>> LogMethodsWithDifferentParameters() - { - const string messageWithoutParameters = "Event received"; - const string messageWith1Parameters = "Event received: {arg1}"; - const string messageWith2Parameters = "Event received: {arg1} {arg2}"; - const string messageWith3Parameters = "Event received: {arg1} {arg2} {arg3}"; - const string messageWith4Parameters = "Event received: {arg1} {arg2} {arg3} {arg4}"; - const string messageWith5Parameters = "Event received: {arg1} {arg2} {arg3} {arg4} {arg5}"; - const string messageWith6Parameters = "Event received: {arg1} {arg2} {arg3} {arg4} {arg5} {arg6}"; - - return - [ - () => - [ - logger => logger.LogTrace(messageWithoutParameters), - logger => logger.LogDebug(messageWithoutParameters), - logger => logger.LogInformation(messageWithoutParameters), - logger => logger.LogWarning(messageWithoutParameters), - logger => logger.LogError(messageWithoutParameters), - logger => logger.LogCritical(messageWithoutParameters), - logger => logger.Log(LogLevel.Information, messageWithoutParameters) - ], - () => - [ - logger => logger.LogTrace(messageWith1Parameters, 1), - logger => logger.LogDebug(messageWith1Parameters, 1), - logger => logger.LogInformation(messageWith1Parameters, 1), - logger => logger.LogWarning(messageWith1Parameters, 1), - logger => logger.LogError(messageWith1Parameters, 1), - logger => logger.LogCritical(messageWith1Parameters, 1), - logger => logger.Log(LogLevel.Information, messageWith1Parameters, 1) - ], - () => - [ - logger => logger.LogTrace(messageWith2Parameters, 1, 2), - logger => logger.LogDebug(messageWith2Parameters, 1, 2), - logger => logger.LogInformation(messageWith2Parameters, 1, 2), - logger => logger.LogWarning(messageWith2Parameters, 1, 2), - logger => logger.LogError(messageWith2Parameters, 1, 2), - logger => logger.LogCritical(messageWith2Parameters, 1, 2), - logger => logger.Log(LogLevel.Information, messageWith2Parameters, 1, 2) - ], - () => - [ - logger => logger.LogTrace(messageWith3Parameters, 1, 2, 3), - logger => logger.LogDebug(messageWith3Parameters, 1, 2, 3), - logger => logger.LogInformation(messageWith3Parameters, 1, 2, 3), - logger => logger.LogWarning(messageWith3Parameters, 1, 2, 3), - logger => logger.LogError(messageWith3Parameters, 1, 2, 3), - logger => logger.LogCritical(messageWith3Parameters, 1, 2, 3), - logger => logger.Log(LogLevel.Information, messageWith3Parameters, 1, 2, 3) - ], - () => - [ - logger => logger.LogTrace(messageWith4Parameters, 1, 2, 3, 4), - logger => logger.LogDebug(messageWith4Parameters, 1, 2, 3, 4), - logger => logger.LogInformation(messageWith4Parameters, 1, 2, 3, 4), - logger => logger.LogWarning(messageWith4Parameters, 1, 2, 3, 4), - logger => logger.LogError(messageWith4Parameters, 1, 2, 3, 4), - logger => logger.LogCritical(messageWith4Parameters, 1, 2, 3, 4), - logger => logger.Log(LogLevel.Information, messageWith4Parameters, 1, 2, 3, 4) - ], - () => - [ - logger => logger.LogTrace(messageWith5Parameters, 1, 2, 3, 4, 5), - logger => logger.LogDebug(messageWith5Parameters, 1, 2, 3, 4, 5), - logger => logger.LogInformation(messageWith5Parameters, 1, 2, 3, 4, 5), - logger => logger.LogWarning(messageWith5Parameters, 1, 2, 3, 4, 5), - logger => logger.LogError(messageWith5Parameters, 1, 2, 3, 4, 5), - logger => logger.LogCritical(messageWith5Parameters, 1, 2, 3, 4, 5), - logger => logger.Log(LogLevel.Information, messageWith5Parameters, 1, 2, 3, 4, 5) - ], - () => - [ - logger => logger.LogTrace(messageWith6Parameters, 1, 2, 3, 4, 5, 6), - logger => logger.LogDebug(messageWith6Parameters, 1, 2, 3, 4, 5, 6), - logger => logger.LogInformation(messageWith6Parameters, 1, 2, 3, 4, 5, 6), - logger => logger.LogWarning(messageWith6Parameters, 1, 2, 3, 4, 5, 6), - logger => logger.LogError(messageWith6Parameters, 1, 2, 3, 4, 5, 6), - logger => logger.LogCritical(messageWith6Parameters, 1, 2, 3, 4, 5, 6), - logger => logger.Log(LogLevel.Information, messageWith6Parameters, 1, 2, 3, 4, 5, 6) - ] - ]; - } -} diff --git a/tests/AutoLoggerMessageGenerator.IntegrationTests/LogPropertiesAttributeTests.cs b/tests/AutoLoggerMessageGenerator.IntegrationTests/LogPropertiesAttributeTests.cs index d6c3ad6..17b15ed 100644 --- a/tests/AutoLoggerMessageGenerator.IntegrationTests/LogPropertiesAttributeTests.cs +++ b/tests/AutoLoggerMessageGenerator.IntegrationTests/LogPropertiesAttributeTests.cs @@ -13,7 +13,9 @@ internal class LogPropertiesAttributeTests public async Task AllLogPropertiesHaveToBeLogged() { IEvent generatorCallCapturedEvent = new GeneratorCallCapturedEvent { Id = Guid.NewGuid() }; - var proxy = DispatchProxyExecutionVerificationDecorator.Decorate(generatorCallCapturedEvent); + var proxy = DispatchProxyExecutionVerificationDecorator.Decorate( + generatorCallCapturedEvent, Constants.LoggerMessageGeneratorName + ); generatorCallCapturedEvent = (IEvent) proxy; var propertiesCount = typeof(GeneratorCallCapturedEvent) diff --git a/tests/AutoLoggerMessageGenerator.IntegrationTests/LogWithAllParameterRangeTests.cs b/tests/AutoLoggerMessageGenerator.IntegrationTests/LogWithAllParameterRangeTests.cs new file mode 100644 index 0000000..7eb659a --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.IntegrationTests/LogWithAllParameterRangeTests.cs @@ -0,0 +1,127 @@ +using Microsoft.Extensions.Logging; + +namespace AutoLoggerMessageGenerator.IntegrationTests; + +internal class LogWithAllParameterRangeTests +{ + [Test] + [MethodDataSource(nameof(LogMethodsWithDifferentParameters))] + public async Task WithAllLogMethods_RequestShouldBeForwardedToLoggerMessageSourceGenerator( + Action logCall) + { + ILogger logger = LoggerFactory.Create(c => + c.AddSimpleConsole().SetMinimumLevel(LogLevel.Trace) + ).CreateLogger(); + + var proxy = DispatchProxyExecutionVerificationDecorator.Decorate(logger, + Constants.LoggerMessageGeneratorName, methodName => methodName == "Log"); + + logCall((ILogger) proxy); + + await Assert.That(proxy.ExecutionsWithoutGenerator).IsEmpty(); + await Assert.That(proxy.ExecutionsFromGenerator.Count).IsEqualTo(1); + } + + [Test] + [MethodDataSource(nameof(LogMethodsWithUnsupportedParametersCount))] + public async Task WithUnsupportedParametersCount_RequestShouldBeForwardedToOriginalImplementation(Action logCall) + { + ILogger logger = LoggerFactory.Create(c => + c.AddSimpleConsole().SetMinimumLevel(LogLevel.Trace) + ).CreateLogger(); + + var proxy = DispatchProxyExecutionVerificationDecorator.Decorate(logger, + Constants.LoggerMessageGeneratorName, methodName => methodName == "Log"); + + logCall((ILogger) proxy); + + await Assert.That(proxy.ExecutionsFromGenerator).IsEmpty(); + await Assert.That(proxy.ExecutionsWithoutGenerator.Count).IsEqualTo(1); + await Assert.That(proxy.ExecutionsWithoutGenerator.Where(c => !c.Contains("LoggerExtensions.Log"))).IsEmpty(); + } + + public static IEnumerable>> LogMethodsWithDifferentParameters() + { + const string messageWithoutParameters = "Event received"; + const string messageWith1Parameters = "Event received: {arg1}"; + const string messageWith2Parameters = "Event received: {arg1} {arg2}"; + const string messageWith3Parameters = "Event received: {arg1} {arg2} {arg3}"; + const string messageWith4Parameters = "Event received: {arg1} {arg2} {arg3} {arg4}"; + const string messageWith5Parameters = "Event received: {arg1} {arg2} {arg3} {arg4} {arg5}"; + const string messageWith6Parameters = "Event received: {arg1} {arg2} {arg3} {arg4} {arg5} {arg6}"; + + return + [ + () => l => l.LogTrace(messageWithoutParameters), + () => l => l.LogDebug(messageWithoutParameters), + () => l => l.LogInformation(messageWithoutParameters), + () => l => l.LogWarning(messageWithoutParameters), + () => l => l.LogError(messageWithoutParameters), + () => l => l.LogCritical(messageWithoutParameters), + () => l => l.Log(LogLevel.Information, messageWithoutParameters), + + () => l => l.LogTrace(messageWith1Parameters, 1), + () => l => l.LogDebug(messageWith1Parameters, 1), + () => l => l.LogInformation(messageWith1Parameters, 1), + () => l => l.LogWarning(messageWith1Parameters, 1), + () => l => l.LogError(messageWith1Parameters, 1), + () => l => l.LogCritical(messageWith1Parameters, 1), + () => l => l.Log(LogLevel.Information, messageWith1Parameters, 1), + + () => l => l.LogTrace(messageWith2Parameters, 1, 2), + () => l => l.LogDebug(messageWith2Parameters, 1, 2), + () => l => l.LogInformation(messageWith2Parameters, 1, 2), + () => l => l.LogWarning(messageWith2Parameters, 1, 2), + () => l => l.LogError(messageWith2Parameters, 1, 2), + () => l => l.LogCritical(messageWith2Parameters, 1, 2), + () => l => l.Log(LogLevel.Information, messageWith2Parameters, 1, 2), + + () => l => l.LogTrace(messageWith3Parameters, 1, 2, 3), + () => l => l.LogDebug(messageWith3Parameters, 1, 2, 3), + () => l => l.LogInformation(messageWith3Parameters, 1, 2, 3), + () => l => l.LogWarning(messageWith3Parameters, 1, 2, 3), + () => l => l.LogError(messageWith3Parameters, 1, 2, 3), + () => l => l.LogCritical(messageWith3Parameters, 1, 2, 3), + () => l => l.Log(LogLevel.Information, messageWith3Parameters, 1, 2, 3), + + () => l => l.LogTrace(messageWith4Parameters, 1, 2, 3, 4), + () => l => l.LogDebug(messageWith4Parameters, 1, 2, 3, 4), + () => l => l.LogInformation(messageWith4Parameters, 1, 2, 3, 4), + () => l => l.LogWarning(messageWith4Parameters, 1, 2, 3, 4), + () => l => l.LogError(messageWith4Parameters, 1, 2, 3, 4), + () => l => l.LogCritical(messageWith4Parameters, 1, 2, 3, 4), + () => l => l.Log(LogLevel.Information, messageWith4Parameters, 1, 2, 3, 4), + + () => l => l.LogTrace(messageWith5Parameters, 1, 2, 3, 4, 5), + () => l => l.LogDebug(messageWith5Parameters, 1, 2, 3, 4, 5), + () => l => l.LogInformation(messageWith5Parameters, 1, 2, 3, 4, 5), + () => l => l.LogWarning(messageWith5Parameters, 1, 2, 3, 4, 5), + () => l => l.LogError(messageWith5Parameters, 1, 2, 3, 4, 5), + () => l => l.LogCritical(messageWith5Parameters, 1, 2, 3, 4, 5), + () => l => l.Log(LogLevel.Information, messageWith5Parameters, 1, 2, 3, 4, 5), + + () => l => l.LogTrace(messageWith6Parameters, 1, 2, 3, 4, 5, 6), + () => l => l.LogDebug(messageWith6Parameters, 1, 2, 3, 4, 5, 6), + () => l => l.LogInformation(messageWith6Parameters, 1, 2, 3, 4, 5, 6), + () => l => l.LogWarning(messageWith6Parameters, 1, 2, 3, 4, 5, 6), + () => l => l.LogError(messageWith6Parameters, 1, 2, 3, 4, 5, 6), + () => l => l.LogCritical(messageWith6Parameters, 1, 2, 3, 4, 5, 6), + () => l => l.Log(LogLevel.Information, messageWith6Parameters, 1, 2, 3, 4, 5, 6) + ]; + } + + public static IEnumerable>> LogMethodsWithUnsupportedParametersCount() + { + const string messageWith7Parameters = "Event received: {arg1} {arg2} {arg3} {arg4} {arg5} {arg6} {arg7}"; + return + [ + () => l => l.LogTrace(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), + () => l => l.LogDebug(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), + () => l => l.LogInformation(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), + () => l => l.LogWarning(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), + () => l => l.LogError(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), + () => l => l.LogCritical(messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7), + () => l => l.Log(LogLevel.Information, messageWith7Parameters, 1, 2, 3, 4, 5, 6, 7) + ]; + } +} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/AutoLoggerMessageGenerator.UnitTests.Build.targets b/tests/AutoLoggerMessageGenerator.UnitTests/AutoLoggerMessageGenerator.UnitTests.Build.targets index 1a85b03..8d9e28b 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/AutoLoggerMessageGenerator.UnitTests.Build.targets +++ b/tests/AutoLoggerMessageGenerator.UnitTests/AutoLoggerMessageGenerator.UnitTests.Build.targets @@ -7,9 +7,9 @@ - - - + + + diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/BaseSourceGeneratorTest.cs b/tests/AutoLoggerMessageGenerator.UnitTests/BaseSourceGeneratorTest.cs index 5be1f64..d285b32 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/BaseSourceGeneratorTest.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/BaseSourceGeneratorTest.cs @@ -66,7 +66,7 @@ public void Main() return (compilation, syntaxTree); } - protected static (InvocationExpressionSyntax, IMethodSymbol?, SemanticModel?) FindLoggerMethodInvocation( + protected static (InvocationExpressionSyntax, IMethodSymbol?, SemanticModel?) FindMethodInvocation( Compilation? compilation, SyntaxTree syntaxTree) { var invocationExpression = syntaxTree.GetRoot().DescendantNodes().OfType().First(); diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Caching/InputSourceComparerTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Caching/InputSourceComparerTests.cs index 41325bd..5f8e9d8 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Caching/InputSourceComparerTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Caching/InputSourceComparerTests.cs @@ -8,7 +8,16 @@ namespace AutoLoggerMessageGenerator.UnitTests.Caching; -using InputSource = (Compilation Compilation, (SourceGeneratorConfiguration Configuration, (ImmutableArray References, ImmutableArray LogCalls) Others) Others); +using InputSource = ( + Compilation Compilation, + ( + SourceGeneratorConfiguration Configuration, + ( + ImmutableArray References, + ImmutableArray LogCalls + ) Others + ) Others +); internal class InputSourceComparerTests { @@ -21,7 +30,7 @@ public async Task Equals_WithDifferentCompilation_ShouldReturnTrue() var inputSource1 = CreateInputSource(compilation: compilation1); var inputSource2 = CreateInputSource(compilation: compilation2); - var sut = new InputSourceComparer(); + var sut = new LogCallInputSourceComparer(); var result = sut.Equals(inputSource1, inputSource2); await Assert.That(result).IsTrue(); @@ -30,13 +39,13 @@ public async Task Equals_WithDifferentCompilation_ShouldReturnTrue() [Test] public async Task Equals_WithDifferentConfiguration_ShouldReturnFalse() { - var configuration1 = new SourceGeneratorConfiguration(true, true, true, true, true); - var configuration2 = new SourceGeneratorConfiguration(false, false, false, false, false); + var configuration1 = new SourceGeneratorConfiguration(true, true, true, true, true, true); + var configuration2 = new SourceGeneratorConfiguration(false, false, false, false, false, false); var inputSource1 = CreateInputSource(configuration: configuration1); var inputSource2 = CreateInputSource(configuration: configuration2); - var sut = new InputSourceComparer(); + var sut = new LogCallInputSourceComparer(); var result = sut.Equals(inputSource1, inputSource2); await Assert.That(result).IsFalse(); @@ -55,7 +64,7 @@ public async Task Equals_WithDifferentReferences_ShouldReturnFalse( var inputSource1 = CreateInputSource(references: references1); var inputSource2 = CreateInputSource(references: references2); - var sut = new InputSourceComparer(); + var sut = new LogCallInputSourceComparer(); var result = sut.Equals(inputSource1, inputSource2); await Assert.That(result).IsFalse(); @@ -64,13 +73,13 @@ public async Task Equals_WithDifferentReferences_ShouldReturnFalse( [Test] public async Task Equals_WithDifferentLogCalls_ShouldReturnFalse() { - ImmutableArray logCalls = [new LogCall { Message = "message1"}]; - ImmutableArray logCalls2 = [new LogCall { Message = "message2"}]; + ImmutableArray logCalls = [new LogMessageCall { Message = "message1"}]; + ImmutableArray logCalls2 = [new LogMessageCall { Message = "message2"}]; var inputSource1 = CreateInputSource(logCalls: logCalls); var inputSource2 = CreateInputSource(logCalls: logCalls2); - var sut = new InputSourceComparer(); + var sut = new LogCallInputSourceComparer(); var result = sut.Equals(inputSource1, inputSource2); await Assert.That(result).IsFalse(); @@ -79,14 +88,14 @@ public async Task Equals_WithDifferentLogCalls_ShouldReturnFalse() private static InputSource CreateInputSource(Compilation? compilation = default, SourceGeneratorConfiguration? configuration = default, ImmutableArray? references = default, - ImmutableArray? logCalls = default) + ImmutableArray? logCalls = default) { compilation ??= CSharpCompilation.Create(default); - configuration ??= new SourceGeneratorConfiguration(true, true, true, true, true); + configuration ??= new SourceGeneratorConfiguration(true, true, true, true, true, true); references ??= [new Reference("some lib", new Version("1.2.3"))]; logCalls ??= [ - new LogCall(Guid.NewGuid(), MockLogCallLocationBuilder.Build("some file", 1, 2), "namespace", "class", "name", + new LogMessageCall(Guid.NewGuid(), MockLogCallLocationBuilder.Build("some file", 1, 2), "namespace", "class", "name", "information", "message", []) ]; diff --git a/src/AutoLoggerMessageGenerator.BuildOutput/LoggerExtensions.g.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingExtensionsOverrides.verified.txt similarity index 99% rename from src/AutoLoggerMessageGenerator.BuildOutput/LoggerExtensions.g.cs rename to tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingExtensionsOverrides.verified.txt index 0b3edad..da059ef 100644 --- a/src/AutoLoggerMessageGenerator.BuildOutput/LoggerExtensions.g.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingExtensionsOverrides.verified.txt @@ -1,11 +1,11 @@ -// +// #nullable enable using System; namespace Microsoft.Extensions.Logging { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("AutoLoggerMessageGenerator", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("AutoLoggerMessageGenerator", "1.2.3.4")] [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] [System.Diagnostics.DebuggerStepThrough] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerScopeExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingScopeExtensionsOverrides.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerScopeExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingScopeExtensionsOverrides.verified.txt new file mode 100644 index 0000000..713899f --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerScopeExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingScopeExtensionsOverrides.verified.txt @@ -0,0 +1,50 @@ +// +#nullable enable + +using System; + +namespace Microsoft.Extensions.Logging +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("AutoLoggerMessageGenerator", "1.2.3.4")] + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [System.Diagnostics.DebuggerStepThrough] + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public static class GenericLoggerScopeExtensions + { + public static IDisposable? BeginScope(this ILogger @logger, string @message) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1, @arg2 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4 }); + } + + public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0, T1 @arg1, T2 @arg2, T3 @arg3, T4 @arg4, T5 @arg5) + { + return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0, @arg1, @arg2, @arg3, @arg4, @arg5 }); + } + + } +} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerExtensionsEmitterTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerExtensionsEmitterTests.cs index 22b6f0f..5a585af 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerExtensionsEmitterTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerExtensionsEmitterTests.cs @@ -3,12 +3,12 @@ namespace AutoLoggerMessageGenerator.UnitTests.Emitters; -internal class LoggerExtensionsEmitterTests +internal class GenericLoggerExtensionsEmitterTests { [Test] - public async Task Emit_ShouldGenerateValidLoggingExtensionsAttribute() + public async Task Emit_ShouldGenerateValidLoggingExtensionsOverrides() { - var sourceCode = LoggerExtensionsEmitter.Emit(); + var sourceCode = GenericLoggerExtensionsEmitter.Emit(); await Verify(sourceCode).AddCodeGeneratedAttributeScrubber(); } } diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerInterceptorsEmitterTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerInterceptorsEmitterTests.cs index 99c8b39..c0aee98 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerInterceptorsEmitterTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerInterceptorsEmitterTests.cs @@ -11,44 +11,44 @@ internal class LoggerInterceptorsEmitterTests [Test] public async Task Emit_ShouldGenerateValidLoggingExtensionsAttribute() { - ImmutableArray logCalls = + ImmutableArray logCalls = [ - new LogCall( + new LogMessageCall( Id: Guid.NewGuid(), Location: MockLogCallLocationBuilder.Build("file", 1, 11), Namespace: "namespace1", ClassName: "class1", - Name: "name1", + MethodName: "name1", LogLevel: "Information", Message: "Message1", - Parameters: [new LogCallParameter("string", MessageParameterName, LogCallParameterType.Message)] + Parameters: [new CallParameter("string", MessageParameterName, CallParameterType.Message)] ), - new LogCall( + new LogMessageCall( Id: Guid.NewGuid(), Location: MockLogCallLocationBuilder.Build("file2", 2, 22), Namespace: "namespace2", ClassName: "class2", - Name: "name2", + MethodName: "name2", LogLevel: "Warning", Message: "Message2", Parameters: [ - new LogCallParameter("string", MessageParameterName, LogCallParameterType.Message), - new LogCallParameter("int", "@intParam", LogCallParameterType.Others) + new CallParameter("string", MessageParameterName, CallParameterType.Message), + new CallParameter("int", "@intParam", CallParameterType.Others) ] ), - new LogCall( + new LogMessageCall( Id: Guid.NewGuid(), Location: MockLogCallLocationBuilder.Build("file3", 3, 33), Namespace: "namespace3", ClassName: "class3", - Name: "name3", + MethodName: "name3", LogLevel: "Error", Message: "Message3", Parameters: [ - new LogCallParameter("string", MessageParameterName, LogCallParameterType.Message), - new LogCallParameter("int", "@intParam", LogCallParameterType.Others), - new LogCallParameter("bool", "@boolParam", LogCallParameterType.Others), - new LogCallParameter("SomeClass", "@objectParam", LogCallParameterType.Others, true) + new CallParameter("string", MessageParameterName, CallParameterType.Message), + new CallParameter("int", "@intParam", CallParameterType.Others), + new CallParameter("bool", "@boolParam", CallParameterType.Others), + new CallParameter("SomeClass", "@objectParam", CallParameterType.Others, true) ] ), ]; diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeExtensionsEmitterTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeExtensionsEmitterTests.cs new file mode 100644 index 0000000..b6459b8 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeExtensionsEmitterTests.cs @@ -0,0 +1,14 @@ +using AutoLoggerMessageGenerator.Emitters; +using AutoLoggerMessageGenerator.UnitTests.Scrubbers; + +namespace AutoLoggerMessageGenerator.UnitTests.Emitters; + +internal class GenericLoggerScopeExtensionsEmitterTests +{ + [Test] + public async Task Emit_ShouldGenerateValidLoggingScopeExtensionsOverrides() + { + var sourceCode = GenericLoggerScopeExtensionsEmitter.Emit(); + await Verify(sourceCode).AddCodeGeneratedAttributeScrubber(); + } +} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerScopeInterceptors.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerScopeInterceptors.verified.txt new file mode 100644 index 0000000..829f4e8 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerScopeInterceptors.verified.txt @@ -0,0 +1,36 @@ +// +#nullable enable + +using System; + +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("AutoLoggerMessageGenerator", "1.2.3.4")] + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [System.Diagnostics.DebuggerStepThrough] + internal static class BeginScopeInterceptors + { + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [FakeInterceptableLocation(-1, "ZmlsZSgxLDExKQ==")] + public static IDisposable? LogScope_namespace1class1_1_11(this ILogger @logger, string @message) + { + return Microsoft.Extensions.Logging.AutoLoggerMessage.LoggerScopes.LogScope_namespace1class1_1_11(@logger); + } + + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [FakeInterceptableLocation(-1, "ZmlsZTIoMiwyMik=")] + public static IDisposable? LogScope_namespace2class2_2_22(this ILogger @logger, string @message, int @intParam) + { + return Microsoft.Extensions.Logging.AutoLoggerMessage.LoggerScopes.LogScope_namespace2class2_2_22(@logger, @intParam); + } + + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [FakeInterceptableLocation(-1, "ZmlsZTMoMywzMyk=")] + public static IDisposable? LogScope_namespace3class3_3_33(this ILogger @logger, string @message, int @intParam, bool @boolParam, SomeClass @objectParam) + { + return Microsoft.Extensions.Logging.AutoLoggerMessage.LoggerScopes.LogScope_namespace3class3_3_33(@logger, @intParam, @boolParam, @objectParam); + } + + } +} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.cs new file mode 100644 index 0000000..3c7ed98 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.cs @@ -0,0 +1,53 @@ +using System.Collections.Immutable; +using AutoLoggerMessageGenerator.Emitters; +using AutoLoggerMessageGenerator.Models; +using AutoLoggerMessageGenerator.UnitTests.Scrubbers; +using static AutoLoggerMessageGenerator.Constants; + +namespace AutoLoggerMessageGenerator.UnitTests.Emitters; + +internal class LoggerScopeInterceptorsEmitterTests +{ + [Test] + public async Task Emit_WithGivenConfiguration_ShouldGenerateValidLoggerScopeInterceptors() + { + ImmutableArray loggerScopes = + [ + new( + Location: MockLogCallLocationBuilder.Build("file", 1, 11), + Namespace: "namespace1", + ClassName: "class1", + MethodName: "name1", + Message: "Message1", + Parameters: [new CallParameter("string", MessageParameterName, CallParameterType.Message)] + ), + new( + Location: MockLogCallLocationBuilder.Build("file2", 2, 22), + Namespace: "namespace2", + ClassName: "class2", + MethodName: "name2", + Message: "Message2", + Parameters: [ + new CallParameter("string", MessageParameterName, CallParameterType.Message), + new CallParameter("int", "@intParam", CallParameterType.Others) + ] + ), + new( + Location: MockLogCallLocationBuilder.Build("file3", 3, 33), + Namespace: "namespace3", + ClassName: "class3", + MethodName: "name3", + Message: "Message3", + Parameters: [ + new CallParameter("string", MessageParameterName, CallParameterType.Message), + new CallParameter("int", "@intParam", CallParameterType.Others), + new CallParameter("bool", "@boolParam", CallParameterType.Others), + new CallParameter("SomeClass", "@objectParam", CallParameterType.Others, true) + ] + ), + ]; + + var sourceCode = LoggerScopeInterceptorsEmitter.Emit(loggerScopes); + await Verify(sourceCode).AddCodeGeneratedAttributeScrubber(); + } +} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopesEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerDefineScopedFunctors.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopesEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerDefineScopedFunctors.verified.txt new file mode 100644 index 0000000..6da4792 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopesEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerDefineScopedFunctors.verified.txt @@ -0,0 +1,31 @@ +// +#nullable enable + +using System; + +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("AutoLoggerMessageGenerator", "1.2.3.4")] + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + public static class LoggerScopes + { + private static readonly Func _LogScope_namespace1class1_1_11 = LoggerMessage.DefineScope("Message1"); + public static IDisposable? LogScope_namespace1class1_1_11(ILogger @logger) + { + return _LogScope_namespace1class1_1_11(@logger); + } + + private static readonly Func _LogScope_namespace2class2_2_22 = LoggerMessage.DefineScope("Message2"); + public static IDisposable? LogScope_namespace2class2_2_22(ILogger @logger, int @intParam) + { + return _LogScope_namespace2class2_2_22(@logger, @intParam); + } + + private static readonly Func _LogScope_namespace3class3_3_33 = LoggerMessage.DefineScope("Message3"); + public static IDisposable? LogScope_namespace3class3_3_33(ILogger @logger, int @intParam, bool @boolParam, SomeClass @objectParam) + { + return _LogScope_namespace3class3_3_33(@logger, @intParam, @boolParam, @objectParam); + } + + } +} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopesEmitterTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopesEmitterTests.cs new file mode 100644 index 0000000..1680793 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopesEmitterTests.cs @@ -0,0 +1,53 @@ +using System.Collections.Immutable; +using AutoLoggerMessageGenerator.Emitters; +using AutoLoggerMessageGenerator.Models; +using AutoLoggerMessageGenerator.UnitTests.Scrubbers; +using static AutoLoggerMessageGenerator.Constants; + +namespace AutoLoggerMessageGenerator.UnitTests.Emitters; + +internal class LoggerScopesEmitterTests +{ + [Test] + public async Task Emit_WithGivenConfiguration_ShouldGenerateValidLoggerDefineScopedFunctors() + { + ImmutableArray loggerScopes = + [ + new( + Location: MockLogCallLocationBuilder.Build("file", 1, 11), + Namespace: "namespace1", + ClassName: "class1", + MethodName: "name1", + Message: "Message1", + Parameters: [new CallParameter("string", MessageParameterName, CallParameterType.Message)] + ), + new( + Location: MockLogCallLocationBuilder.Build("file2", 2, 22), + Namespace: "namespace2", + ClassName: "class2", + MethodName: "name2", + Message: "Message2", + Parameters: [ + new CallParameter("string", MessageParameterName, CallParameterType.Message), + new CallParameter("int", "@intParam", CallParameterType.Others) + ] + ), + new( + Location: MockLogCallLocationBuilder.Build("file3", 3, 33), + Namespace: "namespace3", + ClassName: "class3", + MethodName: "name3", + Message: "Message3", + Parameters: [ + new CallParameter("string", MessageParameterName, CallParameterType.Message), + new CallParameter("int", "@intParam", CallParameterType.Others), + new CallParameter("bool", "@boolParam", CallParameterType.Others), + new CallParameter("SomeClass", "@objectParam", CallParameterType.Others, true) + ] + ), + ]; + + var sourceCode = LoggerScopesEmitter.Emit(loggerScopes); + await Verify(sourceCode).AddCodeGeneratedAttributeScrubber(); + } +} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallCallerExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/EnclosingClassExtractorTests.cs similarity index 79% rename from tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallCallerExtractorTests.cs rename to tests/AutoLoggerMessageGenerator.UnitTests/Extractors/EnclosingClassExtractorTests.cs index 6bc7459..946e4a5 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallCallerExtractorTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/EnclosingClassExtractorTests.cs @@ -2,7 +2,7 @@ namespace AutoLoggerMessageGenerator.UnitTests.Extractors; -internal class LogCallCallerExtractorTests : BaseSourceGeneratorTest +internal class EnclosingClassExtractorTests : BaseSourceGeneratorTest { [Test] [Arguments(false, Namespace, ClassName)] @@ -12,9 +12,9 @@ public async Task Extract_WithLogCallAndGivenNamespace_ShouldReturnExpectedNames { var message = $$"""{{LoggerName}}.LogInformation("Hello world");"""; var (_, syntaxTree) = await CompileSourceCode(message, useGlobalNamespace: useGlobalNamespace); - var (invocationExpression, _, _) = FindLoggerMethodInvocation(null, syntaxTree); + var (invocationExpression, _, _) = FindMethodInvocation(null, syntaxTree); - var (ns, className) = LogCallCallerExtractor.Extract(invocationExpression); + var (ns, className) = EnclosingClassExtractor.Extract(invocationExpression); await Assert.That(ns).IsEqualTo(expectedNamespace); await Assert.That(className).IsEqualTo(expectedClassName); @@ -34,9 +34,9 @@ class Inner } """; var (_, syntaxTree) = await CompileSourceCode(string.Empty, additionalDeclaration); - var (invocationExpression, _, _) = FindLoggerMethodInvocation(null, syntaxTree); + var (invocationExpression, _, _) = FindMethodInvocation(null, syntaxTree); - var (ns, className) = LogCallCallerExtractor.Extract(invocationExpression); + var (ns, className) = EnclosingClassExtractor.Extract(invocationExpression); await Assert.That(ns).IsEqualTo(Namespace); await Assert.That(className).IsEqualTo(ClassName); diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_HashBasedInterceptor.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt similarity index 93% rename from tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_HashBasedInterceptor.verified.txt rename to tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt index 49200cc..84308d4 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_HashBasedInterceptor.verified.txt +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt @@ -1,4 +1,4 @@ -{ +{ Id: Guid_1, Location: { FilePath: path/testFile.cs, @@ -28,7 +28,7 @@ }, Namespace: Foo, ClassName: Test, - Name: LogInformation, + MethodName: LogInformation, LogLevel: Information, Message: Hello world {arg1} {arg2}, Parameters: [ @@ -50,5 +50,6 @@ Type: Others, HasPropertiesToLog: false } - ] -} + ], + GeneratedMethodName: Log_FooTest_12_16 +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_PathBasedInterceptor.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt similarity index 93% rename from tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_PathBasedInterceptor.verified.txt rename to tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt index 8314179..29c586b 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_PathBasedInterceptor.verified.txt +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt @@ -1,4 +1,4 @@ -{ +{ Id: Guid_1, Location: { FilePath: path/testFile.cs, @@ -28,7 +28,7 @@ }, Namespace: Foo, ClassName: Test, - Name: LogInformation, + MethodName: LogInformation, LogLevel: Information, Message: Hello world {arg1} {arg2}, Parameters: [ @@ -50,5 +50,6 @@ Type: Others, HasPropertiesToLog: false } - ] -} + ], + GeneratedMethodName: Log_FooTest_12_16 +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=without parameters_sourceCode=HashBasedInterceptor.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=without parameters_sourceCode=HashBasedInterceptor.verified.txt new file mode 100644 index 0000000..2b6a799 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=without parameters_sourceCode=HashBasedInterceptor.verified.txt @@ -0,0 +1,43 @@ +{ + Id: Guid_1, + Location: { + FilePath: path/testFile.cs, + Line: 12, + Character: 16, + InterceptableLocationSyntax: [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "VSNN9b1ciRV2FhFsas3dUJkAAAB0ZXN0RmlsZS5jcw==")], + Context: { + Kind: SourceFile, + SourceSpan: { + Start: 146, + Length: 36 + }, + SourceTree: { + FilePath: path/testFile.cs, + Length: 191, + HasCompilationUnitRoot: true, + Options: { + LanguageVersion: CSharp12, + Language: C#, + DocumentationMode: Parse, + Errors: null + } + }, + IsInSource: true, + IsInMetadata: false + } + }, + Namespace: Foo, + ClassName: Test, + MethodName: LogInformation, + LogLevel: Information, + Message: Hello world, + Parameters: [ + { + NativeType: global::System.String, + Name: @message, + Type: Message, + HasPropertiesToLog: false + } + ], + GeneratedMethodName: Log_FooTest_12_16 +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=without parameters_sourceCode=PathBasedInterceptor.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=without parameters_sourceCode=PathBasedInterceptor.verified.txt new file mode 100644 index 0000000..3ff25d2 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_description=without parameters_sourceCode=PathBasedInterceptor.verified.txt @@ -0,0 +1,43 @@ +{ + Id: Guid_1, + Location: { + FilePath: path/testFile.cs, + Line: 12, + Character: 16, + InterceptableLocationSyntax: [System.Runtime.CompilerServices.InterceptsLocationAttribute(filePath: @"path/testFile.cs", line: 12, character: 16)], + Context: { + Kind: SourceFile, + SourceSpan: { + Start: 146, + Length: 36 + }, + SourceTree: { + FilePath: path/testFile.cs, + Length: 191, + HasCompilationUnitRoot: true, + Options: { + LanguageVersion: CSharp12, + Language: C#, + DocumentationMode: Parse, + Errors: null + } + }, + IsInSource: true, + IsInMetadata: false + } + }, + Namespace: Foo, + ClassName: Test, + MethodName: LogInformation, + LogLevel: Information, + Message: Hello world, + Parameters: [ + { + NativeType: global::System.String, + Name: @message, + Type: Message, + HasPropertiesToLog: false + } + ], + GeneratedMethodName: Log_FooTest_12_16 +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.cs index 10eb483..010fedb 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.cs @@ -6,20 +6,21 @@ namespace AutoLoggerMessageGenerator.UnitTests.Extractors; internal class LogCallExtractorTests : BaseSourceGeneratorTest { [Test] - [Arguments($$"""{{LoggerName}}.LogInformation("Hello world {arg1} {arg2}", 1, true);""", true)] - [Arguments($$"""{{LoggerName}}.LogInformation(null);""", false)] - [Arguments($$"""{{LoggerName}}.LogInformation("Hello world {arg1}", 1, true);""", false)] - public async Task Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject(string sourceCode, bool isValidCall) + [Arguments("without parameters", $$"""{{LoggerName}}.LogInformation("Hello world");""", true)] + [Arguments("with parameters", $$"""{{LoggerName}}.LogInformation("Hello world {arg1} {arg2}", 1, true);""", true)] + [Arguments("with only null passed", $$"""{{LoggerName}}.LogInformation(null);""", false)] + [Arguments("with parameter count mismatch", $$"""{{LoggerName}}.LogInformation("Hello world {arg1}", 1, true);""", false)] + public async Task Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject(string description, string sourceCode, bool isValidCall) { var (compilation, syntaxTree) = await CompileSourceCode(sourceCode); - var (invocationExpression, methodSymbol, semanticModel) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpression, methodSymbol, semanticModel) = FindMethodInvocation(compilation, syntaxTree); var logCall = LogCallExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); if (isValidCall) { var configuration = InterceptorConfigurationUtilities.GetInterceptorConfiguration(); - await Verify(logCall).UseTextForParameters(configuration); + await Verify(logCall).UseParameters(description, configuration); } else { diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallParametersExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallParametersExtractorTests.cs index 7f796a5..5bcfe27 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallParametersExtractorTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallParametersExtractorTests.cs @@ -24,17 +24,17 @@ private static void Log( """; var (compilation, syntaxTree) = await CompileSourceCode($"Log({parameters});", extensionDeclaration); - var (_, methodSymbol, _) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (_, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); var sut = new LogCallParametersExtractor(); var result = sut.Extract(message, methodSymbol!); - await Assert.That(result).IsEquivalentTo(new LogCallParameter[] + await Assert.That(result).IsEquivalentTo(new CallParameter[] { - new("global::System.String", MessageParameterName, LogCallParameterType.Message), - new("global::System.String", "@EventName", LogCallParameterType.Others), - new("global::System.Int32", "@Time", LogCallParameterType.Others), + new("global::System.String", MessageParameterName, CallParameterType.Message), + new("global::System.String", "@EventName", CallParameterType.Others), + new("global::System.Int32", "@Time", CallParameterType.Others), }); } @@ -46,7 +46,7 @@ public async Task Extract_WithGivenMessageAndNoParameters_ShouldReturnOnlyMessag string extensionDeclaration = $"private static void Log(string {MessageParameterName}) {{}}"; var (compilation, syntaxTree) = await CompileSourceCode($"""Log("{message}");""", extensionDeclaration); - var (_, methodSymbol, _) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (_, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); var sut = new LogCallParametersExtractor(); @@ -55,10 +55,10 @@ public async Task Extract_WithGivenMessageAndNoParameters_ShouldReturnOnlyMessag await Assert.That(result.HasValue).IsTrue(); await Assert.That(result!.Value.Length).IsEqualTo(1); await Assert.That(result.Value[0]).IsEquivalentTo( - new LogCallParameter( + new CallParameter( "global::System.String", MessageParameterName, - LogCallParameterType.Message + CallParameterType.Message ) ); } @@ -88,20 +88,20 @@ private static void Log( 40 ); """, extensionDeclaration); - var (_, methodSymbol, _) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (_, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); var sut = new LogCallParametersExtractor(); var result = sut.Extract(message, methodSymbol!); - await Assert.That(result).IsEquivalentTo(new LogCallParameter[] + await Assert.That(result).IsEquivalentTo(new CallParameter[] { - new("global::Microsoft.Extensions.Logging.LogLevel", LogLevelParameterName, LogCallParameterType.LogLevel), - new("global::Microsoft.Extensions.Logging.EventId", EventIdParameterName, LogCallParameterType.EventId), - new("global::System.Exception", ExceptionParameterName, LogCallParameterType.Exception), - new("global::System.String", MessageParameterName, LogCallParameterType.Message), - new("global::System.String", "@EventName", LogCallParameterType.Others), - new("global::System.Int32", "@Time", LogCallParameterType.Others), + new("global::Microsoft.Extensions.Logging.LogLevel", LogLevelParameterName, CallParameterType.LogLevel), + new("global::Microsoft.Extensions.Logging.EventId", EventIdParameterName, CallParameterType.EventId), + new("global::System.Exception", ExceptionParameterName, CallParameterType.Exception), + new("global::System.String", MessageParameterName, CallParameterType.Message), + new("global::System.String", "@EventName", CallParameterType.Others), + new("global::System.Int32", "@Time", CallParameterType.Others), }); } @@ -132,13 +132,13 @@ private static void Log( var result = sut.Extract(message, methodSymbol); - await Assert.That(result).IsEquivalentTo(new LogCallParameter[] + await Assert.That(result).IsEquivalentTo(new CallParameter[] { - new("global::Microsoft.Extensions.Logging.EventId", "@eventId__", LogCallParameterType.EventId), - new("global::System.String", "@message__", LogCallParameterType.Message), - new("global::System.Int32", "@eventId_", LogCallParameterType.Others), - new("global::System.String", "@message", LogCallParameterType.Others), - new("global::System.Int32", "@time", LogCallParameterType.Others), + new("global::Microsoft.Extensions.Logging.EventId", "@eventId__", CallParameterType.EventId), + new("global::System.String", "@message__", CallParameterType.Message), + new("global::System.Int32", "@eventId_", CallParameterType.Others), + new("global::System.String", "@message", CallParameterType.Others), + new("global::System.Int32", "@time", CallParameterType.Others), }); } @@ -157,7 +157,7 @@ public void Foo({{genericType}} arg) Log<{{genericType}}>("{{message}}", arg); } """); - var (_, methodSymbol, _) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (_, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); var sut = new LogCallParametersExtractor(); diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogLevelExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogLevelExtractorTests.cs index 3286126..ea5c657 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogLevelExtractorTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogLevelExtractorTests.cs @@ -24,7 +24,7 @@ internal class LogLevelExtractorTests : BaseSourceGeneratorTest public async Task Extract_WithGivenMethodCall_ShouldReturnExpectedLogLevel(string methodCall, string? expectedLogLevel) { var (compilation, syntaxTree) = await CompileSourceCode($"{LoggerName}.{methodCall};"); - var (invocationExpression, methodSymbol, _) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpression, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); var result = LogLevelExtractor.Extract(methodSymbol!, invocationExpression); @@ -39,7 +39,7 @@ public async Task Extract_WithNotConstantLogLevel_ShouldReturnNull() Log(logLevel, default); """; var (compilation, syntaxTree) = await CompileSourceCode(sourceCode); - var (invocationExpression, methodSymbol, _) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpression, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); var result = LogLevelExtractor.Extract(methodSymbol!, invocationExpression); diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_sourceCode=logger.LogInformation(-Hello world {arg1} {arg2}-, 1, true);_isValidCall=True.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt similarity index 76% rename from tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_sourceCode=logger.LogInformation(-Hello world {arg1} {arg2}-, 1, true);_isValidCall=True.verified.txt rename to tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt index 6d2de15..e568e25 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallExtractorTests.Extract_WithLogMethodInvocationCode_ShouldTransformThemIntoLogCallObject_sourceCode=logger.LogInformation(-Hello world {arg1} {arg2}-, 1, true);_isValidCall=True.verified.txt +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt @@ -1,19 +1,19 @@ { Id: Guid_1, Location: { - FilePath: , + FilePath: path/testFile.cs, Line: 12, Character: 16, - InterceptableLocationSyntax: [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "bg8SAsBDKqh7V1TqOZu8tZkAAAA=")], + InterceptableLocationSyntax: [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "nedh6WI/ifg2J0oGdnNJkZkAAAB0ZXN0RmlsZS5jcw==")], Context: { Kind: SourceFile, SourceSpan: { Start: 146, - Length: 59 + Length: 55 }, SourceTree: { - FilePath: , - Length: 214, + FilePath: path/testFile.cs, + Length: 210, HasCompilationUnitRoot: true, Options: { LanguageVersion: CSharp12, @@ -28,8 +28,8 @@ }, Namespace: Foo, ClassName: Test, - Name: LogInformation, - LogLevel: Information, + MethodName: BeginScope, + LogLevel: inScope, Message: Hello world {arg1} {arg2}, Parameters: [ { @@ -50,5 +50,6 @@ Type: Others, HasPropertiesToLog: false } - ] + ], + GeneratedMethodName: Log_FooTest_12_16 } \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt new file mode 100644 index 0000000..f556cc5 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt @@ -0,0 +1,55 @@ +{ + Id: Guid_1, + Location: { + FilePath: path/testFile.cs, + Line: 12, + Character: 16, + InterceptableLocationSyntax: [System.Runtime.CompilerServices.InterceptsLocationAttribute(filePath: @"path/testFile.cs", line: 12, character: 16)], + Context: { + Kind: SourceFile, + SourceSpan: { + Start: 146, + Length: 55 + }, + SourceTree: { + FilePath: path/testFile.cs, + Length: 210, + HasCompilationUnitRoot: true, + Options: { + LanguageVersion: CSharp12, + Language: C#, + DocumentationMode: Parse, + Errors: null + } + }, + IsInSource: true, + IsInMetadata: false + } + }, + Namespace: Foo, + ClassName: Test, + MethodName: BeginScope, + LogLevel: inScope, + Message: Hello world {arg1} {arg2}, + Parameters: [ + { + NativeType: global::System.String, + Name: @message, + Type: Message, + HasPropertiesToLog: false + }, + { + NativeType: global::System.Int32, + Name: @arg1, + Type: Others, + HasPropertiesToLog: false + }, + { + NativeType: global::System.Boolean, + Name: @arg2, + Type: Others, + HasPropertiesToLog: false + } + ], + GeneratedMethodName: Log_FooTest_12_16 +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.cs new file mode 100644 index 0000000..1abea54 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.cs @@ -0,0 +1,29 @@ +using AutoLoggerMessageGenerator.Extractors; +using AutoLoggerMessageGenerator.UnitTests.Utilities; + +namespace AutoLoggerMessageGenerator.UnitTests.Extractors; + +internal class LoggerScopeCallExtractorTests : BaseSourceGeneratorTest +{ + [Test] + [Arguments("with parameters", $$"""{{LoggerName}}.BeginScope("Hello world {arg1} {arg2}", 1, true);""", true)] + [Arguments("with null passed", $"""{LoggerName}.BeginScope(null);""", false)] + [Arguments("with parameter mismatch", $$"""{{LoggerName}}.BeginScope("Hello world {arg1}", 1, true);""", false)] + public async Task Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject(string description, string sourceCode, bool isValidCall) + { + var (compilation, syntaxTree) = await CompileSourceCode(sourceCode); + var (invocationExpression, methodSymbol, semanticModel) = FindMethodInvocation(compilation, syntaxTree); + + var loggerScope = LogCallExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); + + if (isValidCall) + { + var configuration = InterceptorConfigurationUtilities.GetInterceptorConfiguration(); + await Verify(loggerScope).UseParameters(description, configuration); + } + else + { + await Assert.That(loggerScope.HasValue).IsFalse(); + } + } +} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallMessageParameterNamesExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/MessageParameterNamesExtractorTests.cs similarity index 88% rename from tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallMessageParameterNamesExtractorTests.cs rename to tests/AutoLoggerMessageGenerator.UnitTests/Extractors/MessageParameterNamesExtractorTests.cs index 4adbdd6..208af50 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallMessageParameterNamesExtractorTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/MessageParameterNamesExtractorTests.cs @@ -2,7 +2,7 @@ namespace AutoLoggerMessageGenerator.UnitTests.Extractors; -internal class LogCallMessageParameterNamesExtractorTests +internal class MessageParameterNamesExtractorTests { [Test] [Arguments("Hello world {1}, {2}!", new[] { "1", "2" })] @@ -18,7 +18,7 @@ internal class LogCallMessageParameterNamesExtractorTests [Arguments(null, new string[0])] public async Task Extract_WithGivenMessage_ShouldReturnGivenMessageParameterNames(string? message, params string[] expectedMessageParameters) { - var result = LogCallMessageParameterNamesExtractor.Extract(message); + var result = MessageParameterNamesExtractor.Extract(message); await Assert.That(result).IsEquivalentTo(expectedMessageParameters); } } diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallMessageExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/MessageParameterTextExtractorTests.cs similarity index 75% rename from tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallMessageExtractorTests.cs rename to tests/AutoLoggerMessageGenerator.UnitTests/Extractors/MessageParameterTextExtractorTests.cs index d70c093..977db2d 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallMessageExtractorTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/MessageParameterTextExtractorTests.cs @@ -2,7 +2,7 @@ namespace AutoLoggerMessageGenerator.UnitTests.Extractors; -internal class LogCallMessageExtractorTests : BaseSourceGeneratorTest +internal class MessageParameterTextExtractorTests : BaseSourceGeneratorTest { [Test] [Arguments("Hello world {1}, {2}!")] @@ -10,9 +10,9 @@ internal class LogCallMessageExtractorTests : BaseSourceGeneratorTest public async Task Extract_WithPassingLiteralValues_ShouldReturnExpectedMessage(string expectedMessage) { var (compilation, syntaxTree) = await CompileSourceCode($"""{LoggerName}.LogInformation("{expectedMessage}");"""); - var (invocationExpression, methodSymbol, semanticModel) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpression, methodSymbol, semanticModel) = FindMethodInvocation(compilation, syntaxTree); - var result = LogCallMessageExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); + var result = MessageParameterTextExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); await Assert.That(result).IsEqualTo(expectedMessage); } @@ -25,9 +25,9 @@ public async Task Extract_WithPassingConstClassMembers_ShouldReturnExpectedMessa $"{LoggerName}.LogInformation(Message);", $"""private const string Message = "{expectedMessage}";""" ); - var (invocationExpression, methodSymbol, semanticModel) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpression, methodSymbol, semanticModel) = FindMethodInvocation(compilation, syntaxTree); - var result = LogCallMessageExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); + var result = MessageParameterTextExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); await Assert.That(result).IsEqualTo(expectedMessage); } @@ -41,9 +41,9 @@ public async Task Extract_WithPassingLocalConstVariable_ShouldReturnExpectedMess {LoggerName}.LogInformation(message); """ ); - var (invocationExpression, methodSymbol, semanticModel) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpression, methodSymbol, semanticModel) = FindMethodInvocation(compilation, syntaxTree); - var result = LogCallMessageExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); + var result = MessageParameterTextExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); await Assert.That(result).IsEqualTo(expectedMessage); } @@ -52,9 +52,9 @@ public async Task Extract_WithPassingLocalConstVariable_ShouldReturnExpectedMess public async Task Extract_WithPassingNullValues_ShouldReturnNull() { var (compilation, syntaxTree) = await CompileSourceCode($"{LoggerName}.LogInformation(null);"); - var (invocationExpression, methodSymbol, semanticModel) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpression, methodSymbol, semanticModel) = FindMethodInvocation(compilation, syntaxTree); - var result = LogCallMessageExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); + var result = MessageParameterTextExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); await Assert.That(result).IsNull(); } @@ -71,9 +71,9 @@ public async Task Extract_WithBinaryExpressions_ShouldReturnConcatenatedString() """; var (compilation, syntaxTree) = await CompileSourceCode(sourceCode); - var (invocationExpression, methodSymbol, semanticModel) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpression, methodSymbol, semanticModel) = FindMethodInvocation(compilation, syntaxTree); - var result = LogCallMessageExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); + var result = MessageParameterTextExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); await Assert.That(result).IsEqualTo("Hello world! 42"); } @@ -90,9 +90,9 @@ public async Task Extract_WithBinaryNonConstantExpressions_ShouldReturnNull() """; var (compilation, syntaxTree) = await CompileSourceCode(sourceCode); - var (invocationExpression, methodSymbol, semanticModel) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpression, methodSymbol, semanticModel) = FindMethodInvocation(compilation, syntaxTree); - var result = LogCallMessageExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); + var result = MessageParameterTextExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); await Assert.That(result).IsNull(); } diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Filters/LogCallFilterTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Filters/LogMessageCallFilterTests.cs similarity index 88% rename from tests/AutoLoggerMessageGenerator.UnitTests/Filters/LogCallFilterTests.cs rename to tests/AutoLoggerMessageGenerator.UnitTests/Filters/LogMessageCallFilterTests.cs index a790ccc..a1b5bec 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Filters/LogCallFilterTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Filters/LogMessageCallFilterTests.cs @@ -5,7 +5,7 @@ namespace AutoLoggerMessageGenerator.UnitTests.Filters; -internal class LogCallFilterTests : BaseSourceGeneratorTest +internal class LogMessageCallFilterTests : BaseSourceGeneratorTest { [Test] public async Task Filter_WithDifferentMethodInvocations_ShouldFilterOnlyLogMethodsFromLoggerInstance() @@ -38,14 +38,14 @@ public void AnotherMethod(string message, DateTime arg1) {} var invocationExpressions = syntaxTree.GetRoot() .DescendantNodes() - .Where(c => LogCallFilter.IsLogCallInvocation(c, CancellationToken.None)) + .Where(c => LogMessageCallFilter.IsLogCallInvocation(c, CancellationToken.None)) .ToArray(); var semanticModel = compilation.GetSemanticModel(syntaxTree); var filteredInvocationExpressions = invocationExpressions.OfType().Where(c => { var methodSymbol = (IMethodSymbol) semanticModel.GetSymbolInfo(c).Symbol!; - return LogCallFilter.IsLoggerMethod(methodSymbol); + return LogMessageCallFilter.IsLoggerMethod(methodSymbol); }).ToArray(); await Assert.That(invocationExpressions.Length).IsEqualTo(2); @@ -74,10 +74,10 @@ public class D {} {{LoggerName}}.LogInformation("B class is private, so {this} argument is inaccessible as well", parameter); """; var (compilation, syntaxTree) = await CompileSourceCode(sourceCode, additionalDeclarations); - var (invocationExpressionSyntax, methodSymbol, _) = FindLoggerMethodInvocation(compilation, syntaxTree); + var (invocationExpressionSyntax, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); - var isLogCallInvocation = LogCallFilter.IsLogCallInvocation(invocationExpressionSyntax, CancellationToken.None); - var isLogCallMethod = LogCallFilter.IsLoggerMethod(methodSymbol!); + var isLogCallInvocation = LogMessageCallFilter.IsLogCallInvocation(invocationExpressionSyntax, CancellationToken.None); + var isLogCallMethod = LogMessageCallFilter.IsLoggerMethod(methodSymbol!); await Assert.That(isLogCallInvocation).IsTrue(); await Assert.That(isLogCallMethod).IsFalse(); diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Filters/LoggerScopeFilterTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Filters/LoggerScopeFilterTests.cs new file mode 100644 index 0000000..00b14fa --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Filters/LoggerScopeFilterTests.cs @@ -0,0 +1,94 @@ +using AutoLoggerMessageGenerator.Filters; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace AutoLoggerMessageGenerator.UnitTests.Filters; + +internal class LoggerScopeFilterTests : BaseSourceGeneratorTest +{ + [Test] + public async Task Filter_WithDifferentMethodInvocations_ShouldFilterOnlyBeginScopeCallsFromLoggerInstance() + { + var additionalDeclarations = """ + public class AnotherObject + { + public IDisposable? BeginScope(string message, Guid arg1) {} + public IDisposable? BeginScope2(string message, Guid arg1) {} + } + + public IDisposable? BeginScope(string message, Guid arg1) {} + """; + var sourceCode = $$""" + const string message = "CorrelationId: {CorrelationId}"; + var correlationId = Guid.NewGuid(); + + {{LoggerName}}.BeginScope(message, correlationId); + + AnotherMethod(message, correlationId); + BeginScope(message, correlationId); + new AnotherObject().BeginScope(message, correlationId); + new AnotherObject().BeginScope2(message, correlationId); + """; + var (compilation, syntaxTree) = await CompileSourceCode(sourceCode, additionalDeclarations); + + var invocationExpressions = syntaxTree.GetRoot() + .DescendantNodes() + .Where(c => LoggerScopeFilter.IsLoggerScopeInvocation(c, CancellationToken.None)) + .ToArray(); + + var semanticModel = compilation.GetSemanticModel(syntaxTree); + var filteredInvocationExpressions = invocationExpressions.OfType().Where(c => + { + var methodSymbol = (IMethodSymbol) semanticModel.GetSymbolInfo(c).Symbol!; + return LoggerScopeFilter.IsLoggerScopeMethod(methodSymbol); + }).ToArray(); + + await Assert.That(invocationExpressions.Length).IsEqualTo(2); + await Assert.That(filteredInvocationExpressions.Length).IsEqualTo(1); + } + + [Test] + public async Task Filter_WithInaccessibleClassAsParameter_ShouldExcludeBeginScopeCall() + { + const string additionalDeclarations = """ + public class A + { + private class B + { + public class C + { + public class D {} + } + } + } + """; + + const string sourceCode = $$""" + var parameter = new A.B.C.D(); + {{LoggerName}}.BeginScope("B class is private, so {this} argument is inaccessible as well", parameter); + """; + var (compilation, syntaxTree) = await CompileSourceCode(sourceCode, additionalDeclarations); + var (invocationExpressionSyntax, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); + + var isLoggerScopeInvocation = LoggerScopeFilter.IsLoggerScopeInvocation(invocationExpressionSyntax, CancellationToken.None); + var isLoggerScopeCall = LoggerScopeFilter.IsLoggerScopeMethod(methodSymbol!); + + await Assert.That(isLoggerScopeInvocation).IsTrue(); + await Assert.That(isLoggerScopeCall).IsFalse(); + } + + [Test] + public async Task Filter_WithOnlyMessageParameterProvided_ShouldExcludeBeginScopeCall() + { + const string sourceCode = $"{LoggerName}.BeginScope(\"Some message\");"; + var (compilation, syntaxTree) = await CompileSourceCode(sourceCode); + var (invocationExpressionSyntax, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); + + var isLoggerScopeInvocation = LoggerScopeFilter.IsLoggerScopeInvocation(invocationExpressionSyntax, CancellationToken.None); + var isLoggerScopeCall = LoggerScopeFilter.IsLoggerScopeMethod(methodSymbol!); + + await Assert.That(isLoggerScopeInvocation).IsFalse(); + await Assert.That(isLoggerScopeCall).IsFalse(); + } +} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/MethodSpecificityRules/InstanceCallVsExtensionCallTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/MethodSpecificityRules/InstanceCallVsExtensionCallTests.cs new file mode 100644 index 0000000..cd87c8c --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/MethodSpecificityRules/InstanceCallVsExtensionCallTests.cs @@ -0,0 +1,29 @@ +namespace AutoLoggerMessageGenerator.UnitTests; + +internal class InstanceCallVsExtensionCallTests +{ + [Test] + public async Task BeginScope_WithOnlyMessageParameter_InstanceCallShouldBePrioritized() + { + ILogger logger = new Logger(); + var result = logger.BeginScope("Some scope"); + + await Assert.That(result).IsEqualTo(CallSource.Instance); + } +} + +interface ILogger +{ + CallSource BeginScope(T _); +} +class Logger : ILogger +{ + public CallSource BeginScope(T _) => CallSource.Instance; +} + +static class LoggerExtensions +{ + public static CallSource BeginScope(this ILogger _, string __) => CallSource.Extension; +} + +enum CallSource { Instance, Extension } diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Utilities/IdentifierHelperTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Utilities/IdentifierHelperTests.cs index 8e8b20f..19bcc2a 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Utilities/IdentifierHelperTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Utilities/IdentifierHelperTests.cs @@ -1,4 +1,5 @@ using AutoLoggerMessageGenerator.Utilities; +using TUnit.Assertions.AssertConditions.Throws; namespace AutoLoggerMessageGenerator.UnitTests.Utilities; diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/MockLogCallLocationBuilder.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Utilities/MockLogCallLocationBuilder.cs similarity index 73% rename from tests/AutoLoggerMessageGenerator.UnitTests/MockLogCallLocationBuilder.cs rename to tests/AutoLoggerMessageGenerator.UnitTests/Utilities/MockLogCallLocationBuilder.cs index e678029..44ffc9a 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/MockLogCallLocationBuilder.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Utilities/MockLogCallLocationBuilder.cs @@ -6,7 +6,7 @@ namespace AutoLoggerMessageGenerator.UnitTests; internal static class MockLogCallLocationBuilder { - internal static LogCallLocation Build(string filePath, int line, int character) + internal static CallLocation Build(string filePath, int line, int character) { ArgumentException.ThrowIfNullOrEmpty(filePath); @@ -15,6 +15,6 @@ internal static LogCallLocation Build(string filePath, int line, int character) var data = Convert.ToBase64String(Encoding.UTF8.GetBytes(location)); var interceptableLocationSyntax = $"""[FakeInterceptableLocation({version}, "{data}")]"""; - return new LogCallLocation(filePath, line, character, interceptableLocationSyntax, Location.None); + return new CallLocation(filePath, line, character, interceptableLocationSyntax, Location.None); } } diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/LogCallExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/LogCallExtractorTests.cs index c94978b..ecfcb1f 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/LogCallExtractorTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/LogCallExtractorTests.cs @@ -4,25 +4,25 @@ namespace AutoLoggerMessageGenerator.UnitTests.VirtualLoggerMessage; -internal class LogCallLocationMapTests : BaseSourceGeneratorTest +internal class LogMessageCallLocationMapTests : BaseSourceGeneratorTest { [Test] public async Task MapBack_GivenMethodName_ShouldReturnLastLocationBeforeTheCurrentNode() { - var logCall1 = new LogCall { Id = Guid.NewGuid() }; - var logCall2 = new LogCall { Id = Guid.NewGuid() }; - var logCall3 = new LogCall { Id = Guid.NewGuid() }; + var logCall1 = new LogMessageCall { Id = Guid.NewGuid() }; + var logCall2 = new LogMessageCall { Id = Guid.NewGuid() }; + var logCall3 = new LogMessageCall { Id = Guid.NewGuid() }; var additionalDeclarations = $$""" public void Method0(){} - {{LogCallLocationMap.CreateMapping(logCall1)}} + {{LogMessageCallLocationMap.CreateMapping(logCall1)}} public void Method1(){} - {{LogCallLocationMap.CreateMapping(logCall2)}} + {{LogMessageCallLocationMap.CreateMapping(logCall2)}} public void Method2(){} - {{LogCallLocationMap.CreateMapping(logCall3)}} + {{LogMessageCallLocationMap.CreateMapping(logCall3)}} public void Method3(){} """; var (_, syntaxTree) = await CompileSourceCode(string.Empty, additionalDeclarations); @@ -36,18 +36,18 @@ public void Method3(){} var method2Declaration = methodDeclarations.Single(m => m.Identifier.Text == "Method2"); var method3Declaration = methodDeclarations.Single(m => m.Identifier.Text == "Method3"); - await Assert.That(LogCallLocationMap.TryMapBack(syntaxTree.GetText(), method0Declaration.GetLocation(), out var logCallId0)) + await Assert.That(LogMessageCallLocationMap.TryMapBack(syntaxTree.GetText(), method0Declaration.GetLocation(), out var logCallId0)) .IsFalse(); - await Assert.That(LogCallLocationMap.TryMapBack(syntaxTree.GetText(), method1Declaration.GetLocation(), out var logCallId1)) + await Assert.That(LogMessageCallLocationMap.TryMapBack(syntaxTree.GetText(), method1Declaration.GetLocation(), out var logCallId1)) .IsTrue(); await Assert.That(logCallId1).IsEqualTo(logCall1.Id); - await Assert.That(LogCallLocationMap.TryMapBack(syntaxTree.GetText(), method2Declaration.GetLocation(), out var logCallId2)) + await Assert.That(LogMessageCallLocationMap.TryMapBack(syntaxTree.GetText(), method2Declaration.GetLocation(), out var logCallId2)) .IsTrue(); await Assert.That(logCallId2).IsEqualTo(logCall2.Id); - await Assert.That(LogCallLocationMap.TryMapBack(syntaxTree.GetText(), method3Declaration.GetLocation(), out var logCallId3)) + await Assert.That(LogMessageCallLocationMap.TryMapBack(syntaxTree.GetText(), method3Declaration.GetLocation(), out var logCallId3)) .IsTrue(); await Assert.That(logCallId3).IsEqualTo(logCall3.Id); } diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_310af49b1b682fdf.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_310af49b1b682fdf.verified.txt new file mode 100644 index 0000000..9d3f72e --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_310af49b1b682fdf.verified.txt @@ -0,0 +1,12 @@ +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + static partial class AutoLoggerMessage + { + // : Guid_1 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = true)] + internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); + // : Guid_2 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = true)] + internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); + } +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_all_disabled.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_all_disabled.verified.txt new file mode 100644 index 0000000..99df045 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_all_disabled.verified.txt @@ -0,0 +1,12 @@ +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + static partial class AutoLoggerMessage + { + // : Guid_1 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = false)] + internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); + // : Guid_2 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = false)] + internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); + } +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_all_enabled.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_all_enabled.verified.txt new file mode 100644 index 0000000..6ebd768 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_all_enabled.verified.txt @@ -0,0 +1,12 @@ +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + static partial class AutoLoggerMessage + { + // : Guid_1 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = true)] + internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeStruct @structParam); + // : Guid_2 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = true)] + internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeStruct @structParam); + } +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_b6a745e4a78dd4ac.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_b6a745e4a78dd4ac.verified.txt new file mode 100644 index 0000000..99df045 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_b6a745e4a78dd4ac.verified.txt @@ -0,0 +1,12 @@ +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + static partial class AutoLoggerMessage + { + // : Guid_1 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = false)] + internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); + // : Guid_2 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = false)] + internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); + } +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_e7fa423334e4b1a0.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_e7fa423334e4b1a0.verified.txt new file mode 100644 index 0000000..6ebd768 --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_e7fa423334e4b1a0.verified.txt @@ -0,0 +1,12 @@ +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + static partial class AutoLoggerMessage + { + // : Guid_1 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = true)] + internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeStruct @structParam); + // : Guid_2 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = true)] + internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeStruct @structParam); + } +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_ed18b4fdbf0bda16.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_ed18b4fdbf0bda16.verified.txt new file mode 100644 index 0000000..782cfdd --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_ed18b4fdbf0bda16.verified.txt @@ -0,0 +1,12 @@ +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + static partial class AutoLoggerMessage + { + // : Guid_1 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = false)] + internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeStruct @structParam); + // : Guid_2 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = false)] + internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeStruct @structParam); + } +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_only_telemetry_enabled.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_only_telemetry_enabled.verified.txt new file mode 100644 index 0000000..782cfdd --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_only_telemetry_enabled.verified.txt @@ -0,0 +1,12 @@ +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + static partial class AutoLoggerMessage + { + // : Guid_1 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = false)] + internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeStruct @structParam); + // : Guid_2 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = false)] + internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeStruct @structParam); + } +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_telemetry_disabled.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_telemetry_disabled.verified.txt new file mode 100644 index 0000000..9d3f72e --- /dev/null +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_telemetry_disabled.verified.txt @@ -0,0 +1,12 @@ +namespace Microsoft.Extensions.Logging.AutoLoggerMessage +{ + static partial class AutoLoggerMessage + { + // : Guid_1 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = true)] + internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); + // : Guid_2 + [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = true)] + internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); + } +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithEscapeSequences_ShouldBuildAsItIs.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithEscapeSequences_ShouldBuildAsItIs.verified.txt index 28c25bb..8a13548 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithEscapeSequences_ShouldBuildAsItIs.verified.txt +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithEscapeSequences_ShouldBuildAsItIs.verified.txt @@ -2,8 +2,8 @@ { static partial class AutoLoggerMessage { - // : Guid_1 + // : Guid_1 [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "All characters should be passed as a string literal expression: \n\r\t", SkipEnabledCheck = false)] internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); } -} +} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.cs index aef3c4c..f8f16c3 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.cs @@ -7,39 +7,40 @@ namespace AutoLoggerMessageGenerator.UnitTests.VirtualLoggerMessage; internal class VirtualLoggerMessageClassBuilderTests { - private readonly LogCall _logCall = new() + private readonly LogMessageCall _logMessageCall = new() { Id = Guid.NewGuid(), - Location = new LogCallLocation + Location = new CallLocation { FilePath = "./some/path/to/file.cs", Line = 2, Character = 22 }, Message = "Hello, World!", - Name = "LogCall", + MethodName = "LogCall", Namespace = "SomeNamespace", ClassName = "SomeClass", LogLevel = "Critical", - Parameters = [ - new LogCallParameter("int", "@intParam", LogCallParameterType.Others), - new LogCallParameter("string", "@stringParam", LogCallParameterType.Others), - new LogCallParameter("bool", "@boolParam", LogCallParameterType.Others), - new LogCallParameter("SomeClass", "@classParam", LogCallParameterType.Others, true), - new LogCallParameter("SomeStruct", "@structParam", LogCallParameterType.Others, true) + Parameters = + [ + new CallParameter("int", "@intParam", CallParameterType.Others), + new CallParameter("string", "@stringParam", CallParameterType.Others), + new CallParameter("bool", "@boolParam", CallParameterType.Others), + new CallParameter("SomeClass", "@classParam", CallParameterType.Others, true), + new CallParameter("SomeStruct", "@structParam", CallParameterType.Others, true) ] }; [Test] [MethodDataSource(nameof(TestConfigurations))] public async Task Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration( - SourceGeneratorConfiguration configuration, bool useTelemetryExtensions) + string description, SourceGeneratorConfiguration configuration, bool useTelemetryExtensions) { var sut = new VirtualLoggerMessageClassBuilder(configuration, useTelemetryExtensions); var result = sut.Build([ - _logCall, - _logCall with + _logMessageCall, + _logMessageCall with { Id = Guid.NewGuid(), Location = MockLogCallLocationBuilder.Build("path/to/another/file.cs", 3, 33), @@ -51,8 +52,7 @@ _logCall with var syntaxTree = (await result.SyntaxTree.GetRootAsync()).NormalizeWhitespace().ToFullString(); await Verify(syntaxTree) - .UseParameters(configuration, useTelemetryExtensions) - .HashParameters() + .UseTextForParameters(description) .ScrubInlineGuids(); } @@ -62,7 +62,7 @@ public async Task Build_WithEscapeSequences_ShouldBuildAsItIs() var sut = new VirtualLoggerMessageClassBuilder(default); var result = sut.Build([ - _logCall with + _logMessageCall with { Id = Guid.NewGuid(), Location = MockLogCallLocationBuilder.Build("path/to/another/file.cs", 3, 33), @@ -76,11 +76,47 @@ _logCall with await Verify(syntaxTree).ScrubInlineGuids(); } - public static IEnumerable> TestConfigurations() => + public static IEnumerable> TestConfigurations() => [ - () => (new SourceGeneratorConfiguration(default, true, true, true, true), true), - () => (new SourceGeneratorConfiguration(default, true, true, true, true), false), - () => (new SourceGeneratorConfiguration(default, false, false, false, false), true), - () => (new SourceGeneratorConfiguration(default, false, false, false, false), false), + () => ( + "all_enabled", + new SourceGeneratorConfiguration( + GenerateInterceptorAttribute: false, + GenerateSkipEnabledCheck: true, + GenerateOmitReferenceName: true, + GenerateSkipNullProperties: true, + GenerateTransitive: true, + OverrideBeginScopeBehavior: false + ), true), + () => ( + "telemetry_disabled", + new SourceGeneratorConfiguration( + GenerateInterceptorAttribute: false, + GenerateSkipEnabledCheck: true, + GenerateOmitReferenceName: true, + GenerateSkipNullProperties: true, + GenerateTransitive: true, + OverrideBeginScopeBehavior: false), + false), + () => ( + "only_telemetry_enabled", + new SourceGeneratorConfiguration( + GenerateInterceptorAttribute: false, + GenerateSkipEnabledCheck: false, + GenerateOmitReferenceName: false, + GenerateSkipNullProperties: false, + GenerateTransitive: false, + OverrideBeginScopeBehavior: false), + true), + () => ( + "all_disabled", + new SourceGeneratorConfiguration( + GenerateInterceptorAttribute: false, + GenerateSkipEnabledCheck: false, + GenerateOmitReferenceName: false, + GenerateSkipNullProperties: false, + GenerateTransitive: false, + OverrideBeginScopeBehavior: false), + false), ]; } From c9c9d9e84b208897c31db3ee0260f3610d67e313 Mon Sep 17 00:00:00 2001 From: stbychkov Date: Sun, 4 May 2025 18:07:02 +0800 Subject: [PATCH 2/9] Excluded BeginScope without template parameters from BuildOutput --- .../GenericLoggerScopeExtensions.g.cs | 5 ----- .../Emitters/GenericLoggerScopeExtensionsEmitter.cs | 2 +- .../Extractors/MessageParameterTextExtractor.cs | 5 ++++- ...GenerateValidLoggingScopeExtensionsOverrides.verified.txt | 5 ----- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerScopeExtensions.g.cs b/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerScopeExtensions.g.cs index fc52c4e..6010a02 100644 --- a/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerScopeExtensions.g.cs +++ b/src/AutoLoggerMessageGenerator.BuildOutput/GenericLoggerScopeExtensions.g.cs @@ -11,11 +11,6 @@ namespace Microsoft.Extensions.Logging [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public static class GenericLoggerScopeExtensions { - public static IDisposable? BeginScope(this ILogger @logger, string @message) - { - return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message); - } - public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0) { return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0 }); diff --git a/src/AutoLoggerMessageGenerator/Emitters/GenericLoggerScopeExtensionsEmitter.cs b/src/AutoLoggerMessageGenerator/Emitters/GenericLoggerScopeExtensionsEmitter.cs index e078f29..554fa60 100644 --- a/src/AutoLoggerMessageGenerator/Emitters/GenericLoggerScopeExtensionsEmitter.cs +++ b/src/AutoLoggerMessageGenerator/Emitters/GenericLoggerScopeExtensionsEmitter.cs @@ -31,7 +31,7 @@ public static string Emit() sb.WriteLine('{'); sb.Indent++; - for (int i = 0; i <= MaxLogParameters; i++) + for (int i = 1; i <= MaxLogParameters; i++) { var parameters = Enumerable.Range(0, i).ToArray(); diff --git a/src/AutoLoggerMessageGenerator/Extractors/MessageParameterTextExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/MessageParameterTextExtractor.cs index 36f2d57..6d5187e 100644 --- a/src/AutoLoggerMessageGenerator/Extractors/MessageParameterTextExtractor.cs +++ b/src/AutoLoggerMessageGenerator/Extractors/MessageParameterTextExtractor.cs @@ -9,7 +9,10 @@ internal static class MessageParameterTextExtractor public static string? Extract(IMethodSymbol methodSymbol, InvocationExpressionSyntax invocationExpressionSyntax, SemanticModel semanticModel) { - var messageParameter = methodSymbol.Parameters.Single(c => c.Name == Constants.MessageParameterName.TrimStart('@')); + var messageParameter = methodSymbol.Parameters.FirstOrDefault(c => c.Name == Constants.MessageParameterName.TrimStart('@')); + if (messageParameter is null) + return null; + var messageParameterIx = methodSymbol.Parameters.IndexOf(messageParameter); var valueExpression = invocationExpressionSyntax.ArgumentList.Arguments[messageParameterIx].Expression; diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerScopeExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingScopeExtensionsOverrides.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerScopeExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingScopeExtensionsOverrides.verified.txt index 713899f..7a7c2e8 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerScopeExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingScopeExtensionsOverrides.verified.txt +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/GenericLoggerScopeExtensionsEmitterTests.Emit_ShouldGenerateValidLoggingScopeExtensionsOverrides.verified.txt @@ -11,11 +11,6 @@ namespace Microsoft.Extensions.Logging [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public static class GenericLoggerScopeExtensions { - public static IDisposable? BeginScope(this ILogger @logger, string @message) - { - return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message); - } - public static IDisposable? BeginScope(this ILogger @logger, string @message, T0 @arg0) { return Microsoft.Extensions.Logging.LoggerExtensions.BeginScope(@logger, @message, new object?[] { @arg0 }); From ffe3a4979a94a864bce29c0fc73d3a65ad5f8c34 Mon Sep 17 00:00:00 2001 From: stbychkov Date: Sun, 4 May 2025 18:40:46 +0800 Subject: [PATCH 3/9] Adjusted configuration key name --- .../Configuration/GeneratorOptionsProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AutoLoggerMessageGenerator/Configuration/GeneratorOptionsProvider.cs b/src/AutoLoggerMessageGenerator/Configuration/GeneratorOptionsProvider.cs index 0d74cc7..d0b64e7 100644 --- a/src/AutoLoggerMessageGenerator/Configuration/GeneratorOptionsProvider.cs +++ b/src/AutoLoggerMessageGenerator/Configuration/GeneratorOptionsProvider.cs @@ -11,7 +11,7 @@ internal static class GeneratorOptionsProvider private const string GenerateOmitReferenceNameKey = $"{CommonPrefix}_{nameof(SourceGeneratorConfiguration.GenerateOmitReferenceName)}"; private const string GenerateSkipNullPropertiesKey = $"{CommonPrefix}_{nameof(SourceGeneratorConfiguration.GenerateSkipNullProperties)}"; private const string GenerateTransitiveKey = $"{CommonPrefix}_{nameof(SourceGeneratorConfiguration.GenerateTransitive)}"; - private const string OverrideBeginScopeBehavior = $"{CommonPrefix}_{nameof(SourceGeneratorConfiguration.OverrideBeginScopeBehavior)}"; + private const string OverrideBeginScopeBehaviorKey = $"{CommonPrefix}_{nameof(SourceGeneratorConfiguration.OverrideBeginScopeBehavior)}"; public static IncrementalValueProvider Provide(IncrementalGeneratorInitializationContext context) => context.AnalyzerConfigOptionsProvider.Select((options, _) => new SourceGeneratorConfiguration( @@ -20,7 +20,7 @@ public static IncrementalValueProvider Provide(Inc GenerateOmitReferenceName: GetValue(options.GlobalOptions, GenerateOmitReferenceNameKey, false), GenerateSkipNullProperties: GetValue(options.GlobalOptions, GenerateSkipNullPropertiesKey, false), GenerateTransitive: GetValue(options.GlobalOptions, GenerateTransitiveKey, false), - OverrideBeginScopeBehavior: GetValue(options.GlobalOptions, OverrideBeginScopeBehavior, true) + OverrideBeginScopeBehavior: GetValue(options.GlobalOptions, OverrideBeginScopeBehaviorKey, true) )); private static bool GetValue(AnalyzerConfigOptions options, string key, bool defaultValue = true) => From 254a478e51a76eeb7bb1281cd9b59e3ae8528242 Mon Sep 17 00:00:00 2001 From: stbychkov Date: Sun, 4 May 2025 18:42:16 +0800 Subject: [PATCH 4/9] Removed EditorBrowsableAttribute --- .../Emitters/LoggerInterceptorsEmitter.cs | 1 - .../Emitters/LoggerScopeInterceptorsEmitter.cs | 3 +-- ...houldGenerateValidLoggingExtensionsAttribute.verified.txt | 3 --- ...n_ShouldGenerateValidLoggerScopeInterceptors.verified.txt | 5 +---- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/AutoLoggerMessageGenerator/Emitters/LoggerInterceptorsEmitter.cs b/src/AutoLoggerMessageGenerator/Emitters/LoggerInterceptorsEmitter.cs index acf2b24..fe25f55 100644 --- a/src/AutoLoggerMessageGenerator/Emitters/LoggerInterceptorsEmitter.cs +++ b/src/AutoLoggerMessageGenerator/Emitters/LoggerInterceptorsEmitter.cs @@ -25,7 +25,6 @@ public static string Emit(IEnumerable logCalls) foreach (var logCall in logCalls) { - sb.WriteLine(Constants.EditorNotBrowsableAttribute); sb.WriteLine(logCall.Location.InterceptableLocationSyntax); var parameters = string.Join(", ", logCall.Parameters.Select((c, i) => $"{c.NativeType} {c.Name}")); diff --git a/src/AutoLoggerMessageGenerator/Emitters/LoggerScopeInterceptorsEmitter.cs b/src/AutoLoggerMessageGenerator/Emitters/LoggerScopeInterceptorsEmitter.cs index 5692afb..6ec7361 100644 --- a/src/AutoLoggerMessageGenerator/Emitters/LoggerScopeInterceptorsEmitter.cs +++ b/src/AutoLoggerMessageGenerator/Emitters/LoggerScopeInterceptorsEmitter.cs @@ -19,13 +19,12 @@ public static string Emit(IEnumerable loggerScopes) sb.WriteLine(Constants.EditorNotBrowsableAttribute); sb.WriteLine(Constants.ExcludeFromCoverageAttribute); sb.WriteLine(Constants.DebuggerStepThroughAttribute); - sb.WriteLine("internal static class BeginScopeInterceptors"); + sb.WriteLine("internal static class LoggerScopeInterceptors"); sb.WriteLine('{'); sb.Indent++; foreach (var loggerScope in loggerScopes) { - sb.WriteLine(Constants.EditorNotBrowsableAttribute); sb.WriteLine(loggerScope.Location.InterceptableLocationSyntax); var parameters = string.Join(", ", loggerScope.Parameters.Select((c, i) => $"{c.NativeType} {c.Name}")); diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerInterceptorsEmitterTests.Emit_ShouldGenerateValidLoggingExtensionsAttribute.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerInterceptorsEmitterTests.Emit_ShouldGenerateValidLoggingExtensionsAttribute.verified.txt index 451234b..f0f3065 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerInterceptorsEmitterTests.Emit_ShouldGenerateValidLoggingExtensionsAttribute.verified.txt +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerInterceptorsEmitterTests.Emit_ShouldGenerateValidLoggingExtensionsAttribute.verified.txt @@ -11,21 +11,18 @@ namespace Microsoft.Extensions.Logging.AutoLoggerMessage [System.Diagnostics.DebuggerStepThrough] internal static class LoggerInterceptors { - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] [FakeInterceptableLocation(-1, "ZmlsZSgxLDExKQ==")] public static void Log_namespace1class1_1_11(this ILogger @logger, string @message) { Microsoft.Extensions.Logging.AutoLoggerMessage.AutoLoggerMessage.Log_namespace1class1_1_11(@logger); } - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] [FakeInterceptableLocation(-1, "ZmlsZTIoMiwyMik=")] public static void Log_namespace2class2_2_22(this ILogger @logger, string @message, int @intParam) { Microsoft.Extensions.Logging.AutoLoggerMessage.AutoLoggerMessage.Log_namespace2class2_2_22(@logger, @intParam); } - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] [FakeInterceptableLocation(-1, "ZmlsZTMoMywzMyk=")] public static void Log_namespace3class3_3_33(this ILogger @logger, string @message, int @intParam, bool @boolParam, SomeClass @objectParam) { diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerScopeInterceptors.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerScopeInterceptors.verified.txt index 829f4e8..405a2af 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerScopeInterceptors.verified.txt +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Emitters/LoggerScopeInterceptorsEmitterTests.Emit_WithGivenConfiguration_ShouldGenerateValidLoggerScopeInterceptors.verified.txt @@ -9,23 +9,20 @@ namespace Microsoft.Extensions.Logging.AutoLoggerMessage [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.DebuggerStepThrough] - internal static class BeginScopeInterceptors + internal static class LoggerScopeInterceptors { - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] [FakeInterceptableLocation(-1, "ZmlsZSgxLDExKQ==")] public static IDisposable? LogScope_namespace1class1_1_11(this ILogger @logger, string @message) { return Microsoft.Extensions.Logging.AutoLoggerMessage.LoggerScopes.LogScope_namespace1class1_1_11(@logger); } - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] [FakeInterceptableLocation(-1, "ZmlsZTIoMiwyMik=")] public static IDisposable? LogScope_namespace2class2_2_22(this ILogger @logger, string @message, int @intParam) { return Microsoft.Extensions.Logging.AutoLoggerMessage.LoggerScopes.LogScope_namespace2class2_2_22(@logger, @intParam); } - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] [FakeInterceptableLocation(-1, "ZmlsZTMoMywzMyk=")] public static IDisposable? LogScope_namespace3class3_3_33(this ILogger @logger, string @message, int @intParam, bool @boolParam, SomeClass @objectParam) { From f96c25b5482f79f0636bdbec095de00a536e1fd3 Mon Sep 17 00:00:00 2001 From: stbychkov Date: Sun, 4 May 2025 18:46:27 +0800 Subject: [PATCH 5/9] Cleaned up snapshots and adjusted class names --- ...metersExtractor.cs => CallParametersExtractor.cs} | 2 +- .../Extractors/LogCallExtractor.cs | 2 +- .../Extractors/LoggerScopeCallExtractor.cs | 2 +- ...erTests.cs => LogCallInputSourceComparerTests.cs} | 2 +- ...actorTests.cs => CallParametersExtractorTests.cs} | 12 ++++++------ ...ers_sourceCode=HashBasedInterceptor.verified.txt} | 4 +--- ...ers_sourceCode=PathBasedInterceptor.verified.txt} | 4 +--- .../Extractors/LoggerScopeCallExtractorTests.cs | 4 ++-- ...rMessageDeclaration_310af49b1b682fdf.verified.txt | 12 ------------ ...rMessageDeclaration_72a4a8950e4d9a2c.verified.txt | 12 ------------ ...rMessageDeclaration_7dc9492fbd4b4118.verified.txt | 12 ------------ ...rMessageDeclaration_8aa982b1f9f3bd17.verified.txt | 12 ------------ ...rMessageDeclaration_90f6f16c422d1dd6.verified.txt | 12 ------------ ...rMessageDeclaration_b6a745e4a78dd4ac.verified.txt | 12 ------------ ...rMessageDeclaration_e7fa423334e4b1a0.verified.txt | 12 ------------ ...rMessageDeclaration_ed18b4fdbf0bda16.verified.txt | 12 ------------ 16 files changed, 14 insertions(+), 114 deletions(-) rename src/AutoLoggerMessageGenerator/Extractors/{LogCallParametersExtractor.cs => CallParametersExtractor.cs} (97%) rename tests/AutoLoggerMessageGenerator.UnitTests/Caching/{InputSourceComparerTests.cs => LogCallInputSourceComparerTests.cs} (98%) rename tests/AutoLoggerMessageGenerator.UnitTests/Extractors/{LogCallParametersExtractorTests.cs => CallParametersExtractorTests.cs} (95%) rename tests/AutoLoggerMessageGenerator.UnitTests/Extractors/{LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt => LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoLoggerScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt} (93%) rename tests/AutoLoggerMessageGenerator.UnitTests/Extractors/{LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt => LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoLoggerScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt} (93%) delete mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_310af49b1b682fdf.verified.txt delete mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_72a4a8950e4d9a2c.verified.txt delete mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_7dc9492fbd4b4118.verified.txt delete mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_8aa982b1f9f3bd17.verified.txt delete mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_90f6f16c422d1dd6.verified.txt delete mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_b6a745e4a78dd4ac.verified.txt delete mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_e7fa423334e4b1a0.verified.txt delete mode 100644 tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_ed18b4fdbf0bda16.verified.txt diff --git a/src/AutoLoggerMessageGenerator/Extractors/LogCallParametersExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/CallParametersExtractor.cs similarity index 97% rename from src/AutoLoggerMessageGenerator/Extractors/LogCallParametersExtractor.cs rename to src/AutoLoggerMessageGenerator/Extractors/CallParametersExtractor.cs index fc3cad8..388ca00 100644 --- a/src/AutoLoggerMessageGenerator/Extractors/LogCallParametersExtractor.cs +++ b/src/AutoLoggerMessageGenerator/Extractors/CallParametersExtractor.cs @@ -8,7 +8,7 @@ namespace AutoLoggerMessageGenerator.Extractors; -internal class LogCallParametersExtractor(LogPropertiesCheck? logPropertiesCheck = null) +internal class CallParametersExtractor(LogPropertiesCheck? logPropertiesCheck = null) { public ImmutableArray? Extract(string message, IMethodSymbol methodSymbol) { diff --git a/src/AutoLoggerMessageGenerator/Extractors/LogCallExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/LogCallExtractor.cs index 8aa9da6..dcc631a 100644 --- a/src/AutoLoggerMessageGenerator/Extractors/LogCallExtractor.cs +++ b/src/AutoLoggerMessageGenerator/Extractors/LogCallExtractor.cs @@ -27,7 +27,7 @@ internal static class LogCallExtractor return default; var logPropertiesCheck = new LogPropertiesCheck(semanticModel.Compilation); - var parameters = new LogCallParametersExtractor(logPropertiesCheck) + var parameters = new CallParametersExtractor(logPropertiesCheck) .Extract(message, methodSymbol); if (parameters is null) diff --git a/src/AutoLoggerMessageGenerator/Extractors/LoggerScopeCallExtractor.cs b/src/AutoLoggerMessageGenerator/Extractors/LoggerScopeCallExtractor.cs index 704fb88..2ebcba4 100644 --- a/src/AutoLoggerMessageGenerator/Extractors/LoggerScopeCallExtractor.cs +++ b/src/AutoLoggerMessageGenerator/Extractors/LoggerScopeCallExtractor.cs @@ -23,7 +23,7 @@ internal static class LoggerScopeCallExtractor return default; var logPropertiesCheck = new LogPropertiesCheck(semanticModel.Compilation); - var parameters = new LogCallParametersExtractor(logPropertiesCheck) + var parameters = new CallParametersExtractor(logPropertiesCheck) .Extract(message, methodSymbol); if (parameters is null) diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Caching/InputSourceComparerTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Caching/LogCallInputSourceComparerTests.cs similarity index 98% rename from tests/AutoLoggerMessageGenerator.UnitTests/Caching/InputSourceComparerTests.cs rename to tests/AutoLoggerMessageGenerator.UnitTests/Caching/LogCallInputSourceComparerTests.cs index 5f8e9d8..07aa24a 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Caching/InputSourceComparerTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Caching/LogCallInputSourceComparerTests.cs @@ -19,7 +19,7 @@ ImmutableArray LogCalls ) Others ); -internal class InputSourceComparerTests +internal class LogCallInputSourceComparerTests { [Test] public async Task Equals_WithDifferentCompilation_ShouldReturnTrue() diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallParametersExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/CallParametersExtractorTests.cs similarity index 95% rename from tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallParametersExtractorTests.cs rename to tests/AutoLoggerMessageGenerator.UnitTests/Extractors/CallParametersExtractorTests.cs index 5bcfe27..9c64de1 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LogCallParametersExtractorTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/CallParametersExtractorTests.cs @@ -6,7 +6,7 @@ namespace AutoLoggerMessageGenerator.UnitTests.Extractors; -internal class LogCallParametersExtractorTests : BaseSourceGeneratorTest +internal class CallParametersExtractorTests : BaseSourceGeneratorTest { [Test] public async Task Extract_WithGivenMessageAndParameters_ShouldReturnMessageAndParsedParameters() @@ -26,7 +26,7 @@ private static void Log( var (compilation, syntaxTree) = await CompileSourceCode($"Log({parameters});", extensionDeclaration); var (_, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); - var sut = new LogCallParametersExtractor(); + var sut = new CallParametersExtractor(); var result = sut.Extract(message, methodSymbol!); @@ -48,7 +48,7 @@ public async Task Extract_WithGivenMessageAndNoParameters_ShouldReturnOnlyMessag var (compilation, syntaxTree) = await CompileSourceCode($"""Log("{message}");""", extensionDeclaration); var (_, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); - var sut = new LogCallParametersExtractor(); + var sut = new CallParametersExtractor(); var result = sut.Extract(message, methodSymbol!); @@ -90,7 +90,7 @@ private static void Log( """, extensionDeclaration); var (_, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); - var sut = new LogCallParametersExtractor(); + var sut = new CallParametersExtractor(); var result = sut.Extract(message, methodSymbol!); @@ -128,7 +128,7 @@ private static void Log( var semanticModel = compilation.GetSemanticModel(syntaxTree); var methodSymbol = (IMethodSymbol)semanticModel.GetSymbolInfo(invocationExpression).Symbol!; - var sut = new LogCallParametersExtractor(); + var sut = new CallParametersExtractor(); var result = sut.Extract(message, methodSymbol); @@ -159,7 +159,7 @@ public void Foo({{genericType}} arg) """); var (_, methodSymbol, _) = FindMethodInvocation(compilation, syntaxTree); - var sut = new LogCallParametersExtractor(); + var sut = new CallParametersExtractor(); var result = sut.Extract(message, methodSymbol!); diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoLoggerScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt similarity index 93% rename from tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt rename to tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoLoggerScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt index e568e25..21a4e8a 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoLoggerScopeCallObject_description=with parameters_sourceCode=HashBasedInterceptor.verified.txt @@ -1,5 +1,4 @@ { - Id: Guid_1, Location: { FilePath: path/testFile.cs, Line: 12, @@ -29,7 +28,6 @@ Namespace: Foo, ClassName: Test, MethodName: BeginScope, - LogLevel: inScope, Message: Hello world {arg1} {arg2}, Parameters: [ { @@ -51,5 +49,5 @@ HasPropertiesToLog: false } ], - GeneratedMethodName: Log_FooTest_12_16 + GeneratedMethodName: LogScope_FooTest_12_16 } \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoLoggerScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt similarity index 93% rename from tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt rename to tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoLoggerScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt index f556cc5..90b47fa 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.Extract_WithGivenLoggerScope_ShouldTransformIntoLoggerScopeCallObject_description=with parameters_sourceCode=PathBasedInterceptor.verified.txt @@ -1,5 +1,4 @@ { - Id: Guid_1, Location: { FilePath: path/testFile.cs, Line: 12, @@ -29,7 +28,6 @@ Namespace: Foo, ClassName: Test, MethodName: BeginScope, - LogLevel: inScope, Message: Hello world {arg1} {arg2}, Parameters: [ { @@ -51,5 +49,5 @@ HasPropertiesToLog: false } ], - GeneratedMethodName: Log_FooTest_12_16 + GeneratedMethodName: LogScope_FooTest_12_16 } \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.cs b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.cs index 1abea54..3e5d25a 100644 --- a/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.cs +++ b/tests/AutoLoggerMessageGenerator.UnitTests/Extractors/LoggerScopeCallExtractorTests.cs @@ -9,12 +9,12 @@ internal class LoggerScopeCallExtractorTests : BaseSourceGeneratorTest [Arguments("with parameters", $$"""{{LoggerName}}.BeginScope("Hello world {arg1} {arg2}", 1, true);""", true)] [Arguments("with null passed", $"""{LoggerName}.BeginScope(null);""", false)] [Arguments("with parameter mismatch", $$"""{{LoggerName}}.BeginScope("Hello world {arg1}", 1, true);""", false)] - public async Task Extract_WithGivenLoggerScope_ShouldTransformIntoBeginScopeCallObject(string description, string sourceCode, bool isValidCall) + public async Task Extract_WithGivenLoggerScope_ShouldTransformIntoLoggerScopeCallObject(string description, string sourceCode, bool isValidCall) { var (compilation, syntaxTree) = await CompileSourceCode(sourceCode); var (invocationExpression, methodSymbol, semanticModel) = FindMethodInvocation(compilation, syntaxTree); - var loggerScope = LogCallExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); + var loggerScope = LoggerScopeCallExtractor.Extract(methodSymbol!, invocationExpression, semanticModel!); if (isValidCall) { diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_310af49b1b682fdf.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_310af49b1b682fdf.verified.txt deleted file mode 100644 index 9d3f72e..0000000 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_310af49b1b682fdf.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Microsoft.Extensions.Logging.AutoLoggerMessage -{ - static partial class AutoLoggerMessage - { - // : Guid_1 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = true)] - internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); - // : Guid_2 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = true)] - internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); - } -} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_72a4a8950e4d9a2c.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_72a4a8950e4d9a2c.verified.txt deleted file mode 100644 index fc4fa50..0000000 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_72a4a8950e4d9a2c.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Microsoft.Extensions.Logging.AutoLoggerMessage -{ - static partial class AutoLoggerMessage - { - // : Guid_1 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = false)] - internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); - // : Guid_2 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = false)] - internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); - } -} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_7dc9492fbd4b4118.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_7dc9492fbd4b4118.verified.txt deleted file mode 100644 index dfa01ac..0000000 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_7dc9492fbd4b4118.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Microsoft.Extensions.Logging.AutoLoggerMessage -{ - static partial class AutoLoggerMessage - { - // : Guid_1 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = true)] - internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); - // : Guid_2 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = true)] - internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); - } -} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_8aa982b1f9f3bd17.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_8aa982b1f9f3bd17.verified.txt deleted file mode 100644 index 2a2657c..0000000 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_8aa982b1f9f3bd17.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Microsoft.Extensions.Logging.AutoLoggerMessage -{ - static partial class AutoLoggerMessage - { - // : Guid_1 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = false)] - internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeStruct @structParam); - // : Guid_2 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = false)] - internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeStruct @structParam); - } -} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_90f6f16c422d1dd6.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_90f6f16c422d1dd6.verified.txt deleted file mode 100644 index 47d8f99..0000000 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_90f6f16c422d1dd6.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Microsoft.Extensions.Logging.AutoLoggerMessage -{ - static partial class AutoLoggerMessage - { - // : Guid_1 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = true)] - internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeStruct @structParam); - // : Guid_2 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = true)] - internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeStruct @structParam); - } -} diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_b6a745e4a78dd4ac.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_b6a745e4a78dd4ac.verified.txt deleted file mode 100644 index 99df045..0000000 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_b6a745e4a78dd4ac.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Microsoft.Extensions.Logging.AutoLoggerMessage -{ - static partial class AutoLoggerMessage - { - // : Guid_1 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = false)] - internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); - // : Guid_2 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = false)] - internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, SomeClass @classParam, SomeStruct @structParam); - } -} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_e7fa423334e4b1a0.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_e7fa423334e4b1a0.verified.txt deleted file mode 100644 index 6ebd768..0000000 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_e7fa423334e4b1a0.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Microsoft.Extensions.Logging.AutoLoggerMessage -{ - static partial class AutoLoggerMessage - { - // : Guid_1 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = true)] - internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeStruct @structParam); - // : Guid_2 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = true)] - internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = true, SkipNullProperties = true, Transitive = true)] SomeStruct @structParam); - } -} \ No newline at end of file diff --git a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_ed18b4fdbf0bda16.verified.txt b/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_ed18b4fdbf0bda16.verified.txt deleted file mode 100644 index 782cfdd..0000000 --- a/tests/AutoLoggerMessageGenerator.UnitTests/VirtualLoggerMessage/VirtualLoggerMessageClassBuilderTests.Build_WithDifferentConfiguration_ShouldReturnLegitLoggerMessageDeclaration_ed18b4fdbf0bda16.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -namespace Microsoft.Extensions.Logging.AutoLoggerMessage -{ - static partial class AutoLoggerMessage - { - // : Guid_1 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Critical, Message = "Hello, World!", SkipEnabledCheck = false)] - internal static partial void Log_SomeNamespaceSomeClass_2_22(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeStruct @structParam); - // : Guid_2 - [Microsoft.Extensions.Logging.LoggerMessageAttribute(Level = Microsoft.Extensions.Logging.LogLevel.Trace, Message = "Goodbye, World!", SkipEnabledCheck = false)] - internal static partial void Log_SomeNamespaceSomeClass_3_33(Microsoft.Extensions.Logging.ILogger Logger, int @intParam, string @stringParam, bool @boolParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeClass @classParam, [Microsoft.Extensions.Logging.LogPropertiesAttribute(OmitReferenceName = false, SkipNullProperties = false)] SomeStruct @structParam); - } -} \ No newline at end of file From 631a945c823543f4eb397daf91288b570c2fef1c Mon Sep 17 00:00:00 2001 From: stbychkov Date: Sun, 4 May 2025 18:46:38 +0800 Subject: [PATCH 6/9] Simplified the logic with GetHashCode --- .../Models/LogMessageCall.cs | 16 ++-------------- .../Models/LoggerScopeCall.cs | 14 +------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/AutoLoggerMessageGenerator/Models/LogMessageCall.cs b/src/AutoLoggerMessageGenerator/Models/LogMessageCall.cs index 6927e15..3f9e065 100644 --- a/src/AutoLoggerMessageGenerator/Models/LogMessageCall.cs +++ b/src/AutoLoggerMessageGenerator/Models/LogMessageCall.cs @@ -29,19 +29,7 @@ public bool Equals(LogMessageCall other) => Message == other.Message && Parameters.SequenceEqual(other.Parameters); - public override int GetHashCode() - { - unchecked - { - var hashCode = Location.GetHashCode(); - hashCode = (hashCode * 397) ^ Namespace.GetHashCode(); - hashCode = (hashCode * 397) ^ ClassName.GetHashCode(); - hashCode = (hashCode * 397) ^ MethodName.GetHashCode(); - hashCode = (hashCode * 397) ^ LogLevel.GetHashCode(); - hashCode = (hashCode * 397) ^ Message.GetHashCode(); - hashCode = (hashCode * 397) ^ Parameters.GetHashCode(); - return hashCode; - } - } + public override int GetHashCode() => + (Location, Namespace, ClassName, MethodName, LogLevel, Message, Parameters).GetHashCode(); }; diff --git a/src/AutoLoggerMessageGenerator/Models/LoggerScopeCall.cs b/src/AutoLoggerMessageGenerator/Models/LoggerScopeCall.cs index 467cb6e..135a12c 100644 --- a/src/AutoLoggerMessageGenerator/Models/LoggerScopeCall.cs +++ b/src/AutoLoggerMessageGenerator/Models/LoggerScopeCall.cs @@ -25,18 +25,6 @@ public bool Equals(LoggerScopeCall other) => Message == other.Message && Parameters.SequenceEqual(other.Parameters); - public override int GetHashCode() - { - unchecked - { - var hashCode = Location.GetHashCode(); - hashCode = (hashCode * 397) ^ Namespace.GetHashCode(); - hashCode = (hashCode * 397) ^ ClassName.GetHashCode(); - hashCode = (hashCode * 397) ^ MethodName.GetHashCode(); - hashCode = (hashCode * 397) ^ Message.GetHashCode(); - hashCode = (hashCode * 397) ^ Parameters.GetHashCode(); - return hashCode; - } - } + public override int GetHashCode() => (Location, Namespace, ClassName, MethodName, Message, Parameters).GetHashCode(); }; From 18d5ee45c9689e353774a9f5eac616daddc3e7e6 Mon Sep 17 00:00:00 2001 From: stbychkov Date: Sun, 4 May 2025 22:15:36 +0800 Subject: [PATCH 7/9] Added benchmark configuration for logger scope generator --- ...onTimeBenchmark.cs => LogCallBenchmark.cs} | 19 ++- .../BenchmarkFiles/LogScopeBenchmark.cs | 109 ++++++++++++++++++ benchmarks/ProjectBuilder.cs | 18 ++- 3 files changed, 140 insertions(+), 6 deletions(-) rename benchmarks/BenchmarkFiles/{ExecutionTimeBenchmark.cs => LogCallBenchmark.cs} (92%) create mode 100644 benchmarks/BenchmarkFiles/LogScopeBenchmark.cs diff --git a/benchmarks/BenchmarkFiles/ExecutionTimeBenchmark.cs b/benchmarks/BenchmarkFiles/LogCallBenchmark.cs similarity index 92% rename from benchmarks/BenchmarkFiles/ExecutionTimeBenchmark.cs rename to benchmarks/BenchmarkFiles/LogCallBenchmark.cs index 901df78..42a5f24 100644 --- a/benchmarks/BenchmarkFiles/ExecutionTimeBenchmark.cs +++ b/benchmarks/BenchmarkFiles/LogCallBenchmark.cs @@ -5,7 +5,7 @@ [RankColumn] [MemoryDiagnoser] [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] -public partial class ExecutionTimeBenchmark +public partial class LogCallBenchmark { private ILogger _logger = null!; private ConfigurationExample _configuration = null!; @@ -13,7 +13,7 @@ public partial class ExecutionTimeBenchmark [GlobalSetup] public void Setup() { - _logger = CustomNullLogger.Instance; + _logger = CustomNullLogger.Instance; _configuration = new ConfigurationExample( 0, "Root", new ConfigurationExample( 1, "First Level", new ConfigurationExample( @@ -29,6 +29,8 @@ public void LogInformationWithoutParameters() _logger.LogInformation("Hello world!"); } +#if DEFAULT && !TELEMETRY && !AUTO_LOGGER_MESSAGE + [Benchmark] [BenchmarkCategory("Without parameters")] public void LoggerMessageWithoutParameters() @@ -39,6 +41,8 @@ public void LoggerMessageWithoutParameters() [LoggerMessage(LogLevel.Information, Message = "Hello world!")] partial void LoggerMessageWithoutParametersImpl(); +#endif + [Benchmark(Baseline = true)] [BenchmarkCategory("With 6 Parameters")] public void LogInformationWith6PrimitiveParameters() @@ -46,6 +50,8 @@ public void LogInformationWith6PrimitiveParameters() _logger.LogInformation("Hello world! {arg1} {arg2} {arg3} {arg4} {arg5} {arg6}", 1, 2, 3, 4, 5, 6); } +#if DEFAULT && !TELEMETRY && !AUTO_LOGGER_MESSAGE + [Benchmark] [BenchmarkCategory("With 6 Parameters")] public void LoggerMessageWith6Parameters() @@ -56,6 +62,8 @@ public void LoggerMessageWith6Parameters() [LoggerMessage(LogLevel.Information, Message = "Hello world! {arg1} {arg2} {arg3} {arg4} {arg5} {arg6}")] partial void LoggerMessageWith6ParametersImpl(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6); +#endif + [Benchmark(Baseline = true)] [BenchmarkCategory("With 7 Parameters")] public void LogInformationWith7PrimitiveParameters() @@ -63,6 +71,8 @@ public void LogInformationWith7PrimitiveParameters() _logger.LogInformation("Hello world! {arg1} {arg2} {arg3} {arg4} {arg5} {arg6} {arg7}", 1, 2, 3, 4, 5, 6, 7); } +#if DEFAULT && !TELEMETRY && !AUTO_LOGGER_MESSAGE + [Benchmark] [BenchmarkCategory("With 7 Parameters")] public void LoggerMessageWith7Parameters() @@ -74,6 +84,8 @@ public void LoggerMessageWith7Parameters() partial void LoggerMessageWith7ParametersImpl(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7); +#endif + [Benchmark(Baseline = true)] [BenchmarkCategory("With Complex Types Parameters")] public void LogInformationWithComplexParameters() @@ -82,6 +94,7 @@ public void LogInformationWithComplexParameters() _configuration, _configuration, _configuration, _configuration, _configuration, _configuration); } +#if DEFAULT && !TELEMETRY && !AUTO_LOGGER_MESSAGE [Benchmark] [BenchmarkCategory("With Complex Types Parameters")] public void LoggerMessageWithComplexParameters() @@ -102,6 +115,8 @@ partial void LoggerMessageWithComplexParametersImpl( ConfigurationExample arg6 ); +#endif + #if TELEMETRY [Benchmark] diff --git a/benchmarks/BenchmarkFiles/LogScopeBenchmark.cs b/benchmarks/BenchmarkFiles/LogScopeBenchmark.cs new file mode 100644 index 0000000..448c1da --- /dev/null +++ b/benchmarks/BenchmarkFiles/LogScopeBenchmark.cs @@ -0,0 +1,109 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using Microsoft.Extensions.Logging; + +[RankColumn] +[MemoryDiagnoser] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +public partial class LogScopeBenchmark +{ + private ILogger _logger = null!; + private ConfigurationExample _configuration = null!; + + [GlobalSetup] + public void Setup() + { + _logger = CustomNullLogger.Instance; + _configuration = new ConfigurationExample( + 0, "Root", new ConfigurationExample( + 1, "First Level", new ConfigurationExample( + 2, "Second level", null) + ) + ); + } + +#if !TELEMETRY + + [Benchmark(Baseline = true)] + [BenchmarkCategory("Without parameters")] + public void BeginScopeWithoutParameters() + { + using var _ = _logger.BeginScope("Hello world!"); + } + +#endif + +#if DEFAULT && !TELEMETRY && !AUTO_LOGGER_MESSAGE + + [Benchmark] + [BenchmarkCategory("Without parameters")] + public void LoggerDefineScopeWithoutParameters() + { + using var _ = _loggerDefineScopeWithoutParameters(_logger); + } + + private static readonly Func _loggerDefineScopeWithoutParameters = + LoggerMessage.DefineScope("Hello world!"); + +#endif + +#if !TELEMETRY + [Benchmark(Baseline = true)] + [BenchmarkCategory("With 6 Parameters")] + public void BeginScopeWith6PrimitiveParameters() + { + using var _ = _logger.BeginScope("Hello world! {arg1} {arg2} {arg3} {arg4} {arg5} {arg6}", 1, 2, 3, 4, 5, 6); + } +#endif + +#if DEFAULT && !TELEMETRY && !AUTO_LOGGER_MESSAGE + + [Benchmark] + [BenchmarkCategory("With 6 Parameters")] + public void LoggerDefineScopeWith6Parameters() + { + using var _ = _loggerDefineScopeWith6Parameters(_logger, 1, 2, 3, 4, 5, 6); + } + + private static readonly Func _loggerDefineScopeWith6Parameters = + LoggerMessage.DefineScope("Hello world! {arg1} {arg2} {arg3} {arg4} {arg5} {arg6}"); + +#endif + +#if !TELEMETRY + + [Benchmark(Baseline = true)] + [BenchmarkCategory("With Complex Types Parameters")] + public void BeginScopeWithComplexParameters() + { + using var _ = _logger.BeginScope("Hello world! {arg1} {arg2} {arg3} {arg4} {arg5} {arg6}", + _configuration, _configuration, _configuration, _configuration, _configuration, _configuration); + } + +#endif + +#if DEFAULT && !TELEMETRY && !AUTO_LOGGER_MESSAGE + + [Benchmark] + [BenchmarkCategory("With Complex Types Parameters")] + public void LoggerDefineScopeWithComplexParameters() + { + using var _ = _loggerDefineScopeWithComplexParameters(_logger, + _configuration, _configuration, _configuration, + _configuration, _configuration, _configuration + ); + } + + private static readonly Func _loggerDefineScopeWithComplexParameters = + LoggerMessage.DefineScope( + "Hello world! {arg1} {arg2} {arg3} {arg4} {arg5} {arg6}" + ); + +#endif + + public record ConfigurationExample(int Id, string Name, ConfigurationExample? NestedConfiguration); +} diff --git a/benchmarks/ProjectBuilder.cs b/benchmarks/ProjectBuilder.cs index 1a3011a..edc051e 100644 --- a/benchmarks/ProjectBuilder.cs +++ b/benchmarks/ProjectBuilder.cs @@ -55,9 +55,7 @@ private static void CopyBenchmarkFiles(string workingDirectory) private async Task<(string OutputFolder, string projFilePath)> GenerateProjectFile(string projectName, string workingDirectory) { - var telemetryConstant = projectConfiguration.References.Contains(PackagesProvider.MicrosoftExtensionsTelemetryPackage) - ? "TELEMETRY" - : string.Empty; + var configurations = string.Join(";", GetProjectConfigurations()); var targetFramework = TargetFrameworkMonikerDetector.Detect(); @@ -74,7 +72,7 @@ private static void CopyBenchmarkFiles(string workingDirectory) false {outputFolder} {projectName} - $(DefineConstants);{telemetryConstant} + $(DefineConstants);{configurations} @@ -99,6 +97,18 @@ private static void CopyBenchmarkFiles(string workingDirectory) return (outputFolder, projFilePath); } + private IEnumerable GetProjectConfigurations() + { + if (projectConfiguration.References.Contains(PackagesProvider.MicrosoftExtensionsLoggingPackage)) + yield return "DEFAULT"; + + if (projectConfiguration.References.Contains(PackagesProvider.MicrosoftExtensionsTelemetryPackage)) + yield return "TELEMETRY"; + + if (projectConfiguration.References.Contains(PackagesProvider.AutoLoggerMessagePackage)) + yield return "AUTO_LOGGER_MESSAGE"; + } + internal class BuildResult { public required string ProjectDirectory { get; init; } From fb350b787c7aa50f1daedc9d32a5d2b87abf1d88 Mon Sep 17 00:00:00 2001 From: stbychkov Date: Sun, 4 May 2025 22:16:17 +0800 Subject: [PATCH 8/9] Fixed invalid benchmark project dependencies --- benchmarks/AutoLoggerMessageGenerator.Benchmarks.csproj | 1 + benchmarks/PackagesProvider.cs | 3 ++- benchmarks/Program.cs | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/benchmarks/AutoLoggerMessageGenerator.Benchmarks.csproj b/benchmarks/AutoLoggerMessageGenerator.Benchmarks.csproj index 893b51f..a8ffde0 100644 --- a/benchmarks/AutoLoggerMessageGenerator.Benchmarks.csproj +++ b/benchmarks/AutoLoggerMessageGenerator.Benchmarks.csproj @@ -21,6 +21,7 @@ + diff --git a/benchmarks/PackagesProvider.cs b/benchmarks/PackagesProvider.cs index c2a806a..ee2e996 100644 --- a/benchmarks/PackagesProvider.cs +++ b/benchmarks/PackagesProvider.cs @@ -5,5 +5,6 @@ internal static class PackagesProvider public const string BenchmarkPackage = """"""; public const string MicrosoftExtensionsLoggingPackage = """"""; public const string MicrosoftExtensionsTelemetryPackage = """"""; - public const string AutoLoggerMessagePackage = """"""; + public const string AutoLoggerMessageBuildOutput = """"""; + public const string AutoLoggerMessagePackage = """"""; } diff --git a/benchmarks/Program.cs b/benchmarks/Program.cs index ffc621b..510f663 100644 --- a/benchmarks/Program.cs +++ b/benchmarks/Program.cs @@ -28,6 +28,7 @@ { PackagesProvider.BenchmarkPackage, PackagesProvider.MicrosoftExtensionsLoggingPackage, + PackagesProvider.AutoLoggerMessageBuildOutput, PackagesProvider.AutoLoggerMessagePackage } }, @@ -38,6 +39,7 @@ { PackagesProvider.BenchmarkPackage, PackagesProvider.AutoLoggerMessagePackage, + PackagesProvider.AutoLoggerMessageBuildOutput, PackagesProvider.MicrosoftExtensionsLoggingPackage, PackagesProvider.MicrosoftExtensionsTelemetryPackage, } From 06db54e95cce27b908dcccc3ca490fffe599616b Mon Sep 17 00:00:00 2001 From: stbychkov Date: Sun, 4 May 2025 22:16:34 +0800 Subject: [PATCH 9/9] Updated README with new benchmark results --- README.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4e6d956..95ad5ec 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,23 @@ Check [this](https://github.com/stbychkov/AutoLoggerMessage/wiki/Configuration) ## Benchmarks -You can achieve performance boosts of up to 90% according to my benchmarks just by including this source generator in your project. +You can achieve performance boosts of up to 90% just by including this source generator in your project. -| Configuration | Mean | Ratio | Allocated | -|------------------------|-----------|-------|-----------| -| Default implementation | 38.149 ns | 1.00 | 216 B | -| Default + AutoLogger | 3.734 ns | 0.10 | - | -| AutoLoggerMessage | 3.747 ns | 0.10 | - | +For `ILogger.Log*` methods: + +| Configuration | Mean | Ratio | Allocated | +|-------------------------|-----------|-------|-----------| +| Default implementation | 41.419 ns | 1.00 | 216 B | +| Default + LoggerMessage | 4.004 ns | 0.10 | - | +| AutoLoggerMessage | 4.577 ns | 0.11 | - | + +And for `ILogger.DefineScope`: + +| Configuration | Mean | Ratio | Allocated | +|-------------------------|-----------|-------|-----------| +| BeginScope | 39.566 ns | 1.00 | 216 B | +| DefineScope | 5.197 ns | 0.13 | - | +| AutoLoggerMessage | 5.296 ns | 0.13 | - | Take a look at [benchmark](https://github.com/stbychkov/AutoLoggerMessage/wiki/Benchmarks) page for more details.