Skip to content

Commit

Permalink
MA0124 can be configured using the StructuredLogField attribute (#578)
Browse files Browse the repository at this point in the history
  • Loading branch information
meziantou committed Jul 26, 2023
1 parent feb4e5b commit b59026c
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 1 deletion.
17 changes: 17 additions & 0 deletions docs/Rules/MA0124.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,23 @@ Then, you need to add the file to the `AdditionalFiles` collection in the `cspro
</Project>
````

You can also configure the allowed types by using an assembly attribute. This attributes are applied only for the current assemblies. The rule does not consider attributes defined in referenced assemblies.

````c#
[assembly: Meziantou.Analyzer.StructuredLogField("Count", typeof(int), typeof(long))]

// Need to be defined in your code
namespace Meziantou.Analyzer
{
[System.Diagnostics.Conditional("MEZIANTOU_ANALYZER_ATTRIBUTES")]
[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
internal sealed class StructuredLogFieldAttribute : System.Attribute
{
public StructuredLogFieldAttribute(string parameterName, params System.Type[] allowedTypes) { }
}
}
````

Then, the analyzer reports log parameters of the wrong type:

````
Expand Down
17 changes: 17 additions & 0 deletions src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public AnalyzerContext(CompilationStartAnalysisContext context)
LoggerSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Logging.ILogger");
LoggerExtensionsSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensions");
LoggerMessageSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessage");
StructuredLogFieldAttributeSymbol = compilation.GetBestTypeByMetadataName("Meziantou.Analyzer.StructuredLogFieldAttribute");

var errors = new List<Diagnostic>();
Configuration = LoadConfiguration();
Expand Down Expand Up @@ -170,6 +171,21 @@ LoggerConfigurationFile LoadConfiguration()
}
}

if (StructuredLogFieldAttributeSymbol != null)
{
var attributes = context.Compilation.Assembly.GetAttributes();
foreach (var attribute in attributes)
{
if (!attribute.AttributeClass.IsEqualTo(StructuredLogFieldAttributeSymbol))
continue;

if (attribute.ConstructorArguments is [{ Type.SpecialType: SpecialType.System_String, IsNull: false, Value: string name }, TypedConstant { Kind: TypedConstantKind.Array } types])
{
configuration[name] = types.Values.Select(v => v.Value as ISymbol).WhereNotNull().ToArray();
}
}
}

return new LoggerConfigurationFile(configuration);

static Location CreateLocation(AdditionalText file, SourceText sourceText, TextLine line)
Expand All @@ -182,6 +198,7 @@ static Location CreateLocation(AdditionalText file, SourceText sourceText, TextL
public INamedTypeSymbol? LoggerSymbol { get; }
public INamedTypeSymbol? LoggerExtensionsSymbol { get; }
public INamedTypeSymbol? LoggerMessageSymbol { get; }
public INamedTypeSymbol? StructuredLogFieldAttributeSymbol { get; private set; }
public LoggerConfigurationFile Configuration { get; }

public bool IsValid => LoggerSymbol != null && LoggerExtensionsSymbol != null && LoggerMessageSymbol != null && Configuration.Count > 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public sealed class LoggerParameterTypeAnalyzerTests
private static ProjectBuilder CreateProjectBuilder() => new ProjectBuilder()
.WithAnalyzer<LoggerParameterTypeAnalyzer>()
.WithOutputKind(Microsoft.CodeAnalysis.OutputKind.ConsoleApplication)
.WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp9)
.WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.Latest)
.WithTargetFramework(TargetFramework.Net7_0)
.AddNuGetReference("Microsoft.Extensions.Logging.Abstractions", "7.0.0", "lib/net7.0");

Expand Down Expand Up @@ -297,4 +297,35 @@ await CreateProjectBuilder()
.ShouldReportDiagnosticWithMessage("Log parameter 'Prop' has no configured type")
.ValidateAsync();
}

[Fact]
public async Task ConfigurationFromAttribute()
{
const string SourceCode = """
using Microsoft.Extensions.Logging;

[assembly: Meziantou.Analyzer.StructuredLogFieldAttribute("Prop", typeof(string), typeof(long))]

ILogger logger = null;
logger.LogInformation("{Prop}", [|2|]);
logger.LogInformation("{Prop}", 2L);
logger.LogInformation("{Prop}", "");

namespace Meziantou.Analyzer
{
[System.Diagnostics.ConditionalAttribute("MEZIANTOU_ANALYZER_ATTRIBUTES")]
[System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
internal sealed class StructuredLogFieldAttribute : System.Attribute
{
public StructuredLogFieldAttribute(string parameterName, params System.Type[] allowedTypes) { }
}
}
""";
await CreateProjectBuilder()
.WithSourceCode(SourceCode)
.AddAdditionalFile("LoggerParameterTypes.txt", """
Prop;System.Int32
""")
.ValidateAsync();
}
}

0 comments on commit b59026c

Please sign in to comment.