Skip to content

Commit 3195fbb

Browse files
authored
Auto generate Event Ids in the logging source gen (#87892)
1 parent d2e8963 commit 3195fbb

File tree

4 files changed

+87
-10
lines changed

4 files changed

+87
-10
lines changed

src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
103103
IMethodSymbol logMethodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken)!;
104104
Debug.Assert(logMethodSymbol != null, "log method is present.");
105105
(int eventId, int? level, string message, string? eventName, bool skipEnabledCheck) = (-1, null, string.Empty, null, false);
106+
bool suppliedEventId = false;
106107

107108
foreach (AttributeListSyntax mal in method.AttributeLists)
108109
{
@@ -160,19 +161,21 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
160161
message = string.Empty;
161162
level = items[0].IsNull ? null : (int?)GetItem(items[0]);
162163
}
163-
eventId = -1;
164164
break;
165165

166166
case 2:
167167
// LoggerMessageAttribute(LogLevel level, string message)
168-
eventId = -1;
169168
level = items[0].IsNull ? null : (int?)GetItem(items[0]);
170169
message = items[1].IsNull ? string.Empty : (string)GetItem(items[1]);
171170
break;
172171

173172
case 3:
174173
// LoggerMessageAttribute(int eventId, LogLevel level, string message)
175-
eventId = items[0].IsNull ? -1 : (int)GetItem(items[0]);
174+
if (!items[0].IsNull)
175+
{
176+
suppliedEventId = true;
177+
eventId = (int)GetItem(items[0]);
178+
}
176179
level = items[1].IsNull ? null : (int?)GetItem(items[1]);
177180
message = items[2].IsNull ? string.Empty : (string)GetItem(items[2]);
178181
break;
@@ -202,6 +205,7 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
202205
{
203206
case "EventId":
204207
eventId = (int)GetItem(value);
208+
suppliedEventId = true;
205209
break;
206210
case "Level":
207211
level = value.IsNull ? null : (int?)GetItem(value);
@@ -227,6 +231,11 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
227231
break;
228232
}
229233

234+
if (!suppliedEventId)
235+
{
236+
eventId = GetNonRandomizedHashCode(string.IsNullOrWhiteSpace(eventName) ? logMethodSymbol.Name : eventName);
237+
}
238+
230239
var lm = new LoggerMethod
231240
{
232241
Name = logMethodSymbol.Name,
@@ -298,8 +307,8 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
298307
}
299308

300309
// ensure there are no duplicate event ids.
301-
// LoggerMessageAttribute has constructors that don't take an EventId, we need to exclude the default Id -1 from duplication checks.
302-
if (lm.EventId != -1 && !eventIds.Add(lm.EventId))
310+
// We don't check Id duplication for the auto-generated event id.
311+
if (suppliedEventId && !eventIds.Add(lm.EventId))
303312
{
304313
Diag(DiagnosticDescriptors.ShouldntReuseEventIds, ma.GetLocation(), lm.EventId, classDec.Identifier.Text);
305314
}
@@ -807,5 +816,19 @@ internal sealed class LoggerParameter
807816
// but instead is supposed to be taken as a parameter for the template.
808817
public bool IsTemplateParameter => !IsLogger && !IsException && !IsLogLevel;
809818
}
819+
820+
/// <summary>
821+
/// Returns a non-randomized hash code for the given string.
822+
/// We always return a positive value.
823+
/// </summary>
824+
internal static int GetNonRandomizedHashCode(string s)
825+
{
826+
uint result = 2166136261u;
827+
foreach (char c in s)
828+
{
829+
result = (c ^ result) * 16777619;
830+
}
831+
return Math.Abs((int)result);
832+
}
810833
}
811834
}

