diff --git a/src/Microsoft.Extensions.Logging.Abstractions/LoggerMessage.cs b/src/Microsoft.Extensions.Logging.Abstractions/LoggerMessage.cs index 63b96d91..3f6879f5 100644 --- a/src/Microsoft.Extensions.Logging.Abstractions/LoggerMessage.cs +++ b/src/Microsoft.Extensions.Logging.Abstractions/LoggerMessage.cs @@ -66,6 +66,26 @@ public static Func DefineScope(str return (logger, arg1, arg2, arg3) => logger.BeginScopeImpl(new LogValues(formatter, arg1, arg2, arg3)); } + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The + /// The event id + /// The named format string + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, int eventId, string formatString) + { + var formatter = new LogValuesFormatter(formatString); + + return (logger, exception) => + { + if (logger.IsEnabled(logLevel)) + { + logger.Log(logLevel, eventId, new LogValues(formatter), exception, LogValues.Callback); + } + }; + } + /// /// Creates a delegate which can be invoked for logging a message. /// @@ -132,6 +152,81 @@ public static Action Define(LogLevel }; } + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The type of the fourth parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, int eventId, string formatString) + { + var formatter = new LogValuesFormatter(formatString); + + return (logger, arg1, arg2, arg3, arg4, exception) => + { + if (logger.IsEnabled(logLevel)) + { + logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4), exception, LogValues.Callback); + } + }; + } + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The type of the fourth parameter passed to the named format string. + /// The type of the fifth parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, int eventId, string formatString) + { + var formatter = new LogValuesFormatter(formatString); + + return (logger, arg1, arg2, arg3, arg4, arg5, exception) => + { + if (logger.IsEnabled(logLevel)) + { + logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5), exception, LogValues.Callback); + } + }; + } + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The type of the fourth parameter passed to the named format string. + /// The type of the fifth parameter passed to the named format string. + /// The type of the sixth parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, int eventId, string formatString) + { + var formatter = new LogValuesFormatter(formatString); + + return (logger, arg1, arg2, arg3, arg4, arg5, arg6, exception) => + { + if (logger.IsEnabled(logLevel)) + { + logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5, arg6), exception, LogValues.Callback); + } + }; + } + private class LogValues : ILogValues { public static Func Callback = (state, exception) => ((LogValues)state)._formatter.Format(((LogValues)state).ToArray()); @@ -268,6 +363,123 @@ public IEnumerable> GetValues() => new[] public override string ToString() => _formatter.Format(ToArray()); } + + private class LogValues : ILogValues + { + public static Func Callback = (state, exception) => ((LogValues)state)._formatter.Format(((LogValues)state).ToArray()); + + private readonly LogValuesFormatter _formatter; + public T0 _value0; + public T1 _value1; + public T2 _value2; + public T3 _value3; + public T4 _value4; + + public LogValues(LogValuesFormatter formatter, T0 value0, T1 value1, T2 value2, T3 value3, T4 value4) + { + _formatter = formatter; + _value0 = value0; + _value1 = value1; + _value2 = value2; + _value3 = value3; + _value4 = value4; + } + + public IEnumerable> GetValues() => new[] + { + new KeyValuePair(_formatter.ValueNames[0], _value0), + new KeyValuePair(_formatter.ValueNames[1], _value1), + new KeyValuePair(_formatter.ValueNames[2], _value2), + new KeyValuePair(_formatter.ValueNames[3], _value3), + new KeyValuePair(_formatter.ValueNames[4], _value4), + new KeyValuePair("{OriginalFormat}", _formatter.OriginalFormat), + }; + + public object[] ToArray() => new object[] { _value0, _value1, _value2, _value3, _value4 }; + + public override string ToString() => _formatter.Format(ToArray()); + } + + private class LogValues : ILogValues + { + public static Func Callback = (state, exception) => ((LogValues)state)._formatter.Format(((LogValues)state).ToArray()); + + private readonly LogValuesFormatter _formatter; + public T0 _value0; + public T1 _value1; + public T2 _value2; + public T3 _value3; + public T4 _value4; + public T5 _value5; + + public LogValues(LogValuesFormatter formatter, T0 value0, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + { + _formatter = formatter; + _value0 = value0; + _value1 = value1; + _value2 = value2; + _value3 = value3; + _value4 = value4; + _value5 = value5; + } + + public IEnumerable> GetValues() => new[] + { + new KeyValuePair(_formatter.ValueNames[0], _value0), + new KeyValuePair(_formatter.ValueNames[1], _value1), + new KeyValuePair(_formatter.ValueNames[2], _value2), + new KeyValuePair(_formatter.ValueNames[3], _value3), + new KeyValuePair(_formatter.ValueNames[4], _value4), + new KeyValuePair(_formatter.ValueNames[5], _value5), + new KeyValuePair("{OriginalFormat}", _formatter.OriginalFormat), + }; + + public object[] ToArray() => new object[] { _value0, _value1, _value2, _value3, _value4, _value5 }; + + public override string ToString() => _formatter.Format(ToArray()); + } + + private class LogValues : ILogValues + { + public static Func Callback = (state, exception) => ((LogValues)state)._formatter.Format(((LogValues)state).ToArray()); + + private readonly LogValuesFormatter _formatter; + public T0 _value0; + public T1 _value1; + public T2 _value2; + public T3 _value3; + public T4 _value4; + public T5 _value5; + public T6 _value6; + + public LogValues(LogValuesFormatter formatter, T0 value0, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + { + _formatter = formatter; + _value0 = value0; + _value1 = value1; + _value2 = value2; + _value3 = value3; + _value4 = value4; + _value5 = value5; + _value6 = value6; + } + + public IEnumerable> GetValues() => new[] + { + new KeyValuePair(_formatter.ValueNames[0], _value0), + new KeyValuePair(_formatter.ValueNames[1], _value1), + new KeyValuePair(_formatter.ValueNames[2], _value2), + new KeyValuePair(_formatter.ValueNames[3], _value3), + new KeyValuePair(_formatter.ValueNames[4], _value4), + new KeyValuePair(_formatter.ValueNames[5], _value5), + new KeyValuePair(_formatter.ValueNames[6], _value6), + new KeyValuePair("{OriginalFormat}", _formatter.OriginalFormat), + }; + + public object[] ToArray() => new object[] { _value0, _value1, _value2, _value3, _value4, _value5, _value6 }; + + public override string ToString() => _formatter.Format(ToArray()); + } } } diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerMessageTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerMessageTest.cs index 114e5875..b019ae4f 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerMessageTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerMessageTest.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Remoting.Messaging; using Microsoft.Extensions.Logging.Test; using Xunit; using Xunit.Sdk; @@ -162,6 +163,47 @@ public void LogScope_WithThreeParameters() actualLogValues.ToString()); } + [Theory] + [MemberData(nameof(LogMessagesData))] + public void LogMessages(Delegate messageDelegate, int argumentCount) + { + // Arrange + var testSink = new TestSink(); + var testLogger = new TestLogger("testlogger", testSink, enabled: true); + var exception = new Exception("TestException"); + var parameterNames = Enumerable.Range(0, argumentCount).Select(i => "P" + i).ToArray(); + var parameters = new List(); + parameters.Add(testLogger); + parameters.AddRange(parameterNames); + parameters.Add(exception); + + var expectedFormat = "Log " + string.Join(" ", parameterNames.Select(p => "{" + p + "}")); + var expectedToString = "Log " + string.Join(" ", parameterNames); + var expectedValues = parameterNames.Select(p => new KeyValuePair(p, p)).ToList(); + expectedValues.Add(new KeyValuePair("{OriginalFormat}", expectedFormat)); + + // Act + messageDelegate.DynamicInvoke(parameters.ToArray()); + + // Assert + Assert.Equal(1, testSink.Writes.Count); + var write = testSink.Writes.First(); + var actualLogValues = Assert.IsAssignableFrom(write.State); + AssertLogValues(expectedValues, actualLogValues.GetValues()); + Assert.Equal(expectedToString, actualLogValues.ToString()); + } + + public static IEnumerable LogMessagesData => new[] + { + new object[] { LoggerMessage.Define(LogLevel.Error, 0, "Log "), 0 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 1, "Log {P0}"), 1 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 2, "Log {P0} {P1}"), 2 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 3, "Log {P0} {P1} {P2}"), 3 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 4, "Log {P0} {P1} {P2} {P3}"), 4 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 5, "Log {P0} {P1} {P2} {P3} {P4}"), 5 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 6, "Log {P0} {P1} {P2} {P3} {P4} {P5}"), 6 }, + }; + private void AssertLogValues( IEnumerable> expected, IEnumerable> actual)