Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Two bugfixes for logging generator #51963

Merged
merged 2 commits into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ internal class Emitter
private const int MaxLoggerMessageDefineArguments = 6;
private const int DefaultStringBuilderCapacity = 1024;

private readonly string _generatedCodeAttribute =
private static readonly string s_generatedCodeAttribute =
$"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" +
$"\"{typeof(Emitter).Assembly.GetName().Name}\", " +
$"\"{typeof(Emitter).Assembly.GetName().Version}\")";
private readonly StringBuilder _builder = new StringBuilder(DefaultStringBuilderCapacity);
private bool _needEnumerationHelper;

public string Emit(IReadOnlyList<LoggerClass> logClasses, CancellationToken cancellationToken)
{
Expand All @@ -34,6 +35,7 @@ public string Emit(IReadOnlyList<LoggerClass> logClasses, CancellationToken canc
GenType(lc);
}

GenEnumerationHelper();
return _builder.ToString();
}

Expand All @@ -47,10 +49,10 @@ private static bool UseLoggerMessageDefine(LoggerMethod lm)
if (result)
{
// make sure the order of the templates matches the order of the logging method parameter
int count = 0;
foreach (string t in lm.TemplateList)
for (int i = 0; i < lm.TemplateList.Count; i++)
{
if (!t.Equals(lm.TemplateParameters[count].Name, StringComparison.OrdinalIgnoreCase))
string t = lm.TemplateList[i];
if (!t.Equals(lm.TemplateParameters[i].Name, StringComparison.OrdinalIgnoreCase))
{
// order doesn't match, can't use LoggerMessage.Define
return false;
Expand Down Expand Up @@ -84,8 +86,6 @@ partial class {lc.Name} {lc.Constraints}
GenLogMethod(lm);
}

GenEnumerationHelper(lc);

_builder.Append($@"
}}");

Expand All @@ -99,7 +99,7 @@ partial class {lc.Name} {lc.Constraints}
private void GenStruct(LoggerMethod lm)
{
_builder.AppendLine($@"
[{_generatedCodeAttribute}]
[{s_generatedCodeAttribute}]
private readonly struct __{lm.Name}Struct : global::System.Collections.Generic.IReadOnlyList<global::System.Collections.Generic.KeyValuePair<string, object?>>
{{");
GenFields(lm);
Expand Down Expand Up @@ -193,7 +193,9 @@ private void GenVariableAssignments(LoggerMethod lm)
if (lm.TemplateParameters[index].IsEnumerable)
{
_builder.AppendLine($" var {t.Key} = "
+ $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.TemplateParameters[index].Name});");
+ $"global::__LoggerMessageGenerator.Enumerate((global::System.Collections.IEnumerable ?)this._{lm.TemplateParameters[index].Name});");

_needEnumerationHelper = true;
}
else
{
Expand Down Expand Up @@ -237,7 +239,7 @@ private void GenDefineTypes(LoggerMethod lm, bool brackets)
}
if (brackets)
{
_builder.Append("<");
_builder.Append('<');
}

bool firstItem = true;
Expand All @@ -257,7 +259,7 @@ private void GenDefineTypes(LoggerMethod lm, bool brackets)

if (brackets)
{
_builder.Append(">");
_builder.Append('>');
}
else
{
Expand Down Expand Up @@ -322,15 +324,15 @@ private void GenHolder(LoggerMethod lm)
private void GenLogMethod(LoggerMethod lm)
{
string level = GetLogLevel(lm);
string extension = (lm.IsExtensionMethod ? "this " : string.Empty);
string extension = lm.IsExtensionMethod ? "this " : string.Empty;
string eventName = string.IsNullOrWhiteSpace(lm.EventName) ? $"nameof({lm.Name})" : $"\"{lm.EventName}\"";
string exceptionArg = GetException(lm);
string logger = GetLogger(lm);

if (UseLoggerMessageDefine(lm))
{
_builder.Append($@"
[{_generatedCodeAttribute}]
[{s_generatedCodeAttribute}]
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, ");

GenDefineTypes(lm, brackets: false);
Expand All @@ -345,7 +347,7 @@ private void GenLogMethod(LoggerMethod lm)
}

_builder.Append($@"
[{_generatedCodeAttribute}]
[{s_generatedCodeAttribute}]
{lm.Modifiers} void {lm.Name}({extension}");

GenParameters(lm);
Expand Down Expand Up @@ -443,63 +445,56 @@ static string GetLogLevel(LoggerMethod lm)
}
}

private void GenEnumerationHelper(LoggerClass lc)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enumeration helper is either generated (only once), or not at all into __LoggerMessageGenerator

private void GenEnumerationHelper()
{
foreach (LoggerMethod lm in lc.Methods)
if (_needEnumerationHelper)
{
if (UseLoggerMessageDefine(lm))
{
foreach (LoggerParameter p in lm.TemplateParameters)
{
if (p.IsEnumerable)
{
_builder.Append($@"
[{_generatedCodeAttribute}]
private static string __Enumerate(global::System.Collections.IEnumerable? enumerable)
[{s_generatedCodeAttribute}]
internal static class __LoggerMessageGenerator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just thinking out loud here, but I almost wonder if this deserves to be a public API in our library instead of generating this code into the user's project.

I started by thinking "is putting this in the global namespace the right thing to do?" And then that quickly led to "why are we generating this code when it never changes based on the user's types?"

Copy link
Member Author

@maryamariyan maryamariyan Apr 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the very first API review proposal (Refer to comment #36022 (comment)), we proposed this static ArgumentFormatter.Enumerate(..) helper method which we said would only be used in the generated code.

The consensus back then was for such cases to just generate them if/when needed.

{{
public static string Enumerate(global::System.Collections.IEnumerable? enumerable)
{{
if (enumerable == null)
{{
if (enumerable == null)
{{
return ""(null)"";
}}
return ""(null)"";
}}

var sb = new global::System.Text.StringBuilder();
_ = sb.Append('[');
var sb = new global::System.Text.StringBuilder();
_ = sb.Append('[');

bool first = true;
foreach (object e in enumerable)
bool first = true;
foreach (object e in enumerable)
{{
if (!first)
{{
if (!first)
{{
_ = sb.Append("", "");
}}
_ = sb.Append("", "");
}}

if (e == null)
if (e == null)
{{
_ = sb.Append(""(null)"");
}}
else
{{
if (e is global::System.IFormattable fmt)
{{
_ = sb.Append(""(null)"");
_ = sb.Append(fmt.ToString(null, global::System.Globalization.CultureInfo.InvariantCulture));
}}
else
{{
if (e is global::System.IFormattable fmt)
{{
_ = sb.Append(fmt.ToString(null, global::System.Globalization.CultureInfo.InvariantCulture));
}}
else
{{
_ = sb.Append(e);
}}
_ = sb.Append(e);
}}

first = false;
}}

_ = sb.Append(']');

return sb.ToString();
first = false;
}}
");
}
}
}

_ = sb.Append(']');

return sb.ToString();
}}
}}");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void Execute(GeneratorExecutionContext context)
var e = new Emitter();
string result = e.Emit(logClasses, context.CancellationToken);

context.AddSource(nameof(LoggerMessageGenerator), SourceText.From(result, Encoding.UTF8));
context.AddSource("LoggerMessage", SourceText.From(result, Encoding.UTF8));
Copy link
Member Author

@maryamariyan maryamariyan Apr 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just so the generated file name would become LoggerMessage.cs instead of LoggerMessageGenerator.cs

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
private readonly global::System.Int32 _p4;
private readonly global::System.Int32 _p5;
private readonly global::System.Int32 _p6;
private readonly global::System.Int32 _p7;
private readonly global::System.Collections.Generic.IEnumerable<global::System.Int32> _p7;

public __Method9Struct(global::System.Int32 p1, global::System.Int32 p2, global::System.Int32 p3, global::System.Int32 p4, global::System.Int32 p5, global::System.Int32 p6, global::System.Int32 p7)
public __Method9Struct(global::System.Int32 p1, global::System.Int32 p2, global::System.Int32 p3, global::System.Int32 p4, global::System.Int32 p5, global::System.Int32 p6, global::System.Collections.Generic.IEnumerable<global::System.Int32> p7)
{
this._p1 = p1;
this._p2 = p2;
Expand All @@ -36,7 +36,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
var p4 = this._p4;
var p5 = this._p5;
var p6 = this._p6;
var p7 = this._p7;
var p7 = global::__LoggerMessageGenerator.Enumerate((global::System.Collections.IEnumerable ?)this._p7);

return $"M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}";
}
Expand Down Expand Up @@ -74,7 +74,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
}

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")]
public static partial void Method9(global::Microsoft.Extensions.Logging.ILogger logger, global::System.Int32 p1, global::System.Int32 p2, global::System.Int32 p3, global::System.Int32 p4, global::System.Int32 p5, global::System.Int32 p6, global::System.Int32 p7)
public static partial void Method9(global::Microsoft.Extensions.Logging.ILogger logger, global::System.Int32 p1, global::System.Int32 p2, global::System.Int32 p3, global::System.Int32 p4, global::System.Int32 p5, global::System.Int32 p6, global::System.Collections.Generic.IEnumerable<global::System.Int32> p7)
{
if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error))
{
Expand All @@ -87,4 +87,49 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
}
}
}
}
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")]
internal static class __LoggerMessageGenerator
{
public static string Enumerate(global::System.Collections.IEnumerable? enumerable)
{
if (enumerable == null)
{
return "(null)";
}

var sb = new global::System.Text.StringBuilder();
_ = sb.Append('[');

bool first = true;
foreach (object e in enumerable)
{
if (!first)
{
_ = sb.Append(", ");
}

if (e == null)
{
_ = sb.Append("(null)");
}
else
{
if (e is global::System.IFormattable fmt)
{
_ = sb.Append(fmt.ToString(null, global::System.Globalization.CultureInfo.InvariantCulture));
}
else
{
_ = sb.Append(e);
}
}

first = false;
}

_ = sb.Append(']');

return sb.ToString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@

namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
{
partial class TestWithOneParam
partial class TestWithTwoParams
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")]
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, global::System.Int32, global::System.Exception?> __M0Callback =
global::Microsoft.Extensions.Logging.LoggerMessage.Define<global::System.Int32>(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {A1}", true);
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, global::System.Int32, global::System.Collections.Generic.IEnumerable<global::System.Int32>, global::System.Exception?> __M0Callback =
global::Microsoft.Extensions.Logging.LoggerMessage.Define<global::System.Int32, global::System.Collections.Generic.IEnumerable<global::System.Int32>>(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {a1} {a2}", true);

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")]
public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger, global::System.Int32 a1)
public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger, global::System.Int32 a1, global::System.Collections.Generic.IEnumerable<global::System.Int32> a2)
{
if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error))
{
__M0Callback(logger, a1, null);
__M0Callback(logger, a1, a2, null);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,18 @@ public async Task TestEmitter()
}

[Fact]
public async Task TestBaseline_TestWithOneParam_Success()
public async Task TestBaseline_TestWithTwoParams_Success()
{
string testSourceCode = @"
namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
{
internal static partial class TestWithOneParam
internal static partial class TestWithTwoParams
{
[LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = ""M0 {A1}"")]
public static partial void M0(ILogger logger, int a1);
[LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = ""M0 {a1} {a2}"")]
public static partial void M0(ILogger logger, int a1, System.Collections.Generic.IEnumerable<int> a2);
}
}";
await VerifyAgainstBaselineUsingFile("TestWithOneParam.generated.txt", testSourceCode);
await VerifyAgainstBaselineUsingFile("TestWithTwoParams.generated.txt", testSourceCode);
}

[Fact]
Expand All @@ -59,7 +59,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
internal static partial class TestWithMoreThan6Params
{
[LoggerMessage(EventId = 8, Level = LogLevel.Error, Message = ""M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"")]
public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7);
public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, System.Collections.Generic.IEnumerable<int> p7);
}
}";
await VerifyAgainstBaselineUsingFile("TestWithMoreThan6Params.generated.txt", testSourceCode);
Expand Down