Skip to content

Commit 037c866

Browse files
[Telemetry, Logging] Emit {OriginalFormat} as the last property of the log record (#6111)
* Fixes #5720 Now {OriginalFormat} is emitted as the last item in the TagArray * Fixes #5720 Adjusts ExtendedLogger to always return {OriginalFormat} as the last tag in the list * Fixes #5720 Fixes ResourceMonitoring tests * Fixes #5720 Address PR comments
1 parent a63d9bb commit 037c866

12 files changed

+279
-65
lines changed

src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs

+10-7
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ private void GenLogMethod(LoggingMethod lm)
102102
OutLn($"[{GeneratorUtilities.GeneratedCodeAttribute}] static string ({lambdaStateName}, {exceptionLambdaName}) =>");
103103
OutOpenBrace();
104104

105-
if (GenVariableAssignments(lm, lambdaStateName, numReservedUnclassifiedTags, numReservedClassifiedTags))
105+
if (!string.IsNullOrEmpty(lm.Message) &&
106+
GenVariableAssignments(lm, lambdaStateName, numReservedUnclassifiedTags, numReservedClassifiedTags))
106107
{
107108
var mapped = Parsing.TemplateProcessor.MapTemplates(lm.Message, t =>
108109
{
@@ -318,7 +319,14 @@ void GenTagWrites(LoggingMethod lm, string stateName, out int numReservedUnclass
318319
{
319320
OutLn();
320321
OutLn($"_ = {stateName}.ReserveTagSpace({numReservedUnclassifiedTags});");
322+
321323
int count = numReservedUnclassifiedTags;
324+
325+
if (!string.IsNullOrEmpty(lm.Message))
326+
{
327+
OutLn($"{stateName}.TagArray[{--count}] = new(\"{{OriginalFormat}}\", {EscapeMessageString(lm.Message)});");
328+
}
329+
322330
foreach (var p in lm.Parameters)
323331
{
324332
if (NeedsASlot(p) && !p.HasDataClassification)
@@ -367,11 +375,6 @@ void GenTagWrites(LoggingMethod lm, string stateName, out int numReservedUnclass
367375
});
368376
}
369377
}
370-
371-
if (!string.IsNullOrEmpty(lm.Message))
372-
{
373-
OutLn($"{stateName}.TagArray[{--count}] = new(\"{{OriginalFormat}}\", {EscapeMessageString(lm.Message)});");
374-
}
375378
}
376379

377380
if (numReservedClassifiedTags > 0)
@@ -516,7 +519,7 @@ bool GenVariableAssignments(LoggingMethod lm, string lambdaStateName, int numRes
516519
{
517520
bool generatedAssignments = false;
518521

519-
int index = numReservedUnclassifiedTags - 1;
522+
int index = numReservedUnclassifiedTags - 2;
520523
foreach (var p in lm.Parameters)
521524
{
522525
if (NeedsASlot(p) && !p.HasDataClassification)

src/Libraries/Microsoft.Extensions.Telemetry/Logging/ExtendedLogger.LegacyTagJoiner.cs

+17-5
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,29 @@ public void SetIncomingTags(IReadOnlyList<KeyValuePair<string, object?>> value)
5050
{
5151
get
5252
{
53-
if (index < _incomingTagCount)
53+
int staticTagsCount = StaticTags!.Length;
54+
int extraTagsCount = _extraTags.Count;
55+
56+
if (index < staticTagsCount)
5457
{
55-
return _incomingTags![index];
58+
return StaticTags[index];
5659
}
57-
else if (index < _incomingTagCount + _extraTags.Count)
60+
61+
// Iterating over "_extraTags" and "_incomingTags" at the end, because they may contain
62+
// the "{OriginalFormat}" property which needs to be the last tag in the list. The order
63+
// "_extraTags" then "_incomingTags" is important because:
64+
// 1. In the case when the "{OriginalFormat}" property is in "_extraTags",
65+
// "_incomingTags" is always empty.
66+
// 2. In the case when the "{OriginalFormat}" property is in "_incomingTags",
67+
// "_extraTags" might contain other tags and we want them to be returned
68+
// before "_incomingTags".
69+
else if (index < staticTagsCount + extraTagsCount)
5870
{
59-
return _extraTags[index - _incomingTagCount];
71+
return _extraTags[index - staticTagsCount];
6072
}
6173
else
6274
{
63-
return StaticTags![index - _incomingTagCount - _extraTags.Count];
75+
return _incomingTags![index - staticTagsCount - extraTagsCount];
6476
}
6577
}
6678
}

src/Libraries/Microsoft.Extensions.Telemetry/Logging/ExtendedLogger.ModernTagJoiner.cs

+13-7
Original file line numberDiff line numberDiff line change
@@ -56,21 +56,27 @@ public void SetIncomingTags(LoggerMessageState value)
5656
{
5757
get
5858
{
59-
if (index < _incomingTagsCount)
59+
int staticTagsCount = StaticTags!.Length;
60+
int extraTagsCount = _extraTags.Count;
61+
62+
if (index < _redactedTagsCount)
6063
{
61-
return _incomingTags![index];
64+
return _redactedTags![index];
6265
}
63-
else if (index < _incomingTagsCount + _redactedTagsCount)
66+
else if (index < _redactedTagsCount + extraTagsCount)
6467
{
65-
return _redactedTags![index - _incomingTagsCount];
68+
return _extraTags[index - _redactedTagsCount];
6669
}
67-
else if (index < _incomingTagsCount + _redactedTagsCount + _extraTags.Count)
70+
else if (index < _redactedTagsCount + extraTagsCount + staticTagsCount)
6871
{
69-
return _extraTags[index - _incomingTagsCount - _redactedTagsCount];
72+
return StaticTags[index - _redactedTagsCount - extraTagsCount];
7073
}
74+
75+
// Iterating over "_incomingTags" at the end, because it may contain the
76+
// "{OriginalFormat}" property which needs to be the last tag in the list.
7177
else
7278
{
73-
return StaticTags![index - _incomingTagsCount - _redactedTagsCount - _extraTags.Count];
79+
return _incomingTags![index - _redactedTagsCount - extraTagsCount - staticTagsCount];
7480
}
7581
}
7682
}

src/Libraries/Microsoft.Extensions.Telemetry/Logging/ExtendedLogger.cs

+19-19
Original file line numberDiff line numberDiff line change
@@ -322,24 +322,6 @@ private void LegacyPath<TState>(LogLevel logLevel, EventId eventId, TState state
322322
joiner.Formatter = formatter;
323323
joiner.State = state;
324324

325-
switch (state)
326-
{
327-
case IReadOnlyList<KeyValuePair<string, object?>> stateList:
328-
joiner.SetIncomingTags(stateList);
329-
break;
330-
331-
case IEnumerable<KeyValuePair<string, object?>> stateList:
332-
joiner.EnrichmentTagCollector.AddRange(stateList);
333-
break;
334-
335-
case null:
336-
break;
337-
338-
default:
339-
joiner.EnrichmentTagCollector.Add("{OriginalFormat}", state);
340-
break;
341-
}
342-
343325
List<Exception>? exceptions = null;
344326

345327
// enrich
@@ -356,12 +338,30 @@ private void LegacyPath<TState>(LogLevel logLevel, EventId eventId, TState state
356338
}
357339
}
358340

359-
// one last dedicated bit of enrichment
341+
// enrich log data with exception information
360342
if (exception != null)
361343
{
362344
RecordException(exception, joiner.EnrichmentTagCollector, config);
363345
}
364346

347+
switch (state)
348+
{
349+
case IReadOnlyList<KeyValuePair<string, object?>> stateList:
350+
joiner.SetIncomingTags(stateList);
351+
break;
352+
353+
case IEnumerable<KeyValuePair<string, object?>> stateList:
354+
joiner.EnrichmentTagCollector.AddRange(stateList);
355+
break;
356+
357+
case null:
358+
break;
359+
360+
default:
361+
joiner.EnrichmentTagCollector.Add("{OriginalFormat}", state);
362+
break;
363+
}
364+
365365
bool? samplingDecision = null;
366366
for (int i = 0; i < loggers.Length; i++)
367367
{

test/Generators/Microsoft.Gen.Logging/Generated/LogMethodTests.cs

+40
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Diagnostics.CodeAnalysis;
77
using System.Globalization;
8+
using System.Linq;
89
using System.Threading;
910
using Microsoft.Extensions.Logging;
1011
using Microsoft.Extensions.Logging.Testing;
@@ -923,6 +924,34 @@ public void FormattableTest()
923924
Assert.Equal("Converted!", collector.LatestRecord.StructuredState!.GetValue("p1"));
924925
}
925926

927+
[Fact]
928+
public void OriginalFormatTest()
929+
{
930+
using var logger = Utils.GetLogger();
931+
var collector = logger.FakeLogCollector;
932+
933+
OriginalFormatTestExtensions.M0(logger);
934+
Assert.Equal(1, collector.Count);
935+
EnsureOriginalFormatIsLastProperty(collector.LatestRecord, "M0", "M0");
936+
937+
collector.Clear();
938+
OriginalFormatTestExtensions.M1(logger, "0");
939+
Assert.Equal(1, collector.Count);
940+
EnsureOriginalFormatIsLastProperty(collector.LatestRecord, "M1 {p0}", "M1 0");
941+
942+
collector.Clear();
943+
OriginalFormatTestExtensions.M2(logger, "0", "1");
944+
Assert.Equal(1, collector.Count);
945+
EnsureOriginalFormatIsLastProperty(collector.LatestRecord, "M2 {p0}{p1}", "M2 01");
946+
947+
collector.Clear();
948+
OriginalFormatTestExtensions.M3(logger, "0", "1", "2");
949+
Assert.Equal(1, collector.Count);
950+
Assert.Empty(collector.LatestRecord.Message);
951+
Assert.Equal(3, collector.LatestRecord.StructuredState!.Count);
952+
AssertLastState(collector, new("p0", "0"), new("p1", "1"), new("p2", "2"));
953+
}
954+
926955
private static void AssertLastState(FakeLogCollector collector, params KeyValuePair<string, string?>[] expected)
927956
{
928957
var rol = (IReadOnlyList<KeyValuePair<string, string>>)collector.LatestRecord.State!;
@@ -949,4 +978,15 @@ private static void TestCollection(int expected, FakeLogCollector collector)
949978
}
950979
}
951980
}
981+
982+
private static void EnsureOriginalFormatIsLastProperty(FakeLogRecord record, string format, string message)
983+
{
984+
var state = (IReadOnlyList<KeyValuePair<string, string>>)record.State!;
985+
Assert.NotNull(state);
986+
987+
var pair = state.Last();
988+
Assert.Equal("{OriginalFormat}", pair.Key);
989+
Assert.Equal(format, pair.Value);
990+
Assert.Equal(message, record.Message);
991+
}
952992
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace TestClasses
7+
{
8+
internal static partial class OriginalFormatTestExtensions
9+
{
10+
[LoggerMessage(0, LogLevel.Information, "M0")]
11+
public static partial void M0(ILogger logger);
12+
13+
[LoggerMessage(1, LogLevel.Information, "M1 {p0}")]
14+
public static partial void M1(ILogger logger, string p0);
15+
16+
[LoggerMessage(2, LogLevel.Information, "M2 {p0}{p1}")]
17+
public static partial void M2(ILogger logger, string p0, string p1);
18+
19+
[LoggerMessage(LogLevel.Information)]
20+
public static partial void M3(ILogger logger, string p0, string p1, string p2);
21+
}
22+
}

test/Generators/Microsoft.Gen.Logging/Verified/ParserTests.ClassWithNullableProperty.verified.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ namespace Test
2323
var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState;
2424

2525
_ = state.ReserveTagSpace(3);
26-
state.TagArray[2] = new("classWithNullablePropertyParam.NullableDateTime", classWithNullablePropertyParam?.NullableDateTime);
27-
state.TagArray[1] = new("classWithNullablePropertyParam.NonNullableDateTime", classWithNullablePropertyParam?.NonNullableDateTime);
28-
state.TagArray[0] = new("{OriginalFormat}", "Testing nullable property within class here...");
26+
state.TagArray[2] = new("{OriginalFormat}", "Testing nullable property within class here...");
27+
state.TagArray[1] = new("classWithNullablePropertyParam.NullableDateTime", classWithNullablePropertyParam?.NullableDateTime);
28+
state.TagArray[0] = new("classWithNullablePropertyParam.NonNullableDateTime", classWithNullablePropertyParam?.NonNullableDateTime);
2929

3030
logger.Log(
3131
global::Microsoft.Extensions.Logging.LogLevel.Information,

test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxUtilizationProviderTests.Provider_EmitsLogRecord.verified.txt

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
Name: SystemResourcesInfo
77
},
88
State: [
9-
{
10-
{OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
11-
},
129
{
1310
memoryRequest: 1048576
1411
},
@@ -20,12 +17,12 @@
2017
},
2118
{
2219
cpuLimit: 20
20+
},
21+
{
22+
{OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
2323
}
2424
],
2525
StructuredState: [
26-
{
27-
{OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
28-
},
2926
{
3027
memoryRequest: 1048576
3128
},
@@ -37,6 +34,9 @@
3734
},
3835
{
3936
cpuLimit: 20
37+
},
38+
{
39+
{OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
4040
}
4141
],
4242
Message: System resources information: CpuLimit = 20, CpuRequest = 0.078125, MemoryLimit = 1048576, MemoryRequest = 1048576.,

test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Verified/ResourceMonitoringServiceTests.ResourceUtilizationTracker_LogsSnapshotInformation.verified.txt

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
Name: SnapshotReceived
66
},
77
State: [
8-
{
9-
{OriginalFormat}: Snapshot received: TotalTimeSinceStart={totalTimeSinceStart}, KernelTimeSinceStart={kernelTimeSinceStart}, UserTimeSinceStart={userTimeSinceStart}, MemoryUsageInBytes={memoryUsageInBytes}.
10-
},
118
{
129
memoryUsageInBytes: 500
1310
},
@@ -19,12 +16,12 @@
1916
},
2017
{
2118
totalTimeSinceStart: 730119.00:00:00
19+
},
20+
{
21+
{OriginalFormat}: Snapshot received: TotalTimeSinceStart={totalTimeSinceStart}, KernelTimeSinceStart={kernelTimeSinceStart}, UserTimeSinceStart={userTimeSinceStart}, MemoryUsageInBytes={memoryUsageInBytes}.
2222
}
2323
],
2424
StructuredState: [
25-
{
26-
{OriginalFormat}: Snapshot received: TotalTimeSinceStart={totalTimeSinceStart}, KernelTimeSinceStart={kernelTimeSinceStart}, UserTimeSinceStart={userTimeSinceStart}, MemoryUsageInBytes={memoryUsageInBytes}.
27-
},
2825
{
2926
memoryUsageInBytes: 500
3027
},
@@ -36,6 +33,9 @@
3633
},
3734
{
3835
totalTimeSinceStart: 730119.00:00:00
36+
},
37+
{
38+
{OriginalFormat}: Snapshot received: TotalTimeSinceStart={totalTimeSinceStart}, KernelTimeSinceStart={kernelTimeSinceStart}, UserTimeSinceStart={userTimeSinceStart}, MemoryUsageInBytes={memoryUsageInBytes}.
3939
}
4040
],
4141
Message: Snapshot received: TotalTimeSinceStart=730119.00:00:00, KernelTimeSinceStart=00:00:01, UserTimeSinceStart=00:00:01, MemoryUsageInBytes=500.,

test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Verified/WindowsContainerSnapshotProviderTests.SnapshotProvider_EmitsLogRecord.DotNet.verified.txt

+6-6
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@
2727
Name: SystemResourcesInfo
2828
},
2929
State: [
30-
{
31-
{OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
32-
},
3330
{
3431
memoryRequest: 2000
3532
},
@@ -41,12 +38,12 @@
4138
},
4239
{
4340
cpuLimit: 1
41+
},
42+
{
43+
{OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
4444
}
4545
],
4646
StructuredState: [
47-
{
48-
{OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
49-
},
5047
{
5148
memoryRequest: 2000
5249
},
@@ -58,6 +55,9 @@
5855
},
5956
{
6057
cpuLimit: 1
58+
},
59+
{
60+
{OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
6161
}
6262
],
6363
Message: System resources information: CpuLimit = 1, CpuRequest = 1, MemoryLimit = 2000, MemoryRequest = 2000.,

0 commit comments

Comments
 (0)