Skip to content

Add LoggerMessage.Define overloads accepting up to 14 arguments. #50913

@maryamariyan

Description

@maryamariyan

We would like to have a LoggerMessage.Define() method that accepts up to 14 type args.

New API Proposal

public static partial class LoggerMessage
{
    public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }

where Microsoft.Extensions.Logging.LogOptions only has the one bool flag SkipEnabledCheck below:

namespace Microsoft.Extensions.Logging
{ 
    public class LogOptions
    {
        public bool SkipEnabledCheck { get; set; }
    }
}

The 6 new APIs below that were added in .NET 6 timeframe need to get updated as well.
It now makes sense to update to taking LogOptions instead of boolean skipEnabledCheck flag:

- public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, System.Exception?> Define<T1>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
+ public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, System.Exception?> Define<T1>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {​ throw null; }​
- public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, System.Exception?> Define<T1, T2>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
+ public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, System.Exception?> Define<T1, T2>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {​ throw null; }​
- public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, System.Exception?> Define<T1, T2, T3>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
+ public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, System.Exception?> Define<T1, T2, T3>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {​ throw null; }​
- public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, System.Exception?> Define<T1, T2, T3, T4>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
+ public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, System.Exception?> Define<T1, T2, T3, T4>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {​ throw null; }​
- public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, System.Exception?> Define<T1, T2, T3, T4, T5>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
+ public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, System.Exception?> Define<T1, T2, T3, T4, T5>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {​ throw null; }​
- public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, System.Exception?> Define<T1, T2, T3, T4, T5, T6>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
+ public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, System.Exception?> Define<T1, T2, T3, T4, T5, T6>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {​ throw null; }​

More info for motive: #50334 (comment)

New proposal adds 10 new overloads. To support up to 16 arguments because that is what Action allows as well.

Previous API Proposal
public static partial class LoggerMessage
{
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; }
        public static System.Action<Microsoft.Extensions.Logging.ILogger, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; }
}

Older issue (#35060) added up to 6 overlods for Define, this issue adds up to 16.

The reason to add up to 16 is because Action itself takes up to 16 parameters: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Action.cs#L24

Motivation

We've had requests to add more overloads in the past as well (e.g. #35060) so there is community interest in adding these APIs.

Also, the compile-time source generation for logging, allows support arbitrary number of parameters in a message template. But the runtime capabilities provided by LoggerMessage.Define APIs today is limited to only 6 parameters.

Adding the above APIs helps keep a more coherent range of logging feature support between compile-time and runtime in the framework library. This way for the most frequently hit user scenarios (up to 16 arguments) the compile time implementation can remain intact with runtime as well.

Additional Information

  • Link to logging generator: here
  • Link to generated baseline code samples using the logging generator: here

Follow up proposal - Support Dynamic Log Levels

As a follow up to the API proposal above, we could consider the APIs below as well to allow supporting dynamic log levels using LoggerMessage.Define.

Note: The compile-time source generator supports dynamic log levels as a feature.

public static partial class LoggerMessage
{
    public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, System.Exception?> Define(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, System.Exception?> Define<T1>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, System.Exception?> Define<T1, T2>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, System.Exception?> Define<T1, T2, T3>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, System.Exception?> Define<T1, T2, T3, T4>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, T5, System.Exception?> Define<T1, T2, T3, T4, T5>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, T5, T6, System.Exception?> Define<T1, T2, T3, T4, T5, T6>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, T5, T6, T7, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, T5, T6, T7, T8, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, T5, T6, T7, T8, T9, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }public static System.Action<Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.LogLevel logLevel, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, System.Exception?> Define<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogOptions options) {throw null; }}

Dynamic Log Levels - Usage in dotnet/aspnetcore:

https://github.com/dotnet/aspnetcore/blob/bbe65b8eaadcb6021f863a293fb57c46085db883/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs#L211-L227

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions