dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.64k stars 4.57k forks source link

[Analyzer] Usage of ToString in logging source generator method call sites #78402

Open stephentoub opened 1 year ago

stephentoub commented 1 year ago

We should consider an analyzer that flags use of ToString() in call sites to logging methods and suggests reconsidering the shape of the logging method such that it takes a strongly-typed value. In many situations, logging is disabled or set at a log level that will result in a particular event being disabled, and doing the ToString() at the call site is then often unnecessary allocation / expense. For example, given:

[LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = "Excessively large timeout `{timeout}`")]
public static partial void ExcessivelyLargeTimeout(this ILogger logger, string timeout);
...
TimeSpan timeout = ...;
logger.ExcessivelyLargeTimeout(timeout.ToString());

the analyzer would flag the .ToString() and suggest the ExcessivelyLargeTimeout method should be changed to take a TimeSpan instead of a string, with the .ToString() removed from the call site.

Performance rules Category Severity = suggestion

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/area-extensions-logging See info in area-owners.md if you want to be subscribed.

Issue Details
We should consider an analyzer that flags use of `ToString()` in call sites to logging methods and suggests reconsidering the shape of the logging method such that it takes a strongly-typed value. In many situations, logging is disabled or set at a log level that will result in a particular event being disabled, and doing the `ToString()` at the call site is then often unnecessary allocation / expense. For example, given: ```C# [LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = "Excessively large timeout `{timeout}`")] public static partial void ExcessivelyLargeTimeout(this ILogger logger, string timeout); ... TimeSpan timeout = ...; logger.ExcessivelyLargeTimeout(timeout.ToString()); ``` the analyzer would flag the `.ToString()` and suggest the `ExcessivelyLargeTimeout` method should be changed to take a `TimeSpan` instead of a `string`, with the `.ToString()` removed from the call site.
Author: stephentoub
Assignees: -
Labels: `code-analyzer`, `area-Extensions-Logging`
Milestone: -
tarekgh commented 1 year ago

CC @Youssef1313

Youssef1313 commented 1 year ago

Happy to implement once the analyzer is approved.

bartonjs commented 1 year ago

Analyzer would detect when "work" is being done in an argument call to an ILogger logging method. This basically boils down to anything other than a literal-expression, local-expression, parameter-expression, field-expression, property-expression, or indexer-expression (potentially recursively applied).

Note that this list includes identifying any parameters being wrapped in a new array to a params method call.

The analyzer should not fire if it identifies that it is in a scope that has already checked on log levels.

A logging method is any of:

Category: Performance Severity: Suggestion

Youssef1313 commented 1 year ago

@buyaa-n I did a quick (not yet tested) prototype (https://github.com/dotnet/roslyn-analyzers/pull/6643).

For now, I don't think I'll be able to complete it soon.

If anyone wants to pick it and continue on the code I wrote, this is totally fine.

Currently, the PR is missing:

tarekgh commented 1 year ago

@Youssef1313 thanks for letting us know and having a draft PR we can use to build this analyzer.

mpidash commented 3 months ago

I'd like to finish this analyzer :)

tarekgh commented 3 months ago

@mpidash thanks for willing to help with that. Please ensure doing it as described in https://github.com/dotnet/runtime/issues/78402#issuecomment-1324132348.