Using CA2254 to prevent logging from failing silently
2026-03-22
Tags: .NET
If you try to use string interpolation while logging, CA2254 (from Microsoft.Extensions.Logging.Abstractions) recommends that "The logging message template should not vary between calls to LoggerExtensions.Log*". This is because when logging, parameters should be given names, so that an implementation can allow for easier filtering logs by the values of these properties.
_logger.LogInformation($"User {userName} logged in"); // Warning: CA2254
_logger.LogInformation("User {userName} logged in", userName); // Correct version
A caveat of this is that many ILogger implementations, including the default in most app hosts, will fail silently in the event of a parameter count mismatch. There is some merit to this decision - you probably don't want a failed log event to throw an exception.
_logger.LogInformation("User {userName} logged in at {dateTime}", userName);
// fails quietly because we didn't pass {dateTime}
Fortunately, it's possible to catch parameter count mismatches at compile time, with warning CA2017. To ensure that logging never fails in this way, you can promote the warning to a compiler error. In an SDK-style project, or better yet in Directory.Build.props (to apply to all projects), add:
<PropertyGroup>
<WarningsAsErrors>
$(WarningsAsErrors);CA2017
</WarningsAsErrors>
</PropertyGroup>
Tip
You may also want to treat CA2254 as an error to prevent the use of string interpolation and ensure that all logging methods are properly parameterised.
You can also add this validation to youur own methods methods, such as extensions on ILogger or your own implementation (so long as Microsoft.Extensions.Logging.Abstractions is installed).
First, declare the following attribute in any namespace. Note that the class and property name must match exactly what is in this example.
[AttributeUsage(AttributeTargets.Method)]
public sealed class MessageTemplateFormatMethodAttribute : Attribute
{
public string FormatParameterName { get; }
public MessageTemplateFormatMethodAttribute(string formatParameterName)
{
FormatParameterName = formatParameterName;
}
}
Then, use the attribute on a method, giving it the name of the method's template parameter.
Tip
Instead of using the name of the parameter as a string literal, use nameof(). This will prevent the attribute from quietly breaking if you update the parameter name but forget to update the attribute.
[MessageTemplateFormatMethod(nameof(template))]
public static void LogCustom(this ILogger source, string template, params object?[]? arguments)
{
// ...
}
Now, you can rest assured that every log event will succeed and be properly parameterised.