Description
Background and motivation
#51064 introduced a new LoggingGenerator source generator which allows writing code like
public partial class LoggingSample3
{
private readonly ILogger _logger;
public LoggingSample3(ILogger logger)
{
_logger = logger;
}
[LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = "Hello {name}")]
public partial void LogName(string name);
}
and having an implementation using LoggerMessage.Define
generated like
partial class LoggingSample3
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "1.0.0.0")]
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, string, global::System.Exception?> _LogNameCallback =
global::Microsoft.Extensions.Logging.LoggerMessage.Define<string>(global::Microsoft.Extensions.Logging.LogLevel.Information, new global::Microsoft.Extensions.Logging.EventId(0, nameof(LogName)), "Hello {name}", skipEnabledCheck: true);
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "1.0.0.0")]
public partial void LogName(string name)
{
if (_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Information))
{
_LogNameCallback(_logger, name, null);
}
}
}
However, this doesn't support the other method for high-performance logging, LoggerMessage.DefineScope
. This means that, if you use scopes, you will have manual calls to LoggerMessage.DefineScope
and the requisite delegate fields side-by-side with the new source generator approach:
[LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = "Hello {name}")]
public partial void LogName(string name);
private static readonly Func<ILogger, string, int, IDisposable?> ScopeCallback =
LoggerMessage.DefineScope<string, int>("Value={Value};OtherValue={OtherValue}");
public IDisposable? Scope(string value, int otherValue) => ScopeCallback(_logger, value, otherValue);
API Proposal
I suggest adding a new attribute side-by-side with the existing LoggerMessageAttribute
namespace Microsoft.Extensions.Logging;
[AttributeUsage(AttributeTargets.Method)]
public sealed partial class LoggerMessageScopeAttribute : Attribute
{
public LoggerMessageScopeAttribute();
public string Message { get; set; } = "";
}
and having LoggingGenerator generate calls to LoggerMessage.DefineScope
, similar to the way it currently generates them for LoggerMessage.Define
.
API Usage
This will allow defining logger messages and scopes in the same way:
[LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = "Hello {name}")]
public partial void LogName(string name);
[LoggerMessageScope(Message = "Value={Value};OtherValue={OtherValue}")]
public partial IDisposable? Scope(string value, int otherValue);
Alternative Designs
No response
Risks
No response