src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDefaultValues.generated.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
4949
{
5050
logger.Log(
5151
level,
52-
new global::Microsoft.Extensions.Logging.EventId(-1, nameof(M0)),
52+
new global::Microsoft.Extensions.Logging.EventId(316638712, nameof(M0)),
5353
new __M0Struct(),
5454
null,
5555
__M0Struct.Format);

src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public void MessageTests()
209209
Assert.Null(logger.LastException);
210210
Assert.Equal(string.Empty, logger.LastFormattedString);
211211
Assert.Equal(LogLevel.Trace, logger.LastLogLevel);
212-
Assert.Equal(-1, logger.LastEventId.Id);
212+
Assert.Equal(400_526_807, logger.LastEventId.Id);
213213
Assert.Equal(1, logger.CallCount);
214214

215215
logger.Reset();
@@ -269,40 +269,80 @@ public void InstanceTests()
269269
var logger = new MockLogger();
270270
var o = new TestInstances(logger);
271271

272+
// [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M0")]
272273
logger.Reset();
273274
o.M0();
274275
Assert.Null(logger.LastException);
275276
Assert.Equal("M0", logger.LastFormattedString);
276277
Assert.Equal(LogLevel.Error, logger.LastLogLevel);
277278
Assert.Equal(1, logger.CallCount);
278279

280+
// [LoggerMessage(EventId = 1, Level = LogLevel.Trace, Message = "M1 {p1}")]
279281
logger.Reset();
280282
o.M1("Foo");
281283
Assert.Null(logger.LastException);
282284
Assert.Equal("M1 Foo", logger.LastFormattedString);
283285
Assert.Equal(LogLevel.Trace, logger.LastLogLevel);
284286
Assert.Equal(1, logger.CallCount);
285287

288+
// [LoggerMessage(LogLevel.Information, "M2 {p1}")]
286289
logger.Reset();
287290
o.M2("Bar");
288291
Assert.Null(logger.LastException);
289292
Assert.Equal("M2 Bar", logger.LastFormattedString);
290293
Assert.Equal(LogLevel.Information, logger.LastLogLevel);
291-
Assert.Equal(-1, logger.LastEventId.Id);
294+
Assert.Equal(350_193_950, logger.LastEventId.Id);
292295

296+
// [LoggerMessage("M3 {p1}")]
293297
logger.Reset();
294298
o.M3(LogLevel.Critical, "Foo Bar");
295299
Assert.Null(logger.LastException);
296300
Assert.Equal("M3 Foo Bar", logger.LastFormattedString);
297301
Assert.Equal(LogLevel.Critical, logger.LastLogLevel);
298-
Assert.Equal(-1, logger.LastEventId.Id);
302+
Assert.Equal(366_971_569, logger.LastEventId.Id);
299303

304+
// [LoggerMessage(LogLevel.Debug)]
300305
logger.Reset();
301306
o.M4();
302307
Assert.Null(logger.LastException);
303308
Assert.Equal("", logger.LastFormattedString);
304309
Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
305-
Assert.Equal(-1, logger.LastEventId.Id);
310+
Assert.Equal(383_749_188, logger.LastEventId.Id);
311+
312+
// [LoggerMessage(level: LogLevel.Warning, message: "custom message {v}", eventId: 12341)]
313+
logger.Reset();
314+
o.M5("Hello");
315+
Assert.Null(logger.LastException);
316+
Assert.Equal("custom message Hello", logger.LastFormattedString);
317+
Assert.Equal(LogLevel.Warning, logger.LastLogLevel);
318+
Assert.Equal(12341, logger.LastEventId.Id);
319+
320+
// [LoggerMessage(EventName = "My Event Name", Level = LogLevel.Information, Message = "M6 - {p1}")]
321+
logger.Reset();
322+
o.M6("Generate event Id");
323+
Assert.Null(logger.LastException);
324+
Assert.Equal("M6 - Generate event Id", logger.LastFormattedString);
325+
Assert.Equal(LogLevel.Information, logger.LastLogLevel);
326+
Assert.Equal("My Event Name", logger.LastEventId.Name);
327+
Assert.Equal(26_601_394, logger.LastEventId.Id);
328+
329+
// [LoggerMessage(Level = LogLevel.Warning, Message = "M7 - {p1}")]
330+
logger.Reset();
331+
o.M7("Generate event Id");
332+
Assert.Null(logger.LastException);
333+
Assert.Equal("M7 - Generate event Id", logger.LastFormattedString);
334+
Assert.Equal(LogLevel.Warning, logger.LastLogLevel);
335+
Assert.Equal("M7", logger.LastEventId.Name);
336+
Assert.Equal(434_082_045, logger.LastEventId.Id);
337+
338+
// [LoggerMessage(EventId = 100, Level = LogLevel.Warning, Message = "M8 - {p1}")]
339+
logger.Reset();
340+
o.M8("Generate event Id");
341+
Assert.Null(logger.LastException);
342+
Assert.Equal("M8 - Generate event Id", logger.LastFormattedString);
343+
Assert.Equal(LogLevel.Warning, logger.LastLogLevel);
344+
Assert.Equal("M8", logger.LastEventId.Name);
345+
Assert.Equal(100, logger.LastEventId.Id);
306346
}
307347

308348
[Fact]

src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,19 @@ public TestInstances(ILogger logger)
2828

2929
[LoggerMessage(LogLevel.Debug)]
3030
public partial void M4();
31+
32+
// Test with named parameters
33+
[LoggerMessage(level: LogLevel.Warning, message: "custom message {v}", eventId: 12341)]
34+
public partial void M5(string v);
35+
36+
// Test auto-generated EventId
37+
[LoggerMessage(EventName = "My Event Name", Level = LogLevel.Information, Message = "M6 - {p1}")]
38+
public partial void M6(string p1);
39+
40+
[LoggerMessage(Level = LogLevel.Warning, Message = "M7 - {p1}")]
41+
public partial void M7(string p1);
42+
43+
[LoggerMessage(EventId = 100, Level = LogLevel.Warning, Message = "M8 - {p1}")]
44+
public partial void M8(string p1);
3145
}
3246
}

0 commit comments

Comments
 (0)