Skip to content

Commit 4215658

Browse files
committed
MessageProperties - Support list with single property (do not treat as message template)
1 parent 00a88fe commit 4215658

File tree

3 files changed

+98
-61
lines changed

3 files changed

+98
-61
lines changed

src/NLog.Extensions.Logging/Logging/NLogLogger.cs

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,29 @@ public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId
3535
{
3636
throw new ArgumentNullException(nameof(formatter));
3737
}
38-
var message = formatter(state, exception);
3938

39+
var message = formatter(state, exception);
4040
var messageTemplate = _options.CaptureMessageTemplates ? state as IReadOnlyList<KeyValuePair<string, object>> : null;
4141
LogEventInfo eventInfo = CreateLogEventInfo(nLogLogLevel, message, messageTemplate);
42-
eventInfo.Exception = exception;
42+
if (exception != null)
43+
{
44+
eventInfo.Exception = exception;
45+
}
4346

4447
CaptureEventId(eventInfo, eventId);
4548

46-
CaptureMessageProperties(eventInfo, state, messageTemplate);
49+
if (messageTemplate == null)
50+
{
51+
CaptureMessageProperties(eventInfo, state);
52+
}
4753

4854
_logger.Log(eventInfo);
4955
}
5056

5157

5258
private LogEventInfo CreateLogEventInfo(LogLevel nLogLogLevel, string message, IReadOnlyList<KeyValuePair<string, object>> parameterList)
5359
{
54-
if (parameterList != null && parameterList.Count > 1 && IsNonDigitValue(parameterList[0].Key))
60+
if (parameterList != null && parameterList.Count > 0 && IsNonDigitValue(parameterList[0].Key))
5561
{
5662
return CreateLogEventInfoWithMultipleParameters(nLogLogLevel, message, parameterList);
5763
}
@@ -68,30 +74,24 @@ private static bool IsNonDigitValue(string value)
6874
/// </summary>
6975
private LogEventInfo CreateLogEventInfoWithMultipleParameters(LogLevel nLogLogLevel, string message, IReadOnlyList<KeyValuePair<string, object>> parameterList)
7076
{
71-
var originalFormat = parameterList[parameterList.Count - 1];
72-
string originalMessage = null;
73-
if (originalFormat.Key == OriginalFormatPropertyName)
74-
{
75-
// Attempt to capture original message with placeholders
76-
originalMessage = originalFormat.Value as string;
77-
}
78-
79-
var messageTemplateParameters = new NLogMessageParameterList(parameterList, originalMessage != null);
80-
var eventInfo = new LogEventInfo(nLogLogLevel, _logger.Name, originalMessage ?? message, messageTemplateParameters);
77+
var messageTemplateParameters = new NLogMessageParameterList(parameterList);
78+
var originalMessage = messageTemplateParameters.OriginalMessage as string;
79+
var logEvent = new LogEventInfo(nLogLogLevel, _logger.Name, originalMessage ?? message, messageTemplateParameters);
8180
if (originalMessage != null)
8281
{
83-
SetEventInfoParameters(eventInfo, messageTemplateParameters);
84-
eventInfo.Parameters[messageTemplateParameters.Count] = message;
85-
eventInfo.MessageFormatter = (l) => (string)l.Parameters[l.Parameters.Length - 1];
82+
SetLogEventMessageFormatter(logEvent, messageTemplateParameters, message);
8683
}
87-
return eventInfo;
84+
return logEvent;
8885
}
8986

90-
private static void SetEventInfoParameters(LogEventInfo eventInfo, NLogMessageParameterList messageTemplateParameters)
87+
private static void SetLogEventMessageFormatter(LogEventInfo logEvent, NLogMessageParameterList messageTemplateParameters, string formattedMessage)
9188
{
92-
eventInfo.Parameters = new object[messageTemplateParameters.Count + 1];
93-
for (int i = 0; i < messageTemplateParameters.Count; ++i)
94-
eventInfo.Parameters[i] = messageTemplateParameters[i].Value;
89+
var parameters = new object[messageTemplateParameters.Count + 1];
90+
for (int i = 0; i < parameters.Length - 1; ++i)
91+
parameters[i] = messageTemplateParameters[i].Value;
92+
parameters[parameters.Length - 1] = formattedMessage;
93+
logEvent.Parameters = parameters;
94+
logEvent.MessageFormatter = (l) => (string)l.Parameters[l.Parameters.Length - 1];
9595
}
9696

9797
private void CaptureEventId(LogEventInfo eventInfo, EventId eventId)
@@ -123,9 +123,9 @@ private static Tuple<string, string, string> CreateEventIdPropertyNames(string e
123123
return eventIdPropertyNames;
124124
}
125125

126-
private void CaptureMessageProperties<TState>(LogEventInfo eventInfo, TState state, IReadOnlyList<KeyValuePair<string, object>> messageTemplate)
126+
private void CaptureMessageProperties<TState>(LogEventInfo eventInfo, TState state)
127127
{
128-
if (_options.CaptureMessageProperties && messageTemplate == null && state is IEnumerable<KeyValuePair<string, object>> messageProperties)
128+
if (_options.CaptureMessageProperties && state is IEnumerable<KeyValuePair<string, object>> messageProperties)
129129
{
130130
foreach (var property in messageProperties)
131131
{
@@ -273,15 +273,5 @@ public IDisposable BeginScope<TState>(TState state)
273273

274274
return NestedDiagnosticsLogicalContext.Push(state);
275275
}
276-
277-
internal static string RemoveMarkerFromName(string parameterName)
278-
{
279-
var firstChar = parameterName[0];
280-
if (firstChar == '@' || firstChar == '$')
281-
{
282-
parameterName = parameterName.Substring(1);
283-
}
284-
return parameterName;
285-
}
286276
}
287277
}

src/NLog.Extensions.Logging/Logging/NLogMessageParameterList.cs

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,46 @@ internal class NLogMessageParameterList : IList<NLog.MessageTemplates.MessageTem
1111
{
1212
private readonly IReadOnlyList<KeyValuePair<string, object>> _parameterList;
1313

14-
public NLogMessageParameterList(IReadOnlyList<KeyValuePair<string, object>> parameterList, bool includesOriginalMessage)
14+
public object OriginalMessage => _originalMessageIndex.HasValue ? _parameterList[_originalMessageIndex.Value].Value : null;
15+
public int? _originalMessageIndex;
16+
17+
public NLogMessageParameterList(IReadOnlyList<KeyValuePair<string, object>> parameterList)
1518
{
16-
if (!includesOriginalMessage || !IsValidParameterList(parameterList))
19+
if (IsValidParameterList(parameterList, out _originalMessageIndex))
1720
{
18-
_parameterList = CreateValidParameterList(parameterList);
21+
_parameterList = parameterList;
1922
}
2023
else
2124
{
22-
_parameterList = parameterList;
25+
_parameterList = CreateValidParameterList(parameterList);
2326
}
2427
}
2528

2629
/// <summary>
2730
/// Verify that the input parameterList contains non-empty key-values and the orignal-format-property at the end
2831
/// </summary>
29-
private bool IsValidParameterList(IReadOnlyList<KeyValuePair<string, object>> parameterList)
32+
private bool IsValidParameterList(IReadOnlyList<KeyValuePair<string, object>> parameterList, out int? originalMessageIndex)
3033
{
31-
int parameterCount = parameterList.Count - 1;
32-
for (int i = 0; i <= parameterCount; ++i)
34+
originalMessageIndex = null;
35+
for (int i = 0; i < parameterList.Count; ++i)
3336
{
3437
var paramPair = parameterList[i];
35-
if (!ValidParameterKey(paramPair.Key, i == parameterCount))
38+
if (string.IsNullOrEmpty(paramPair.Key))
39+
{
40+
originalMessageIndex = null;
3641
return false;
42+
}
43+
44+
if (paramPair.Key == NLogLogger.OriginalFormatPropertyName)
45+
{
46+
if (originalMessageIndex.HasValue)
47+
{
48+
originalMessageIndex = null;
49+
return false;
50+
}
51+
52+
originalMessageIndex = i;
53+
}
3754
}
3855

3956
return true;
@@ -44,46 +61,48 @@ private bool IsValidParameterList(IReadOnlyList<KeyValuePair<string, object>> pa
4461
/// </summary>
4562
private IReadOnlyList<KeyValuePair<string, object>> CreateValidParameterList(IReadOnlyList<KeyValuePair<string, object>> parameterList)
4663
{
47-
var validParameterList = new List<KeyValuePair<string, object>>(parameterList.Count + 1);
64+
var validParameterList = new List<KeyValuePair<string, object>>(parameterList.Count);
4865
for (int i = 0; i < parameterList.Count; ++i)
4966
{
5067
var paramPair = parameterList[i];
51-
if (!ValidParameterKey(paramPair.Key, false))
68+
if (string.IsNullOrEmpty(paramPair.Key))
69+
continue;
70+
71+
if (paramPair.Key == NLogLogger.OriginalFormatPropertyName)
5272
continue;
5373

5474
validParameterList.Add(parameterList[i]);
5575
}
56-
validParameterList.Add(new KeyValuePair<string, object>()); // Simulate NLogLogger.OriginalFormatPropertyName
5776
return validParameterList;
5877
}
5978

60-
private bool ValidParameterKey(string keyValue, bool lastKey)
61-
{
62-
if (string.IsNullOrEmpty(keyValue))
63-
return false; // Non-empty string not allowed
64-
65-
if (keyValue == NLogLogger.OriginalFormatPropertyName)
66-
return lastKey; // Original format message, must be last parameter
67-
68-
if (lastKey)
69-
return false; // Original format message, must be last parameter
70-
71-
return true;
72-
}
73-
7479
public NLog.MessageTemplates.MessageTemplateParameter this[int index]
7580
{
7681
get
7782
{
83+
if (index >= _originalMessageIndex)
84+
index += 1;
85+
7886
var parameter = _parameterList[index];
7987
var parameterName = parameter.Key;
8088
var capture = GetCaptureType(parameterName);
81-
parameterName = NLogLogger.RemoveMarkerFromName(parameterName);
89+
if (capture != MessageTemplates.CaptureType.Normal)
90+
parameterName = RemoveMarkerFromName(parameterName);
8291
return new NLog.MessageTemplates.MessageTemplateParameter(parameterName, parameter.Value, null, capture);
8392
}
8493
set => throw new NotSupportedException();
8594
}
8695

96+
private static string RemoveMarkerFromName(string parameterName)
97+
{
98+
var firstChar = parameterName[0];
99+
if (firstChar == '@' || firstChar == '$')
100+
{
101+
parameterName = parameterName.Substring(1);
102+
}
103+
return parameterName;
104+
}
105+
87106
private static NLog.MessageTemplates.CaptureType GetCaptureType(string parameterName)
88107
{
89108
var captureType = NLog.MessageTemplates.CaptureType.Normal;
@@ -100,7 +119,7 @@ private static NLog.MessageTemplates.CaptureType GetCaptureType(string parameter
100119
return captureType;
101120
}
102121

103-
public int Count => _parameterList.Count - 1;
122+
public int Count => _parameterList.Count - (_originalMessageIndex.HasValue ? 1 : 0);
104123

105124
public bool IsReadOnly => true;
106125

test/LoggerTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ public void TestStructuredLogging()
5454
Assert.Equal("NLog.Extensions.Logging.Tests.LoggerTests.Runner|DEBUG|message with id and 1 parameters |1", target.Logs.FirstOrDefault());
5555
}
5656

57+
[Fact]
58+
public void TestSimulateStructuredLogging()
59+
{
60+
GetRunner().LogDebugWithSimulatedStructuredParameters();
61+
62+
var target = GetTarget();
63+
Assert.Equal("NLog.Extensions.Logging.Tests.LoggerTests.Runner|DEBUG|message with id and 1 property |1", target.Logs.FirstOrDefault());
64+
}
65+
5766
[Fact]
5867
public void TestMessageProperties()
5968
{
@@ -63,6 +72,15 @@ public void TestMessageProperties()
6372
Assert.Equal("NLog.Extensions.Logging.Tests.LoggerTests.Runner|DEBUG|message with id and 1 property |1", target.Logs.FirstOrDefault());
6473
}
6574

75+
[Fact]
76+
public void TestMessagePropertiesList()
77+
{
78+
GetRunner().LogDebugWithMessagePropertiesList();
79+
80+
var target = GetTarget();
81+
Assert.Equal("NLog.Extensions.Logging.Tests.LoggerTests.Runner|DEBUG|message with id and 1 property |1", target.Logs.FirstOrDefault());
82+
}
83+
6684
[Fact]
6785
public void TestScopeProperties()
6886
{
@@ -243,11 +261,21 @@ public void LogDebugWithStructuredParameters()
243261
_logger.LogDebug("message with id and {ParameterCount} parameters", "1");
244262
}
245263

264+
public void LogDebugWithSimulatedStructuredParameters()
265+
{
266+
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Debug, default(EventId), new List<KeyValuePair<string, object>>(new [] { new KeyValuePair<string,object>("{OriginalFormat}", "message with id and {ParameterCount} property"), new KeyValuePair<string, object>("ParameterCount", 1) }), null, (s, ex) => "message with id and 1 property");
267+
}
268+
246269
public void LogDebugWithMessageProperties()
247270
{
248271
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Debug, default(EventId), new Dictionary<string, object> { { "ParameterCount", "1" } }, null, (s,ex) => "message with id and 1 property");
249272
}
250273

274+
public void LogDebugWithMessagePropertiesList()
275+
{
276+
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Debug, default(EventId), new List<KeyValuePair<string, object>>(new[] { new KeyValuePair<string, object>("ParameterCount", "1") }), null, (s, ex) => "message with id and 1 property");
277+
}
278+
251279
public void LogWithScope()
252280
{
253281
using (_logger.BeginScope("scope1"))

0 commit comments

Comments
 (0)