Add LoggingGenerator (#51064)
authorMaryam Ariyan <maryam.ariyan@microsoft.com>
Fri, 16 Apr 2021 17:36:13 +0000 (13:36 -0400)
committerGitHub <noreply@github.com>
Fri, 16 Apr 2021 17:36:13 +0000 (10:36 -0700)
48 files changed:
docs/project/list-of-diagnostics.md [moved from docs/project/list-of-obsoletions.md with 55% similarity]
eng/Versions.props
src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/Microsoft.Extensions.Logging.sln
src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/pkg/Microsoft.Extensions.Logging.pkgproj
src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs
src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/README.md [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs [new file with mode: 0644]
src/libraries/src.proj
src/libraries/tests.proj

similarity index 55%
rename from docs/project/list-of-obsoletions.md
rename to docs/project/list-of-diagnostics.md
index cb09467..df1f73e 100644 (file)
@@ -26,4 +26,32 @@ Currently the identifiers `SYSLIB0001` through `SYSLIB0999` are carved out for o
 |  __`SYSLIB0011`__ | `BinaryFormatter` serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for recommended alternatives. |
 |  __`SYSLIB0012`__ | Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location instead. |
 |  __`SYSLIB0013`__ | Uri.EscapeUriString can corrupt the Uri string in some cases. Consider using Uri.EscapeDataString for query string components instead. |
-|  __`SYSLIB0015`__ | DisablePrivateReflectionAttribute has no effect in .NET 6.0+ applications. |
\ No newline at end of file
+|  __`SYSLIB0015`__ | DisablePrivateReflectionAttribute has no effect in .NET 6.0+ applications. |
+
+### Analyzer warnings (`SYSLIB1001` - `SYSLIB1999`)
+| Diagnostic ID     | Description |
+| :---------------- | :---------- |
+|  __`SYSLIB1001`__ | Logging method names cannot start with _ |
+|  __`SYSLIB1002`__ | Don't include log level parameters as templates in the logging message |
+|  __`SYSLIB1003`__ | InvalidLoggingMethodParameterNameTitle |
+|  __`SYSLIB1004`__ | Logging class cannot be in nested types |
+|  __`SYSLIB1005`__ | Could not find a required type definition |
+|  __`SYSLIB1006`__ | Multiple logging methods cannot use the same event id within a class |
+|  __`SYSLIB1007`__ | Logging methods must return void |
+|  __`SYSLIB1008`__ | One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface |
+|  __`SYSLIB1009`__ | Logging methods must be static |
+|  __`SYSLIB1010`__ | Logging methods must be partial |
+|  __`SYSLIB1011`__ | Logging methods cannot be generic |
+|  __`SYSLIB1012`__ | Redundant qualifier in logging message |
+|  __`SYSLIB1013`__ | Don't include exception parameters as templates in the logging message |
+|  __`SYSLIB1014`__ | Logging template has no corresponding method argument |
+|  __`SYSLIB1015`__ | Argument is not referenced from the logging message |
+|  __`SYSLIB1016`__ | Logging methods cannot have a body |
+|  __`SYSLIB1017`__ | A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method |
+|  __`SYSLIB1018`__ | Don't include logger parameters as templates in the logging message |
+|  __`SYSLIB1019`__ | Couldn't find a field of type Microsoft.Extensions.Logging.ILogger |
+|  __`SYSLIB1020`__ | Found multiple fields of type Microsoft.Extensions.Logging.ILogger |
+|  __`SYSLIB1021`__ | Can't have the same template with different casing |
+|  __`SYSLIB1022`__ | Can't have malformed format strings (like dangling {, etc)  |
+|  __`SYSLIB1023`__ | Generating more than 6 arguments is not supported |
+|  __`SYSLIB1029`__ | *_Blocked range `SYSLIB1024`-`SYSLIB1029` for logging._* |
\ No newline at end of file
index db8910c..28f003f 100644 (file)
     <ProjectServicingConfiguration Include="Microsoft.NETCore.App.Ref" PatchVersion="0" />
   </ItemGroup>
   <PropertyGroup>
+    <!-- For source generator support we need to target a pinned version in order to be able to run on older versions of Roslyn -->
+    <MicrosoftCodeAnalysisCSharpWorkspacesVersion>3.8.0</MicrosoftCodeAnalysisCSharpWorkspacesVersion>
+    <MicrosoftCodeAnalysisVersion>3.8.0</MicrosoftCodeAnalysisVersion>
+  </PropertyGroup>
+  <PropertyGroup>
     <MicrosoftCodeAnalysisNetAnalyzersVersion>6.0.0-preview3.21168.1</MicrosoftCodeAnalysisNetAnalyzersVersion>
     <MicrosoftCodeAnalysisCSharpCodeStyleVersion>3.9.0-5.final</MicrosoftCodeAnalysisCSharpCodeStyleVersion>
     <MicrosoftCodeAnalysisCSharpVersion>3.9.0-5.final</MicrosoftCodeAnalysisCSharpVersion>
     <XUnitRunnerVisualStudioVersion>2.4.2</XUnitRunnerVisualStudioVersion>
     <CoverletCollectorVersion>1.3.0</CoverletCollectorVersion>
     <NewtonsoftJsonVersion>12.0.3</NewtonsoftJsonVersion>
+    <SQLitePCLRawbundle_greenVersion>2.0.4</SQLitePCLRawbundle_greenVersion>
     <MoqVersion>4.12.0</MoqVersion>
     <FsCheckVersion>2.14.3</FsCheckVersion>
     <!-- Docs -->
diff --git a/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs
new file mode 100644 (file)
index 0000000..4928270
--- /dev/null
@@ -0,0 +1,293 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Text;
+using Xunit;
+
+namespace SourceGenerators.Tests
+{
+    internal static class RoslynTestUtils
+    {
+        /// <summary>
+        /// Creates a canonical Roslyn project for testing.
+        /// </summary>
+        /// <param name="references">Assembly references to include in the project.</param>
+        /// <param name="includeBaseReferences">Whether to include references to the BCL assemblies.</param>
+        public static Project CreateTestProject(IEnumerable<Assembly>? references, bool includeBaseReferences = true)
+        {
+            string corelib = Assembly.GetAssembly(typeof(object))!.Location;
+            string runtimeDir = Path.GetDirectoryName(corelib)!;
+
+            var refs = new List<MetadataReference>();
+            if (includeBaseReferences)
+            {
+                refs.Add(MetadataReference.CreateFromFile(corelib));
+                refs.Add(MetadataReference.CreateFromFile(Path.Combine(runtimeDir, "netstandard.dll")));
+                refs.Add(MetadataReference.CreateFromFile(Path.Combine(runtimeDir, "System.Runtime.dll")));
+            }
+
+            if (references != null)
+            {
+                foreach (var r in references)
+                {
+                    refs.Add(MetadataReference.CreateFromFile(r.Location));
+                }
+            }
+
+            return new AdhocWorkspace()
+                        .AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create()))
+                        .AddProject("Test", "test.dll", "C#")
+                            .WithMetadataReferences(refs)
+                            .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithNullableContextOptions(NullableContextOptions.Enable));
+        }
+
+        public static Task CommitChanges(this Project proj, params string[] ignorables)
+        {
+            Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution));
+            return AssertNoDiagnostic(proj, ignorables);
+        }
+
+        public static async Task AssertNoDiagnostic(this Project proj, params string[] ignorables)
+        {
+            foreach (Document doc in proj.Documents)
+            {
+                SemanticModel? sm = await doc.GetSemanticModelAsync(CancellationToken.None).ConfigureAwait(false);
+                Assert.NotNull(sm);
+
+                foreach (Diagnostic d in sm!.GetDiagnostics())
+                {
+                    bool ignore = ignorables.Any(ig => d.Id == ig);
+
+                    Assert.True(ignore, d.ToString());
+                }
+            }
+        }
+
+        private static Project WithDocuments(this Project project, IEnumerable<string> sources, IEnumerable<string>? sourceNames = null)
+        {
+            int count = 0;
+            Project result = project;
+            if (sourceNames != null)
+            {
+                List<string> names = sourceNames.ToList();
+                foreach (string s in sources)
+                    result = result.WithDocument(names[count++], s);
+            }
+            else
+            {
+                foreach (string s in sources)
+                    result = result.WithDocument($"src-{count++}.cs", s);
+            }
+
+            return result;
+        }
+
+        public static Project WithDocument(this Project proj, string name, string text)
+        {
+            return proj.AddDocument(name, text).Project;
+        }
+
+        public static Document FindDocument(this Project proj, string name)
+        {
+            foreach (Document doc in proj.Documents)
+            {
+                if (doc.Name == name)
+                {
+                    return doc;
+                }
+            }
+
+            throw new FileNotFoundException(name);
+        }
+
+        /// <summary>
+        /// Looks for /*N+*/ and /*-N*/ markers in a string and creates a TextSpan containing the enclosed text.
+        /// </summary>
+        public static TextSpan MakeSpan(string text, int spanNum)
+        {
+            int start = text.IndexOf($"/*{spanNum}+*/", StringComparison.Ordinal);
+            if (start < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(spanNum));
+            }
+
+            start += 6;
+
+            int end = text.IndexOf($"/*-{spanNum}*/", StringComparison.Ordinal);
+            if (end < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(spanNum));
+            }
+
+            end -= 1;
+
+            return new TextSpan(start, end - start);
+        }
+
+        /// <summary>
+        /// Runs a Roslyn generator over a set of source files.
+        /// </summary>
+        public static async Task<(ImmutableArray<Diagnostic>, ImmutableArray<GeneratedSourceResult>)> RunGenerator(
+            ISourceGenerator generator,
+            IEnumerable<Assembly>? references,
+            IEnumerable<string> sources,
+            AnalyzerConfigOptionsProvider? optionsProvider = null,
+            bool includeBaseReferences = true,
+            CancellationToken cancellationToken = default)
+        {
+            Project proj = CreateTestProject(references, includeBaseReferences);
+
+            proj = proj.WithDocuments(sources);
+
+            Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution));
+
+            Compilation? comp = await proj!.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false);
+
+            CSharpGeneratorDriver cgd = CSharpGeneratorDriver.Create(new[] { generator }, optionsProvider: optionsProvider);
+            GeneratorDriver gd = cgd.RunGenerators(comp!, cancellationToken);
+
+            GeneratorDriverRunResult r = gd.GetRunResult();
+            return (r.Results[0].Diagnostics, r.Results[0].GeneratedSources);
+        }
+
+        /// <summary>
+        /// Runs a Roslyn analyzer over a set of source files.
+        /// </summary>
+        public static async Task<IList<Diagnostic>> RunAnalyzer(
+            DiagnosticAnalyzer analyzer,
+            IEnumerable<Assembly> references,
+            IEnumerable<string> sources)
+        {
+            Project proj = CreateTestProject(references);
+
+            proj = proj.WithDocuments(sources);
+
+            await proj.CommitChanges().ConfigureAwait(false);
+
+            ImmutableArray<DiagnosticAnalyzer> analyzers = ImmutableArray.Create(analyzer);
+
+            Compilation? comp = await proj!.GetCompilationAsync().ConfigureAwait(false);
+            return await comp!.WithAnalyzers(analyzers).GetAllDiagnosticsAsync().ConfigureAwait(false);
+        }
+
+        /// <summary>
+        /// Runs a Roslyn analyzer and fixer.
+        /// </summary>
+        public static async Task<IList<string>> RunAnalyzerAndFixer(
+            DiagnosticAnalyzer analyzer,
+            CodeFixProvider fixer,
+            IEnumerable<Assembly> references,
+            IEnumerable<string> sources,
+            IEnumerable<string>? sourceNames = null,
+            string? defaultNamespace = null,
+            string? extraFile = null)
+        {
+            Project proj = CreateTestProject(references);
+
+            int count = sources.Count();
+            proj = proj.WithDocuments(sources, sourceNames);
+
+            if (defaultNamespace != null)
+            {
+                proj = proj.WithDefaultNamespace(defaultNamespace);
+            }
+
+            await proj.CommitChanges().ConfigureAwait(false);
+
+            ImmutableArray<DiagnosticAnalyzer> analyzers = ImmutableArray.Create(analyzer);
+
+            while (true)
+            {
+                Compilation? comp = await proj!.GetCompilationAsync().ConfigureAwait(false);
+                ImmutableArray<Diagnostic> diags = await comp!.WithAnalyzers(analyzers).GetAllDiagnosticsAsync().ConfigureAwait(false);
+                if (diags.IsEmpty)
+                {
+                    // no more diagnostics reported by the analyzers
+                    break;
+                }
+
+                var actions = new List<CodeAction>();
+                foreach (Diagnostic d in diags)
+                {
+                    Document? doc = proj.GetDocument(d.Location.SourceTree);
+
+                    CodeFixContext context = new CodeFixContext(doc!, d, (action, _) => actions.Add(action), CancellationToken.None);
+                    await fixer.RegisterCodeFixesAsync(context).ConfigureAwait(false);
+                }
+
+                if (actions.Count == 0)
+                {
+                    // nothing to fix
+                    break;
+                }
+
+                ImmutableArray<CodeActionOperation> operations = await actions[0].GetOperationsAsync(CancellationToken.None).ConfigureAwait(false);
+                Solution solution = operations.OfType<ApplyChangesOperation>().Single().ChangedSolution;
+                Project? changedProj = solution.GetProject(proj.Id);
+                if (changedProj != proj)
+                {
+                    proj = await RecreateProjectDocumentsAsync(changedProj!).ConfigureAwait(false);
+                }
+            }
+
+            var results = new List<string>();
+
+            if (sourceNames != null)
+            {
+                List<string> l = sourceNames.ToList();
+                for (int i = 0; i < count; i++)
+                {
+                    SourceText s = await proj.FindDocument(l[i]).GetTextAsync().ConfigureAwait(false);
+                    results.Add(s.ToString().Replace("\r\n", "\n", StringComparison.Ordinal));
+                }
+            }
+            else
+            {
+                for (int i = 0; i < count; i++)
+                {
+                    SourceText s = await proj.FindDocument($"src-{i}.cs").GetTextAsync().ConfigureAwait(false);
+                    results.Add(s.ToString().Replace("\r\n", "\n", StringComparison.Ordinal));
+                }
+            }
+
+            if (extraFile != null)
+            {
+                SourceText s = await proj.FindDocument(extraFile).GetTextAsync().ConfigureAwait(false);
+                results.Add(s.ToString().Replace("\r\n", "\n", StringComparison.Ordinal));
+            }
+
+            return results;
+        }
+
+        private static async Task<Project> RecreateProjectDocumentsAsync(Project project)
+        {
+            foreach (DocumentId documentId in project.DocumentIds)
+            {
+                Document? document = project.GetDocument(documentId);
+                document = await RecreateDocumentAsync(document!).ConfigureAwait(false);
+                project = document.Project;
+            }
+
+            return project;
+        }
+
+        private static async Task<Document> RecreateDocumentAsync(Document document)
+        {
+            SourceText newText = await document.GetTextAsync().ConfigureAwait(false);
+            return document.WithText(SourceText.From(newText.ToString(), newText.Encoding, newText.ChecksumAlgorithm));
+        }
+    }
+}
index 7d19ffd..c04786d 100644 (file)
@@ -141,6 +141,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{62569F09-F90
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{35D3ECF9-E321-4AA6-BF5B-41E7AC54A620}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{9E96ED55-37A0-4007-854D-F3E3526E3CC0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators", "gen\Microsoft.Extensions.Logging.Generators.csproj", "{56A5DED2-47C2-4938-931E-B896A6BDDA0D}"
+EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.Encodings.Web", "..\System.Text.Encodings.Web\ref\System.Text.Encodings.Web.csproj", "{58614FD7-05BC-46A8-900D-AC5B8622C724}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.Json", "..\System.Text.Json\ref\System.Text.Json.csproj", "{F7E444A4-124D-48E2-B311-17A5ED8B0CC2}"
@@ -149,6 +153,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Diagnostics.Diagnost
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Threading.AccessControl", "..\System.Threading.AccessControl\ref\System.Threading.AccessControl.csproj", "{5FF1A443-F491-428F-9121-51523AA65052}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators.Tests", "tests\Microsoft.Extensions.Logging.Generators.Tests\Microsoft.Extensions.Logging.Generators.Tests.csproj", "{8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
@@ -419,6 +425,10 @@ Global
                {1E1B25F0-7B14-4798-BBF4-156A52949CBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {1E1B25F0-7B14-4798-BBF4-156A52949CBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {1E1B25F0-7B14-4798-BBF4-156A52949CBA}.Release|Any CPU.Build.0 = Release|Any CPU
+               {56A5DED2-47C2-4938-931E-B896A6BDDA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {56A5DED2-47C2-4938-931E-B896A6BDDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {56A5DED2-47C2-4938-931E-B896A6BDDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {56A5DED2-47C2-4938-931E-B896A6BDDA0D}.Release|Any CPU.Build.0 = Release|Any CPU
                {58614FD7-05BC-46A8-900D-AC5B8622C724}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {58614FD7-05BC-46A8-900D-AC5B8622C724}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {58614FD7-05BC-46A8-900D-AC5B8622C724}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -435,6 +445,10 @@ Global
                {5FF1A443-F491-428F-9121-51523AA65052}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {5FF1A443-F491-428F-9121-51523AA65052}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {5FF1A443-F491-428F-9121-51523AA65052}.Release|Any CPU.Build.0 = Release|Any CPU
+               {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -506,10 +520,12 @@ Global
                {504464B5-B163-4D6B-A113-52DDC78A401D} = {35D3ECF9-E321-4AA6-BF5B-41E7AC54A620}
                {7467E5B1-7454-4277-A84F-E8BE34A80CE4} = {62569F09-F901-4240-B3E1-E2FF90544D74}
                {1E1B25F0-7B14-4798-BBF4-156A52949CBA} = {35D3ECF9-E321-4AA6-BF5B-41E7AC54A620}
+               {56A5DED2-47C2-4938-931E-B896A6BDDA0D} = {9E96ED55-37A0-4007-854D-F3E3526E3CC0}
                {58614FD7-05BC-46A8-900D-AC5B8622C724} = {62569F09-F901-4240-B3E1-E2FF90544D74}
                {F7E444A4-124D-48E2-B311-17A5ED8B0CC2} = {62569F09-F901-4240-B3E1-E2FF90544D74}
                {5B5AFA97-C1FC-47B9-AB7A-6A10E0285282} = {62569F09-F901-4240-B3E1-E2FF90544D74}
                {5FF1A443-F491-428F-9121-51523AA65052} = {62569F09-F901-4240-B3E1-E2FF90544D74}
+               {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761} = {88957302-AFDD-4923-BF5A-336EAB5F28B7}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {B57B7C13-740F-4482-B7B6-B5E87014ACB1}
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs
new file mode 100644 (file)
index 0000000..9e51646
--- /dev/null
@@ -0,0 +1,195 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Extensions.Logging.Generators
+{
+    public static class DiagnosticDescriptors
+    {
+        public static DiagnosticDescriptor InvalidLoggingMethodName { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1001",
+            title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor ShouldntMentionLogLevelInMessage { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1002",
+            title: new LocalizableResourceString(nameof(SR.ShouldntMentionLogLevelInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionInTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Warning,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor InvalidLoggingMethodParameterName { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1003",
+            title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodParameterNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodParameterNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor LoggingMethodInNestedType { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1004",
+            title: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor MissingRequiredType { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1005",
+            title: new LocalizableResourceString(nameof(SR.MissingRequiredTypeTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.MissingRequiredTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor ShouldntReuseEventIds { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1006",
+            title: new LocalizableResourceString(nameof(SR.ShouldntReuseEventIdsTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.ShouldntReuseEventIdsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Warning,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor LoggingMethodMustReturnVoid { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1007",
+            title: new LocalizableResourceString(nameof(SR.LoggingMethodMustReturnVoidMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodMustReturnVoidMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor MissingLoggerArgument { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1008",
+            title: new LocalizableResourceString(nameof(SR.MissingLoggerArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.MissingLoggerArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor LoggingMethodShouldBeStatic { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1009",
+            title: new LocalizableResourceString(nameof(SR.LoggingMethodShouldBeStaticMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodShouldBeStaticMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Warning,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor LoggingMethodMustBePartial { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1010",
+            title: new LocalizableResourceString(nameof(SR.LoggingMethodMustBePartialMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodMustBePartialMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor LoggingMethodIsGeneric { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1011",
+            title: new LocalizableResourceString(nameof(SR.LoggingMethodIsGenericMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodIsGenericMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor RedundantQualifierInMessage { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1012",
+            title: new LocalizableResourceString(nameof(SR.RedundantQualifierInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.RedundantQualifierInMessageMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Warning,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor ShouldntMentionExceptionInMessage { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1013",
+            title: new LocalizableResourceString(nameof(SR.ShouldntMentionExceptionInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionInTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Warning,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor TemplateHasNoCorrespondingArgument { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1014",
+            title: new LocalizableResourceString(nameof(SR.TemplateHasNoCorrespondingArgumentTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.TemplateHasNoCorrespondingArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor ArgumentHasNoCorrespondingTemplate { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1015",
+            title: new LocalizableResourceString(nameof(SR.ArgumentHasNoCorrespondingTemplateTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.ArgumentHasNoCorrespondingTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Warning,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor LoggingMethodHasBody { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1016",
+            title: new LocalizableResourceString(nameof(SR.LoggingMethodHasBodyMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodHasBodyMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor MissingLogLevel { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1017",
+            title: new LocalizableResourceString(nameof(SR.MissingLogLevelMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.MissingLogLevelMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor ShouldntMentionLoggerInMessage { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1018",
+            title: new LocalizableResourceString(nameof(SR.ShouldntMentionLoggerInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionInTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Warning,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor MissingLoggerField { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1019",
+            title: new LocalizableResourceString(nameof(SR.MissingLoggerFieldTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.MissingLoggerFieldMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor MultipleLoggerFields { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1020",
+            title: new LocalizableResourceString(nameof(SR.MultipleLoggerFieldsTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.MultipleLoggerFieldsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor InconsistentTemplateCasing { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1021",
+            title: new LocalizableResourceString(nameof(SR.InconsistentTemplateCasingMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.InconsistentTemplateCasingMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor MalformedFormatStrings { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1022",
+            title: new LocalizableResourceString(nameof(SR.MalformedFormatStringsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.MalformedFormatStringsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+
+        public static DiagnosticDescriptor GeneratingForMax6Arguments { get; } = new DiagnosticDescriptor(
+            id: "SYSLIB1023",
+            title: new LocalizableResourceString(nameof(SR.GeneratingForMax6ArgumentsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            messageFormat: new LocalizableResourceString(nameof(SR.GeneratingForMax6ArgumentsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
+            category: "LoggingGenerator",
+            DiagnosticSeverity.Error,
+            isEnabledByDefault: true);
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs
new file mode 100644 (file)
index 0000000..91065fa
--- /dev/null
@@ -0,0 +1,557 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+
+namespace Microsoft.Extensions.Logging.Generators
+{
+    public partial class LoggerMessageGenerator
+    {
+        internal class Emitter
+        {
+            // The maximum arity of LoggerMessage.Define.
+            private const int MaxLoggerMessageDefineArguments = 6;
+            private const int DefaultStringBuilderCapacity = 1024;
+
+            private readonly string _generatedCodeAttribute =
+                $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" +
+                $"\"{typeof(Emitter).Assembly.GetName().Name}\", " +
+                $"\"{typeof(Emitter).Assembly.GetName().Version}\")";
+            private readonly StringBuilder _builder = new StringBuilder(DefaultStringBuilderCapacity);
+
+            public string Emit(IReadOnlyList<LoggerClass> logClasses, CancellationToken cancellationToken)
+            {
+                _builder.Clear();
+                _builder.AppendLine("// <auto-generated/>");
+                _builder.AppendLine("#nullable enable");
+
+                foreach (LoggerClass lc in logClasses)
+                {
+                    cancellationToken.ThrowIfCancellationRequested();
+                    GenType(lc);
+                }
+
+                return _builder.ToString();
+            }
+
+            private static bool UseLoggerMessageDefine(LoggerMethod lm)
+            {
+                bool result =
+                    (lm.TemplateParameters.Count <= MaxLoggerMessageDefineArguments) && // more args than LoggerMessage.Define can handle
+                    (lm.Level != null) &&                                               // dynamic log level, which LoggerMessage.Define can't handle
+                    (lm.TemplateList.Count == lm.TemplateParameters.Count);             // mismatch in template to args, which LoggerMessage.Define can't handle
+
+                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)
+                    {
+                        if (!t.Equals(lm.TemplateParameters[count].Name, StringComparison.OrdinalIgnoreCase))
+                        {
+                            // order doesn't match, can't use LoggerMessage.Define
+                            return false;
+                        }
+                    }
+                }
+
+                return result;
+            }
+
+            private void GenType(LoggerClass lc)
+            {
+                if (!string.IsNullOrWhiteSpace(lc.Namespace))
+                {
+                    _builder.Append($@"
+namespace {lc.Namespace}
+{{");
+                }
+
+                _builder.Append($@"
+    partial class {lc.Name} {lc.Constraints}
+    {{");
+
+                foreach (LoggerMethod lm in lc.Methods)
+                {
+                    if (!UseLoggerMessageDefine(lm))
+                    {
+                        GenStruct(lm);
+                    }
+
+                    GenLogMethod(lm);
+                }
+
+                GenEnumerationHelper(lc);
+
+                _builder.Append($@"
+    }}");
+
+                if (!string.IsNullOrWhiteSpace(lc.Namespace))
+                {
+                    _builder.Append($@"
+}}");
+                }
+            }
+
+            private void GenStruct(LoggerMethod lm)
+            {
+                _builder.AppendLine($@"
+        [{_generatedCodeAttribute}]
+        private readonly struct __{lm.Name}Struct : global::System.Collections.Generic.IReadOnlyList<global::System.Collections.Generic.KeyValuePair<string, object?>>
+        {{");
+                GenFields(lm);
+
+                if (lm.TemplateParameters.Count > 0)
+                {
+                    _builder.Append($@"
+            public __{lm.Name}Struct(");
+                    GenArguments(lm);
+                    _builder.Append($@")
+            {{");
+                    _builder.AppendLine();
+                    GenFieldAssignments(lm);
+                    _builder.Append($@"
+            }}
+");
+                }
+
+                _builder.Append($@"
+            public override string ToString()
+            {{
+");
+                GenVariableAssignments(lm);
+                _builder.Append($@"
+                return $""{lm.Message}"";
+            }}
+");
+                _builder.Append($@"
+            public static string Format(__{lm.Name}Struct state, global::System.Exception? ex) => state.ToString();
+
+            public int Count => {lm.TemplateParameters.Count + 1};
+
+            public global::System.Collections.Generic.KeyValuePair<string, object?> this[int index]
+            {{
+                get => index switch
+                {{
+");
+                GenCases(lm);
+                _builder.Append($@"
+                    _ => throw new global::System.IndexOutOfRangeException(nameof(index)),  // return the same exception LoggerMessage.Define returns in this case
+                }};
+            }}
+
+            public global::System.Collections.Generic.IEnumerator<global::System.Collections.Generic.KeyValuePair<string, object?>> GetEnumerator()
+            {{
+                for (int i = 0; i < {lm.TemplateParameters.Count + 1}; i++)
+                {{
+                    yield return this[i];
+                }}
+            }}
+
+            global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
+        }}
+");
+            }
+
+            private void GenFields(LoggerMethod lm)
+            {
+                foreach (LoggerParameter p in lm.TemplateParameters)
+                {
+                    _builder.AppendLine($"            private readonly {p.Type} _{p.Name};");
+                }
+            }
+
+            private void GenFieldAssignments(LoggerMethod lm)
+            {
+                foreach (LoggerParameter p in lm.TemplateParameters)
+                {
+                    _builder.AppendLine($"                this._{p.Name} = {p.Name};");
+                }
+            }
+
+            private void GenVariableAssignments(LoggerMethod lm)
+            {
+                foreach (KeyValuePair<string, string> t in lm.TemplateMap)
+                {
+                    int index = 0;
+                    foreach (LoggerParameter p in lm.TemplateParameters)
+                    {
+                        if (t.Key.Equals(p.Name, System.StringComparison.OrdinalIgnoreCase))
+                        {
+                            break;
+                        }
+
+                        index++;
+                    }
+
+                    // check for an index that's too big, this can happen in some cases of malformed input
+                    if (index < lm.TemplateParameters.Count)
+                    {
+                        if (lm.TemplateParameters[index].IsEnumerable)
+                        {
+                            _builder.AppendLine($"                var {t.Key} = "
+                                + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.TemplateParameters[index].Name});");
+                        }
+                        else
+                        {
+                            _builder.AppendLine($"                var {t.Key} = this._{lm.TemplateParameters[index].Name};");
+                        }
+                    }
+                }
+            }
+
+            private void GenCases(LoggerMethod lm)
+            {
+                int index = 0;
+                foreach (LoggerParameter p in lm.TemplateParameters)
+                {
+                    string name = p.Name;
+                    if (lm.TemplateMap.ContainsKey(name))
+                    {
+                        // take the letter casing from the template
+                        name = lm.TemplateMap[name];
+                    }
+
+                    _builder.AppendLine($"                    {index++} => new global::System.Collections.Generic.KeyValuePair<string, object?>(\"{name}\", this._{p.Name}),");
+                }
+
+                _builder.AppendLine($"                    {index++} => new global::System.Collections.Generic.KeyValuePair<string, object?>(\"{{OriginalFormat}}\", \"{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}\"),");
+            }
+
+            private void GenCallbackArguments(LoggerMethod lm)
+            {
+                foreach (LoggerParameter p in lm.TemplateParameters)
+                {
+                    _builder.Append($"{p.Name}, ");
+                }
+            }
+
+            private void GenDefineTypes(LoggerMethod lm, bool brackets)
+            {
+                if (lm.TemplateParameters.Count == 0)
+                {
+                    return;
+                }
+                if (brackets)
+                {
+                    _builder.Append("<");
+                }
+
+                bool firstItem = true;
+                foreach (LoggerParameter p in lm.TemplateParameters)
+                {
+                    if (firstItem)
+                    {
+                        firstItem = false;
+                    }
+                    else
+                    {
+                        _builder.Append(", ");
+                    }
+
+                    _builder.Append($"{p.Type}");
+                }
+
+                if (brackets)
+                {
+                    _builder.Append(">");
+                }
+                else
+                {
+                    _builder.Append(", ");
+                }
+            }
+
+            private void GenParameters(LoggerMethod lm)
+            {
+                bool firstItem = true;
+                foreach (LoggerParameter p in lm.AllParameters)
+                {
+                    if (firstItem)
+                    {
+                        firstItem = false;
+                    }
+                    else
+                    {
+                        _builder.Append(", ");
+                    }
+
+                    _builder.Append($"{p.Type} {p.Name}");
+                }
+            }
+
+            private void GenArguments(LoggerMethod lm)
+            {
+                bool firstItem = true;
+                foreach (LoggerParameter p in lm.TemplateParameters)
+                {
+                    if (firstItem)
+                    {
+                        firstItem = false;
+                    }
+                    else
+                    {
+                        _builder.Append(", ");
+                    }
+
+                    _builder.Append($"{p.Type} {p.Name}");
+                }
+            }
+
+            private void GenHolder(LoggerMethod lm)
+            {
+                string typeName = $"__{lm.Name}Struct";
+
+                _builder.Append($"new {typeName}(");
+                foreach (LoggerParameter p in lm.TemplateParameters)
+                {
+                    if (p != lm.TemplateParameters[0])
+                    {
+                        _builder.Append(", ");
+                    }
+
+                    _builder.Append(p.Name);
+                }
+
+                _builder.Append(')');
+            }
+
+            private void GenLogMethod(LoggerMethod lm)
+            {
+                string level = GetLogLevel(lm);
+                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}]
+        private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, ");
+
+                    GenDefineTypes(lm, brackets: false);
+
+                    _builder.Append(@$"global::System.Exception?> __{lm.Name}Callback =
+            global::Microsoft.Extensions.Logging.LoggerMessage.Define");
+
+                    GenDefineTypes(lm, brackets: true);
+
+                    _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}"", true); 
+");
+                }
+
+                _builder.Append($@"
+        [{_generatedCodeAttribute}]
+        {lm.Modifiers} void {lm.Name}({extension}");
+
+                GenParameters(lm);
+
+                _builder.Append($@")
+        {{
+            if ({logger}.IsEnabled({level}))
+            {{");
+
+                if (UseLoggerMessageDefine(lm))
+                {
+                    _builder.Append($@"
+                __{lm.Name}Callback({logger}, ");
+
+                    GenCallbackArguments(lm);
+
+                    _builder.Append(@$"{exceptionArg});");
+                }
+                else
+                {
+                    _builder.Append($@"
+                {logger}.Log(
+                    {level},
+                    new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}),
+                    ");
+                    GenHolder(lm);
+                    _builder.Append($@",
+                    {exceptionArg},
+                    __{lm.Name}Struct.Format);");
+                }
+
+                _builder.Append($@"
+            }}
+        }}");
+
+                static string GetException(LoggerMethod lm)
+                {
+                    string exceptionArg = "null";
+                    foreach (LoggerParameter p in lm.AllParameters)
+                    {
+                        if (p.IsException)
+                        {
+                            exceptionArg = p.Name;
+                            break;
+                        }
+                    }
+                    return exceptionArg;
+                }
+
+                static string GetLogger(LoggerMethod lm)
+                {
+                    string logger = lm.LoggerField;
+                    foreach (LoggerParameter p in lm.AllParameters)
+                    {
+                        if (p.IsLogger)
+                        {
+                            logger = p.Name;
+                            break;
+                        }
+                    }
+                    return logger;
+                }
+
+                static string GetLogLevel(LoggerMethod lm)
+                {
+                    string level = string.Empty;
+
+                    if (lm.Level == null)
+                    {
+                        foreach (LoggerParameter p in lm.AllParameters)
+                        {
+                            if (p.IsLogLevel)
+                            {
+                                level = p.Name;
+                                break;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        level = lm.Level switch
+                        {
+                            0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace",
+                            1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug",
+                            2 => "global::Microsoft.Extensions.Logging.LogLevel.Information",
+                            3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning",
+                            4 => "global::Microsoft.Extensions.Logging.LogLevel.Error",
+                            5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical",
+                            6 => "global::Microsoft.Extensions.Logging.LogLevel.None",
+                            _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}",
+                        };
+                    }
+
+                    return level;
+                }
+            }
+
+            private void GenEnumerationHelper(LoggerClass lc)
+            {
+                foreach (LoggerMethod lm in lc.Methods)
+                {
+                    if (UseLoggerMessageDefine(lm))
+                    {
+                        foreach (LoggerParameter p in lm.TemplateParameters)
+                        {
+                            if (p.IsEnumerable)
+                            {
+                                _builder.Append($@"
+        [{_generatedCodeAttribute}]
+        private 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();
+        }}
+");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        private static string ConvertEndOfLineAndQuotationCharactersToEscapeForm(string s)
+        {
+            int index = 0;
+            while (index < s.Length)
+            {
+                if (s[index] == '\n' || s[index] == '\r' || s[index] == '"')
+                {
+                    break;
+                }
+                index++;
+            }
+
+            if (index >= s.Length)
+            {
+                return s;
+            }
+
+            StringBuilder sb = new StringBuilder(s.Length);
+            sb.Append(s, 0, index);
+
+            while (index < s.Length)
+            {
+                switch (s[index])
+                {
+                    case '\n':
+                        sb.Append('\\');
+                        sb.Append('n');
+                        break;
+
+                    case '\r':
+                        sb.Append('\\');
+                        sb.Append('r');
+                        break;
+
+                    case '"':
+                        sb.Append('\\');
+                        sb.Append('"');
+                        break;
+
+                    default:
+                        sb.Append(s[index]);
+                        break;
+                }
+
+                index++;
+            }
+
+            return sb.ToString();
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs
new file mode 100644 (file)
index 0000000..3b909af
--- /dev/null
@@ -0,0 +1,664 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Microsoft.Extensions.Logging.Generators
+{
+    public partial class LoggerMessageGenerator
+    {
+        internal class Parser
+        {
+            private readonly CancellationToken _cancellationToken;
+            private readonly Compilation _compilation;
+            private readonly Action<Diagnostic> _reportDiagnostic;
+
+            public Parser(Compilation compilation, Action<Diagnostic> reportDiagnostic, CancellationToken cancellationToken)
+            {
+                _compilation = compilation;
+                _cancellationToken = cancellationToken;
+                _reportDiagnostic = reportDiagnostic;
+            }
+
+            /// <summary>
+            /// Gets the set of logging classes containing methods to output.
+            /// </summary>
+            public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSyntax> classes)
+            {
+                const string LoggerMessageAttribute = "Microsoft.Extensions.Logging.LoggerMessageAttribute";
+
+                INamedTypeSymbol loggerMessageAttribute = _compilation.GetTypeByMetadataName(LoggerMessageAttribute);
+                if (loggerMessageAttribute == null)
+                {
+                    // nothing to do if this type isn't available
+                    return Array.Empty<LoggerClass>();
+                }
+
+                INamedTypeSymbol loggerSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILogger");
+                if (loggerSymbol == null)
+                {
+                    // nothing to do if this type isn't available
+                    return Array.Empty<LoggerClass>();
+                }
+
+                INamedTypeSymbol logLevelSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LogLevel");
+                if (logLevelSymbol == null)
+                {
+                    // nothing to do if this type isn't available
+                    return Array.Empty<LoggerClass>();
+                }
+
+                INamedTypeSymbol exceptionSymbol = _compilation.GetTypeByMetadataName("System.Exception");
+                if (exceptionSymbol == null)
+                {
+                    Diag(DiagnosticDescriptors.MissingRequiredType, null, "System.Exception");
+                    return Array.Empty<LoggerClass>();
+                }
+
+                INamedTypeSymbol enumerableSymbol = _compilation.GetTypeByMetadataName("System.Collections.IEnumerable");
+                if (enumerableSymbol == null)
+                {
+                    Diag(DiagnosticDescriptors.MissingRequiredType, null, "System.Collections.IEnumerable");
+                    return Array.Empty<LoggerClass>();
+                }
+
+                INamedTypeSymbol stringSymbol = _compilation.GetTypeByMetadataName("System.String");
+                if (stringSymbol == null)
+                {
+                    Diag(DiagnosticDescriptors.MissingRequiredType, null, "System.String");
+                    return Array.Empty<LoggerClass>();
+                }
+
+                var results = new List<LoggerClass>();
+                var ids = new HashSet<int>();
+
+                // we enumerate by syntax tree, to minimize the need to instantiate semantic models (since they're expensive)
+                foreach (var group in classes.GroupBy(x => x.SyntaxTree))
+                {
+                    SemanticModel? sm = null;
+                    foreach (ClassDeclarationSyntax classDec in group)
+                    {
+                        // stop if we're asked to
+                        _cancellationToken.ThrowIfCancellationRequested();
+
+                        LoggerClass? lc = null;
+                        string nspace = string.Empty;
+                        string? loggerField = null;
+                        bool multipleLoggerFields = false;
+
+                        ids.Clear();
+                        foreach (var member in classDec.Members)
+                        {
+                            var method = member as MethodDeclarationSyntax;
+                            if (method == null)
+                            {
+                                // we only care about methods
+                                continue;
+                            }
+
+                            foreach (AttributeListSyntax mal in method.AttributeLists)
+                            {
+                                foreach (AttributeSyntax ma in mal.Attributes)
+                                {
+                                    sm ??= _compilation.GetSemanticModel(classDec.SyntaxTree);
+
+                                    IMethodSymbol attrCtorSymbol = sm.GetSymbolInfo(ma, _cancellationToken).Symbol as IMethodSymbol;
+                                    if (attrCtorSymbol == null || !loggerMessageAttribute.Equals(attrCtorSymbol.ContainingType, SymbolEqualityComparer.Default))
+                                    {
+                                        // badly formed attribute definition, or not the right attribute
+                                        continue;
+                                    }
+
+                                    (int eventId, int? level, string? message, string? eventName) = ExtractAttributeValues(ma.ArgumentList!, sm);
+
+                                    IMethodSymbol? methodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken);
+                                    if (methodSymbol != null)
+                                    {
+                                        var lm = new LoggerMethod
+                                        {
+                                            Name = method.Identifier.ToString(),
+                                            Level = level,
+                                            Message = message ?? string.Empty,
+                                            EventId = eventId,
+                                            EventName = eventName,
+                                            IsExtensionMethod = methodSymbol.IsExtensionMethod,
+                                            Modifiers = method.Modifiers.ToString(),
+                                        };
+
+                                        ExtractTemplates(message, lm.TemplateMap, lm.TemplateList);
+
+                                        bool keepMethod = true;   // whether or not we want to keep the method definition or if it's got errors making it so we should discard it instead
+                                        if (lm.Name[0] == '_')
+                                        {
+                                            // can't have logging method names that start with _ since that can lead to conflicting symbol names
+                                            // because the generated symbols start with _
+                                            Diag(DiagnosticDescriptors.InvalidLoggingMethodName, method.Identifier.GetLocation());
+                                            keepMethod = false;
+                                        }
+
+                                        if (sm.GetTypeInfo(method.ReturnType!).Type!.SpecialType != SpecialType.System_Void)
+                                        {
+                                            // logging methods must return void
+                                            Diag(DiagnosticDescriptors.LoggingMethodMustReturnVoid, method.ReturnType.GetLocation());
+                                            keepMethod = false;
+                                        }
+
+                                        if (method.Arity > 0)
+                                        {
+                                            // we don't currently support generic methods
+                                            Diag(DiagnosticDescriptors.LoggingMethodIsGeneric, method.Identifier.GetLocation());
+                                            keepMethod = false;
+                                        }
+
+                                        bool isStatic = false;
+                                        bool isPartial = false;
+                                        foreach (SyntaxToken mod in method.Modifiers)
+                                        {
+                                            switch (mod.Text)
+                                            {
+                                                case "partial":
+                                                    isPartial = true;
+                                                    break;
+
+                                                case "static":
+                                                    isStatic = true;
+                                                    break;
+                                            }
+                                        }
+
+                                        if (!isPartial)
+                                        {
+                                            Diag(DiagnosticDescriptors.LoggingMethodMustBePartial, method.GetLocation());
+                                            keepMethod = false;
+                                        }
+
+                                        if (method.Body != null)
+                                        {
+                                            Diag(DiagnosticDescriptors.LoggingMethodHasBody, method.Body.GetLocation());
+                                            keepMethod = false;
+                                        }
+
+                                        // ensure there are no duplicate ids.
+                                        if (ids.Contains(lm.EventId))
+                                        {
+                                            Diag(DiagnosticDescriptors.ShouldntReuseEventIds, ma.GetLocation(), lm.EventId, classDec.Identifier.Text);
+                                        }
+                                        else
+                                        {
+                                            _ = ids.Add(lm.EventId);
+                                        }
+
+                                        string msg = lm.Message;
+                                        if (msg.StartsWith("INFORMATION:", StringComparison.OrdinalIgnoreCase)
+                                            || msg.StartsWith("INFO:", StringComparison.OrdinalIgnoreCase)
+                                            || msg.StartsWith("WARNING:", StringComparison.OrdinalIgnoreCase)
+                                            || msg.StartsWith("WARN:", StringComparison.OrdinalIgnoreCase)
+                                            || msg.StartsWith("ERROR:", StringComparison.OrdinalIgnoreCase)
+                                            || msg.StartsWith("ERR:", StringComparison.OrdinalIgnoreCase))
+                                        {
+                                            Diag(DiagnosticDescriptors.RedundantQualifierInMessage, ma.GetLocation(), method.Identifier.ToString());
+                                        }
+
+                                        bool foundLogger = false;
+                                        bool foundException = false;
+                                        bool foundLogLevel = level != null;
+                                        foreach (ParameterSyntax p in method.ParameterList.Parameters)
+                                        {
+                                            string paramName = p.Identifier.ToString();
+                                            if (string.IsNullOrWhiteSpace(paramName))
+                                            {
+                                                // semantic problem, just bail quietly
+                                                keepMethod = false;
+                                                break;
+                                            }
+
+                                            IParameterSymbol? declSymbol = sm.GetDeclaredSymbol(p);
+                                            ITypeSymbol paramSymbol = declSymbol!.Type;
+                                            if (paramSymbol is IErrorTypeSymbol)
+                                            {
+                                                // semantic problem, just bail quietly
+                                                keepMethod = false;
+                                                break;
+                                            }
+
+                                            IParameterSymbol declaredType = sm.GetDeclaredSymbol(p);
+                                            string typeName = declaredType!.Type.ToDisplayString(
+                                                SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions(
+                                                    SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier));
+
+                                            var lp = new LoggerParameter
+                                            {
+                                                Name = paramName,
+                                                Type = typeName,
+                                                IsLogger = !foundLogger && IsBaseOrIdentity(paramSymbol!, loggerSymbol),
+                                                IsException = !foundException && IsBaseOrIdentity(paramSymbol!, exceptionSymbol),
+                                                IsLogLevel = !foundLogLevel && IsBaseOrIdentity(paramSymbol!, logLevelSymbol),
+                                                IsEnumerable = IsBaseOrIdentity(paramSymbol!, enumerableSymbol) && !IsBaseOrIdentity(paramSymbol!, stringSymbol),
+                                            };
+
+                                            foundLogger |= lp.IsLogger;
+                                            foundException |= lp.IsException;
+                                            foundLogLevel |= lp.IsLogLevel;
+
+                                            if (lp.IsLogger && lm.TemplateMap.ContainsKey(paramName))
+                                            {
+                                                Diag(DiagnosticDescriptors.ShouldntMentionLoggerInMessage, p.Identifier.GetLocation(), paramName);
+                                            }
+                                            else if (lp.IsException && lm.TemplateMap.ContainsKey(paramName))
+                                            {
+                                                Diag(DiagnosticDescriptors.ShouldntMentionExceptionInMessage, p.Identifier.GetLocation(), paramName);
+                                            }
+                                            else if (lp.IsLogLevel && lm.TemplateMap.ContainsKey(paramName))
+                                            {
+                                                Diag(DiagnosticDescriptors.ShouldntMentionLogLevelInMessage, p.Identifier.GetLocation(), paramName);
+                                            }
+                                            else if (lp.IsLogLevel && level != null && !lm.TemplateMap.ContainsKey(paramName))
+                                            {
+                                                Diag(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate, p.Identifier.GetLocation(), paramName);
+                                            }
+                                            else if (lp.IsTemplateParameter && !lm.TemplateMap.ContainsKey(paramName))
+                                            {
+                                                Diag(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate, p.Identifier.GetLocation(), paramName);
+                                            }
+
+                                            if (paramName[0] == '_')
+                                            {
+                                                // can't have logging method parameter names that start with _ since that can lead to conflicting symbol names
+                                                // because all generated symbols start with _
+                                                Diag(DiagnosticDescriptors.InvalidLoggingMethodParameterName, p.Identifier.GetLocation());
+                                            }
+
+                                            lm.AllParameters.Add(lp);
+                                            if (lp.IsTemplateParameter)
+                                            {
+                                                lm.TemplateParameters.Add(lp);
+                                            }
+                                        }
+
+                                        if (keepMethod)
+                                        {
+                                            if (isStatic && !foundLogger)
+                                            {
+                                                Diag(DiagnosticDescriptors.MissingLoggerArgument, method.GetLocation());
+                                                keepMethod = false;
+                                            }
+                                            else if (!isStatic && foundLogger)
+                                            {
+                                                Diag(DiagnosticDescriptors.LoggingMethodShouldBeStatic, method.GetLocation());
+                                            }
+                                            else if (!isStatic && !foundLogger)
+                                            {
+                                                if (loggerField == null)
+                                                {
+                                                    (loggerField, multipleLoggerFields) = FindLoggerField(sm, classDec, loggerSymbol);
+                                                }
+
+                                                if (multipleLoggerFields)
+                                                {
+                                                    Diag(DiagnosticDescriptors.MultipleLoggerFields, method.GetLocation(), classDec.Identifier.Text);
+                                                    keepMethod = false;
+                                                }
+                                                else if (loggerField == null)
+                                                {
+                                                    Diag(DiagnosticDescriptors.MissingLoggerField, method.GetLocation(), classDec.Identifier.Text);
+                                                    keepMethod = false;
+                                                }
+                                                else
+                                                {
+                                                    lm.LoggerField = loggerField;
+                                                }
+                                            }
+
+                                            if (level == null && !foundLogLevel)
+                                            {
+                                                Diag(DiagnosticDescriptors.MissingLogLevel, method.GetLocation());
+                                                keepMethod = false;
+                                            }
+
+                                            foreach (KeyValuePair<string, string> t in lm.TemplateMap)
+                                            {
+                                                bool found = false;
+                                                foreach (LoggerParameter p in lm.AllParameters)
+                                                {
+                                                    if (t.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase))
+                                                    {
+                                                        found = true;
+                                                        break;
+                                                    }
+                                                }
+
+                                                if (!found)
+                                                {
+                                                    Diag(DiagnosticDescriptors.TemplateHasNoCorrespondingArgument, ma.GetLocation(), t);
+                                                }
+                                            }
+                                        }
+
+                                        if (lc == null)
+                                        {
+                                            // determine the namespace the class is declared in, if any
+                                            var ns = classDec.Parent as NamespaceDeclarationSyntax;
+                                            if (ns == null)
+                                            {
+                                                if (classDec.Parent is not CompilationUnitSyntax)
+                                                {
+                                                    // since this generator doesn't know how to generate a nested type...
+                                                    Diag(DiagnosticDescriptors.LoggingMethodInNestedType, classDec.Identifier.GetLocation());
+                                                    keepMethod = false;
+                                                }
+                                            }
+                                            else
+                                            {
+                                                nspace = ns.Name.ToString();
+                                                while (true)
+                                                {
+                                                    ns = ns.Parent as NamespaceDeclarationSyntax;
+                                                    if (ns == null)
+                                                    {
+                                                        break;
+                                                    }
+
+                                                    nspace = $"{ns.Name}.{nspace}";
+                                                }
+                                            }
+                                        }
+
+                                        if (keepMethod)
+                                        {
+                                            lc ??= new LoggerClass
+                                            {
+                                                Namespace = nspace,
+                                                Name = classDec.Identifier.ToString() + classDec.TypeParameterList,
+                                                Constraints = classDec.ConstraintClauses.ToString(),
+                                            };
+
+                                            lc.Methods.Add(lm);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        if (lc != null)
+                        {
+                            results.Add(lc);
+                        }
+                    }
+                }
+
+                return results;
+            }
+
+            private (string? loggerField, bool multipleLoggerFields) FindLoggerField(SemanticModel sm, TypeDeclarationSyntax classDec, ITypeSymbol loggerSymbol)
+            {
+                string? loggerField = null;
+
+                foreach (MemberDeclarationSyntax m in classDec.Members)
+                {
+                    if (m is FieldDeclarationSyntax fds)
+                    {
+                        foreach (VariableDeclaratorSyntax v in fds.Declaration.Variables)
+                        {
+                            var fs = sm.GetDeclaredSymbol(v, _cancellationToken) as IFieldSymbol;
+                            if (fs != null)
+                            {
+                                if (IsBaseOrIdentity(fs.Type, loggerSymbol))
+                                {
+                                    if (loggerField == null)
+                                    {
+                                        loggerField = v.Identifier.Text;
+                                    }
+                                    else
+                                    {
+                                        return (null, true);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                return (loggerField, false);
+            }
+
+            private (int eventId, int? level, string? message, string? eventName) ExtractAttributeValues(AttributeArgumentListSyntax args, SemanticModel sm)
+            {
+                // two constructor arg shapes:
+                //
+                //   (eventId, level, message)
+                //   (eventId, message)
+
+                int eventId = 0;
+                int? level = null;
+                string? eventName = null;
+                string? message = null;
+                int numPositional = 0;
+                foreach (AttributeArgumentSyntax a in args.Arguments)
+                {
+                    if (a.NameEquals != null)
+                    {
+                        switch (a.NameEquals.Name.ToString())
+                        {
+                            case "EventId":
+                                eventId = (int)sm.GetConstantValue(a.Expression, _cancellationToken).Value!;
+                                break;
+                            case "EventName":
+                                eventName = sm.GetConstantValue(a.Expression, _cancellationToken).ToString();
+                                break;
+                            case "Level":
+                                level = (int)sm.GetConstantValue(a.Expression, _cancellationToken).Value!;
+                                break;
+                            case "Message":
+                                message = sm.GetConstantValue(a.Expression, _cancellationToken).ToString();
+                                break;
+                        }
+                    }
+                    else if (a.NameColon != null)
+                    {
+                        switch (a.NameColon.Name.ToString())
+                        {
+                            case "eventId":
+                                eventId = (int)sm.GetConstantValue(a.Expression, _cancellationToken).Value!;
+                                break;
+
+                            case "level":
+                                level = (int)sm.GetConstantValue(a.Expression, _cancellationToken).Value!;
+                                break;
+
+                            case "message":
+                                message = sm.GetConstantValue(a.Expression, _cancellationToken).ToString();
+                                break;
+                        }
+                    }
+                    else
+                    {
+                        switch (numPositional)
+                        {
+                            // event id
+                            case 0:
+                                eventId = (int)sm.GetConstantValue(a.Expression, _cancellationToken).Value!;
+                                break;
+
+                            // log level or message
+                            case 1:
+                                object o = sm.GetConstantValue(a.Expression, _cancellationToken).Value!;
+                                if (o is int l)
+                                {
+                                    level = l;
+                                }
+                                else
+                                {
+                                    message = sm.GetConstantValue(a.Expression, _cancellationToken).ToString();
+                                }
+
+                                break;
+
+                            // message
+                            case 2:
+                                message = sm.GetConstantValue(a.Expression, _cancellationToken).ToString();
+                                break;
+                        }
+
+                        numPositional++;
+                    }
+                }
+
+                return (eventId, level, message, eventName);
+            }
+
+            private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs)
+            {
+                _reportDiagnostic(Diagnostic.Create(desc, location, messageArgs));
+            }
+
+            private bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest)
+            {
+                Conversion conversion = _compilation.ClassifyConversion(source, dest);
+                return conversion.IsIdentity || (conversion.IsReference && conversion.IsImplicit);
+            }
+
+            private static readonly char[] _formatDelimiters = { ',', ':' };
+
+            /// <summary>
+            /// Finds the template arguments contained in the message string.
+            /// </summary>
+            private static void ExtractTemplates(string? message, IDictionary<string, string> templateMap, IList<string> templateList)
+            {
+                if (string.IsNullOrEmpty(message))
+                {
+                    return;
+                }
+
+                int scanIndex = 0;
+                int endIndex = message!.Length;
+
+                while (scanIndex < endIndex)
+                {
+                    int openBraceIndex = FindBraceIndex(message, '{', scanIndex, endIndex);
+                    int closeBraceIndex = FindBraceIndex(message, '}', openBraceIndex, endIndex);
+
+                    if (closeBraceIndex == endIndex)
+                    {
+                        scanIndex = endIndex;
+                    }
+                    else
+                    {
+                        // Format item syntax : { index[,alignment][ :formatString] }.
+                        int formatDelimiterIndex = FindIndexOfAny(message, _formatDelimiters, openBraceIndex, closeBraceIndex);
+
+                        string templateName = message.Substring(openBraceIndex + 1, formatDelimiterIndex - openBraceIndex - 1);
+                        templateMap[templateName] = templateName;
+                        templateList.Add(templateName);
+                        scanIndex = closeBraceIndex + 1;
+                    }
+                }
+            }
+
+            private static int FindBraceIndex(string message, char brace, int startIndex, int endIndex)
+            {
+                // Example: {{prefix{{{Argument}}}suffix}}.
+                int braceIndex = endIndex;
+                int scanIndex = startIndex;
+                int braceOccurrenceCount = 0;
+
+                while (scanIndex < endIndex)
+                {
+                    if (braceOccurrenceCount > 0 && message[scanIndex] != brace)
+                    {
+                        if (braceOccurrenceCount % 2 == 0)
+                        {
+                            // Even number of '{' or '}' found. Proceed search with next occurrence of '{' or '}'.
+                            braceOccurrenceCount = 0;
+                            braceIndex = endIndex;
+                        }
+                        else
+                        {
+                            // An unescaped '{' or '}' found.
+                            break;
+                        }
+                    }
+                    else if (message[scanIndex] == brace)
+                    {
+                        if (brace == '}')
+                        {
+                            if (braceOccurrenceCount == 0)
+                            {
+                                // For '}' pick the first occurrence.
+                                braceIndex = scanIndex;
+                            }
+                        }
+                        else
+                        {
+                            // For '{' pick the last occurrence.
+                            braceIndex = scanIndex;
+                        }
+
+                        braceOccurrenceCount++;
+                    }
+
+                    scanIndex++;
+                }
+
+                return braceIndex;
+            }
+
+            private static int FindIndexOfAny(string message, char[] chars, int startIndex, int endIndex)
+            {
+                int findIndex = message.IndexOfAny(chars, startIndex, endIndex - startIndex);
+                return findIndex == -1 ? endIndex : findIndex;
+            }
+        }
+
+        /// <summary>
+        /// A logger class holding a bunch of logger methods.
+        /// </summary>
+        internal class LoggerClass
+        {
+            public readonly List<LoggerMethod> Methods = new ();
+            public string Namespace = string.Empty;
+            public string Name = string.Empty;
+            public string Constraints = string.Empty;
+        }
+
+        /// <summary>
+        /// A logger method in a logger class.
+        /// </summary>
+        internal class LoggerMethod
+        {
+            public readonly List<LoggerParameter> AllParameters = new ();
+            public readonly List<LoggerParameter> TemplateParameters = new ();
+            public readonly Dictionary<string, string> TemplateMap = new (StringComparer.OrdinalIgnoreCase);
+            public readonly List<string> TemplateList = new ();
+            public string Name = string.Empty;
+            public string Message = string.Empty;
+            public int? Level;
+            public int EventId;
+            public string? EventName;
+            public bool IsExtensionMethod;
+            public string Modifiers = string.Empty;
+            public string LoggerField;
+        }
+
+        /// <summary>
+        /// A single parameter to a logger method.
+        /// </summary>
+        internal class LoggerParameter
+        {
+            public string Name = string.Empty;
+            public string Type = string.Empty;
+            public bool IsLogger;
+            public bool IsException;
+            public bool IsLogLevel;
+            public bool IsEnumerable;
+            // A parameter flagged as IsTemplateParameter is not going to be taken care of specially as an argument to ILogger.Log
+            // but instead is supposed to be taken as a parameter for the template.
+            public bool IsTemplateParameter => !IsLogger && !IsException && !IsLogLevel;
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs
new file mode 100644 (file)
index 0000000..9c1d2cb
--- /dev/null
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+[assembly: System.Resources.NeutralResourcesLanguage("en-us")]
+
+namespace Microsoft.Extensions.Logging.Generators
+{
+    [Generator]
+    public partial class LoggerMessageGenerator : ISourceGenerator
+    {
+        [ExcludeFromCodeCoverage]
+        public void Initialize(GeneratorInitializationContext context)
+        {
+            context.RegisterForSyntaxNotifications(SyntaxReceiver.Create);
+        }
+
+        [ExcludeFromCodeCoverage]
+        public void Execute(GeneratorExecutionContext context)
+        {
+            if (context.SyntaxReceiver is not SyntaxReceiver receiver || receiver.ClassDeclarations.Count == 0)
+            {
+                // nothing to do yet
+                return;
+            }
+
+            var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken);
+            IReadOnlyList<LoggerClass> logClasses = p.GetLogClasses(receiver.ClassDeclarations);
+            if (logClasses.Count > 0)
+            {
+                var e = new Emitter();
+                string result = e.Emit(logClasses, context.CancellationToken);
+    
+                context.AddSource(nameof(LoggerMessageGenerator), SourceText.From(result, Encoding.UTF8));
+            }
+        }
+
+        [ExcludeFromCodeCoverage]
+        private sealed class SyntaxReceiver : ISyntaxReceiver
+        {
+            internal static ISyntaxReceiver Create()
+            {
+                return new SyntaxReceiver();
+            }
+
+            public List<ClassDeclarationSyntax> ClassDeclarations { get; } = new ();
+
+            public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+            {
+                if (syntaxNode is ClassDeclarationSyntax classSyntax)
+                {
+                    ClassDeclarations.Add(classSyntax);
+                }
+            }
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj
new file mode 100644 (file)
index 0000000..719c901
--- /dev/null
@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>netstandard2.0</TargetFrameworks>
+    <Nullable>enable</Nullable>
+    <EnableDefaultItems>true</EnableDefaultItems>
+    <EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
+    <UsingToolXliff>true</UsingToolXliff>
+    <CLSCompliant>false</CLSCompliant>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageDestination Include="analyzers\dotnet\cs" />
+  </ItemGroup>
+
+  <Target Name="IncludeSatteliteResourceInPackage" BeforeTargets="GetFilesToPackage" DependsOnTargets="SatelliteDllsProjectOutputGroup">
+    <ItemGroup>
+      <AdditionalFileToPackage Include="%(SatelliteDllsProjectOutputGroupOutput.FullPath)" SubFolder="/%(SatelliteDllsProjectOutputGroupOutput.Culture)" />
+    </ItemGroup>
+  </Target>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisCSharpWorkspacesVersion)" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.DotNet.Build.Tasks.Packaging" Version="$(MicrosoftDotNetBuildTasksPackagingVersion)" PrivateAssets="all" />
+  </ItemGroup>
+
+</Project>
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx
new file mode 100644 (file)
index 0000000..f0d6b21
--- /dev/null
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="InvalidLoggingMethodNameMessage" xml:space="preserve">
+    <value>Logging method names cannot start with _</value>
+  </data>
+  <data name="InvalidLoggingMethodParameterNameMessage" xml:space="preserve">
+    <value>Logging method parameter names cannot start with _</value>
+  </data>
+  <data name="LoggingMethodInNestedTypeMessage" xml:space="preserve">
+    <value>Logging class cannot be in nested types</value>
+  </data>
+  <data name="MissingRequiredTypeTitle" xml:space="preserve">
+    <value>Could not find a required type definition</value>
+  </data>
+  <data name="MissingRequiredTypeMessage" xml:space="preserve">
+    <value>Could not find definition for type {0}</value>
+  </data>
+  <data name="ShouldntReuseEventIdsTitle" xml:space="preserve">
+    <value>Multiple logging methods cannot use the same event id within a class</value>
+  </data>
+  <data name="ShouldntReuseEventIdsMessage" xml:space="preserve">
+    <value>Multiple logging methods are using event id {0} in class {1}</value>
+  </data>
+  <data name="LoggingMethodMustReturnVoidMessage" xml:space="preserve">
+    <value>Logging methods must return void</value>
+  </data>
+  <data name="MissingLoggerArgumentMessage" xml:space="preserve">
+    <value>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</value>
+    <comment>{Locked="Microsoft.Extensions.Logging.ILogger"}</comment>
+  </data>
+  <data name="LoggingMethodShouldBeStaticMessage" xml:space="preserve">
+    <value>Logging methods must be static</value>
+  </data>
+  <data name="LoggingMethodMustBePartialMessage" xml:space="preserve">
+    <value>Logging methods must be partial</value>
+  </data>
+  <data name="LoggingMethodIsGenericMessage" xml:space="preserve">
+    <value>Logging methods cannot be generic</value>
+  </data>
+  <data name="ShouldntMentionInTemplateMessage" xml:space="preserve">
+    <value>Don't include a template for {0} in the logging message since it is implicitly taken care of</value>
+  </data>
+  <data name="ShouldntMentionExceptionInMessageTitle" xml:space="preserve">
+    <value>Don't include exception parameters as templates in the logging message</value>
+  </data>
+  <data name="RedundantQualifierInMessageMessage" xml:space="preserve">
+    <value>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</value>
+  </data>
+  <data name="RedundantQualifierInMessageTitle" xml:space="preserve">
+    <value>Redundant qualifier in logging message</value>
+  </data>
+  <data name="ArgumentHasNoCorrespondingTemplateMessage" xml:space="preserve">
+    <value>Argument {0} is not referenced from the logging message</value>
+  </data>
+  <data name="ArgumentHasNoCorrespondingTemplateTitle" xml:space="preserve">
+    <value>Argument is not referenced from the logging message</value>
+  </data>
+  <data name="TemplateHasNoCorrespondingArgumentMessage" xml:space="preserve">
+    <value>Template {0} is not provided as argument to the logging method</value>
+  </data>
+  <data name="TemplateHasNoCorrespondingArgumentTitle" xml:space="preserve">
+    <value>Logging template has no corresponding method argument</value>
+  </data>
+  <data name="LoggingMethodHasBodyMessage" xml:space="preserve">
+    <value>Logging methods cannot have a body</value>
+  </data>
+  <data name="MissingLogLevelMessage" xml:space="preserve">
+    <value>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</value>
+  </data>
+  <data name="ShouldntMentionLogLevelInMessageTitle" xml:space="preserve">
+    <value>Don't include log level parameters as templates in the logging message</value>
+  </data>
+  <data name="ShouldntMentionLoggerInMessageTitle" xml:space="preserve">
+    <value>Don't include logger parameters as templates in the logging message</value>
+  </data>
+  <data name="MissingLoggerFieldMessage" xml:space="preserve">
+    <value>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</value>
+    <comment>{Locked="Microsoft.Extensions.Logging.ILogger"}</comment>
+  </data>
+  <data name="MissingLoggerFieldTitle" xml:space="preserve">
+    <value>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</value>
+    <comment>{Locked="Microsoft.Extensions.Logging.ILogger"}</comment>
+  </data>
+  <data name="MultipleLoggerFieldsMessage" xml:space="preserve">
+    <value>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</value>
+    <comment>{Locked="Microsoft.Extensions.Logging.ILogger"}</comment>
+  </data>
+  <data name="MultipleLoggerFieldsTitle" xml:space="preserve">
+    <value>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</value>
+    <comment>{Locked="Microsoft.Extensions.Logging.ILogger"}</comment>
+  </data>
+  <data name="InconsistentTemplateCasingMessage" xml:space="preserve">
+    <value>Can't have the same template with different casing</value>
+  </data>
+  <data name="MalformedFormatStringsMessage" xml:space="preserve">
+    <value>Can't have malformed format strings (like dangling {, etc)</value>
+  </data>
+  <data name="GeneratingForMax6ArgumentsMessage" xml:space="preserve">
+    <value>Generating more than 6 arguments is not supported</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf
new file mode 100644 (file)
index 0000000..044e1bd
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="cs" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf
new file mode 100644 (file)
index 0000000..258f697
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="de" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf
new file mode 100644 (file)
index 0000000..0424989
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="es" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf
new file mode 100644 (file)
index 0000000..a482372
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="fr" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf
new file mode 100644 (file)
index 0000000..304056d
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="it" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf
new file mode 100644 (file)
index 0000000..c8b172f
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="ja" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf
new file mode 100644 (file)
index 0000000..fdb5357
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="ko" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf
new file mode 100644 (file)
index 0000000..790fec1
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="pl" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf
new file mode 100644 (file)
index 0000000..1451df0
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="pt-BR" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf
new file mode 100644 (file)
index 0000000..66fbc83
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="ru" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf
new file mode 100644 (file)
index 0000000..fc70ba8
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="tr" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf
new file mode 100644 (file)
index 0000000..2cdeaa1
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="zh-Hans" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf
new file mode 100644 (file)
index 0000000..52f6557
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
+  <file datatype="xml" source-language="en" target-language="zh-Hant" original="../Strings.resx">
+    <body>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateMessage">
+        <source>Argument {0} is not referenced from the logging message</source>
+        <target state="new">Argument {0} is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ArgumentHasNoCorrespondingTemplateTitle">
+        <source>Argument is not referenced from the logging message</source>
+        <target state="new">Argument is not referenced from the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="GeneratingForMax6ArgumentsMessage">
+        <source>Generating more than 6 arguments is not supported</source>
+        <target state="new">Generating more than 6 arguments is not supported</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InconsistentTemplateCasingMessage">
+        <source>Can't have the same template with different casing</source>
+        <target state="new">Can't have the same template with different casing</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodNameMessage">
+        <source>Logging method names cannot start with _</source>
+        <target state="new">Logging method names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidLoggingMethodParameterNameMessage">
+        <source>Logging method parameter names cannot start with _</source>
+        <target state="new">Logging method parameter names cannot start with _</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodHasBodyMessage">
+        <source>Logging methods cannot have a body</source>
+        <target state="new">Logging methods cannot have a body</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodInNestedTypeMessage">
+        <source>Logging class cannot be in nested types</source>
+        <target state="new">Logging class cannot be in nested types</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodIsGenericMessage">
+        <source>Logging methods cannot be generic</source>
+        <target state="new">Logging methods cannot be generic</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustBePartialMessage">
+        <source>Logging methods must be partial</source>
+        <target state="new">Logging methods must be partial</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodMustReturnVoidMessage">
+        <source>Logging methods must return void</source>
+        <target state="new">Logging methods must return void</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="LoggingMethodShouldBeStaticMessage">
+        <source>Logging methods must be static</source>
+        <target state="new">Logging methods must be static</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MalformedFormatStringsMessage">
+        <source>Can't have malformed format strings (like dangling {, etc)</source>
+        <target state="new">Can't have malformed format strings (like dangling {, etc)</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLogLevelMessage">
+        <source>A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</source>
+        <target state="new">A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingLoggerArgumentMessage">
+        <source>One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</source>
+        <target state="new">One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldMessage">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingLoggerFieldTitle">
+        <source>Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Couldn't find a field of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeMessage">
+        <source>Could not find definition for type {0}</source>
+        <target state="new">Could not find definition for type {0}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MissingRequiredTypeTitle">
+        <source>Could not find a required type definition</source>
+        <target state="new">Could not find a required type definition</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsMessage">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="MultipleLoggerFieldsTitle">
+        <source>Found multiple fields of type Microsoft.Extensions.Logging.ILogger</source>
+        <target state="new">Found multiple fields of type Microsoft.Extensions.Logging.ILogger</target>
+        <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageMessage">
+        <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
+        <target state="new">Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="RedundantQualifierInMessageTitle">
+        <source>Redundant qualifier in logging message</source>
+        <target state="new">Redundant qualifier in logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionExceptionInMessageTitle">
+        <source>Don't include exception parameters as templates in the logging message</source>
+        <target state="new">Don't include exception parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionInTemplateMessage">
+        <source>Don't include a template for {0} in the logging message since it is implicitly taken care of</source>
+        <target state="new">Don't include a template for {0} in the logging message since it is implicitly taken care of</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLogLevelInMessageTitle">
+        <source>Don't include log level parameters as templates in the logging message</source>
+        <target state="new">Don't include log level parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntMentionLoggerInMessageTitle">
+        <source>Don't include logger parameters as templates in the logging message</source>
+        <target state="new">Don't include logger parameters as templates in the logging message</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsMessage">
+        <source>Multiple logging methods are using event id {0} in class {1}</source>
+        <target state="new">Multiple logging methods are using event id {0} in class {1}</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ShouldntReuseEventIdsTitle">
+        <source>Multiple logging methods cannot use the same event id within a class</source>
+        <target state="new">Multiple logging methods cannot use the same event id within a class</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentMessage">
+        <source>Template {0} is not provided as argument to the logging method</source>
+        <target state="new">Template {0} is not provided as argument to the logging method</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="TemplateHasNoCorrespondingArgumentTitle">
+        <source>Logging template has no corresponding method argument</source>
+        <target state="new">Logging template has no corresponding method argument</target>
+        <note />
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
\ No newline at end of file
index b1aeb27..5180cb1 100644 (file)
@@ -4,6 +4,7 @@
     <ProjectReference Include="..\src\Microsoft.Extensions.Logging.csproj">
       <SupportedFramework>net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks)</SupportedFramework>
     </ProjectReference>
+    <ProjectReference Include="..\gen\Microsoft.Extensions.Logging.Generators.csproj" />
   </ItemGroup>
   <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />
 </Project> 
\ No newline at end of file
index bb2d201..a81c21d 100644 (file)
@@ -24,7 +24,7 @@ namespace Microsoft.Extensions.Logging
         TraceState = 8,
         TraceFlags = 16,
         Tags = 32,
-        Baggage = 64
+        Baggage = 64,
     }
     public static partial class FilterLoggingBuilderExtensions
     {
@@ -85,6 +85,15 @@ namespace Microsoft.Extensions.Logging
         public string ProviderName { get { throw null; } }
         public override string ToString() { throw null; }
     }
+    [System.AttributeUsageAttribute(System.AttributeTargets.Method)]
+    public sealed partial class LoggerMessageAttribute : System.Attribute
+    {
+        public LoggerMessageAttribute() { }
+        public int EventId { get { throw null; } set { } }
+        public string? EventName { get { throw null; } set { } }
+        public Microsoft.Extensions.Logging.LogLevel Level { get { throw null; } set { } }
+        public string Message { get { throw null; } set { } }
+    }
     public static partial class LoggingBuilderExtensions
     {
         public static Microsoft.Extensions.Logging.ILoggingBuilder AddProvider(this Microsoft.Extensions.Logging.ILoggingBuilder builder, Microsoft.Extensions.Logging.ILoggerProvider provider) { throw null; }
diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs
new file mode 100644 (file)
index 0000000..b103ef3
--- /dev/null
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.Extensions.Logging
+{
+    /// <summary>
+    /// Provides information to guide the production of a strongly-typed logging method.
+    /// </summary>
+    /// <remarks>
+    /// <para>The method this attribute is applied to:</para>
+    /// <para>   - Must be a partial method.</para>
+    /// <para>   - Must return <c>void</c>.</para>
+    /// <para>   - Must not be generic.</para>
+    /// <para>   - Must have an <see cref="ILogger"/> as one of its parameters.</para>
+    /// <para>   - Must have a <see cref="Microsoft.Extensions.Logging.LogLevel"/> as one of its parameters.</para>
+    /// <para>   - None of the parameters can be generic.</para>
+    /// </remarks>
+    /// <example>
+    /// <format type="text/markdown"><![CDATA[
+    /// ```csharp
+    /// static partial class Log
+    /// {
+    ///     [LoggerMessage(EventId = 0, Message = "Could not open socket for {hostName}")]
+    ///     static partial void CouldNotOpenSocket(ILogger logger, LogLevel level, string hostName);
+    /// }
+    /// ```
+    /// ]]></format>
+    /// </example>
+    [AttributeUsage(AttributeTargets.Method)]
+    public sealed class LoggerMessageAttribute : Attribute
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="LoggerMessageAttribute"/> class
+        /// which is used to guide the production of a strongly-typed logging method.
+        /// </summary>
+        public LoggerMessageAttribute() { }
+
+        /// <summary>
+        /// Gets the logging event id for the logging method.
+        /// </summary>
+        public int EventId { get; set; } = -1;
+
+        /// <summary>
+        /// Gets or sets the logging event name for the logging method.
+        /// </summary>
+        /// <remarks>
+        /// This will equal the method name if not specified.
+        /// </remarks>
+        public string? EventName { get; set; }
+
+        /// <summary>
+        /// Gets the logging level for the logging method.
+        /// </summary>
+        public LogLevel Level { get; set; } = LogLevel.None;
+
+        /// <summary>
+        /// Gets the message text for the logging method.
+        /// </summary>
+        public string Message { get; set; } = "";
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt
new file mode 100644 (file)
index 0000000..7a20263
--- /dev/null
@@ -0,0 +1,57 @@
+// <auto-generated/>
+#nullable enable
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    partial class TestWithDynamicLogLevel 
+    {
+        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")]
+        private readonly struct __M9Struct : global::System.Collections.Generic.IReadOnlyList<global::System.Collections.Generic.KeyValuePair<string, object?>>
+        {
+
+            public override string ToString()
+            {
+
+                return $"M9";
+            }
+
+            public static string Format(__M9Struct state, global::System.Exception? ex) => state.ToString();
+
+            public int Count => 1;
+
+            public global::System.Collections.Generic.KeyValuePair<string, object?> this[int index]
+            {
+                get => index switch
+                {
+                    0 => new global::System.Collections.Generic.KeyValuePair<string, object?>("{OriginalFormat}", "M9"),
+
+                    _ => throw new global::System.IndexOutOfRangeException(nameof(index)),  // return the same exception LoggerMessage.Define returns in this case
+                };
+            }
+
+            public global::System.Collections.Generic.IEnumerator<global::System.Collections.Generic.KeyValuePair<string, object?>> GetEnumerator()
+            {
+                for (int i = 0; i < 1; i++)
+                {
+                    yield return this[i];
+                }
+            }
+
+            global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
+        }
+
+        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")]
+        public static partial void M9(global::Microsoft.Extensions.Logging.LogLevel level, global::Microsoft.Extensions.Logging.ILogger logger)
+        {
+            if (logger.IsEnabled(level))
+            {
+                logger.Log(
+                    level,
+                    new global::Microsoft.Extensions.Logging.EventId(9, nameof(M9)),
+                    new __M9Struct(),
+                    null,
+                    __M9Struct.Format);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt
new file mode 100644 (file)
index 0000000..32b7629
--- /dev/null
@@ -0,0 +1,90 @@
+// <auto-generated/>
+#nullable enable
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    partial class TestWithMoreThan6Params 
+    {
+        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")]
+        private readonly struct __Method9Struct : global::System.Collections.Generic.IReadOnlyList<global::System.Collections.Generic.KeyValuePair<string, object?>>
+        {
+            private readonly global::System.Int32 _p1;
+            private readonly global::System.Int32 _p2;
+            private readonly global::System.Int32 _p3;
+            private readonly global::System.Int32 _p4;
+            private readonly global::System.Int32 _p5;
+            private readonly global::System.Int32 _p6;
+            private readonly 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)
+            {
+                this._p1 = p1;
+                this._p2 = p2;
+                this._p3 = p3;
+                this._p4 = p4;
+                this._p5 = p5;
+                this._p6 = p6;
+                this._p7 = p7;
+
+            }
+
+            public override string ToString()
+            {
+                var p1 = this._p1;
+                var p2 = this._p2;
+                var p3 = this._p3;
+                var p4 = this._p4;
+                var p5 = this._p5;
+                var p6 = this._p6;
+                var p7 = this._p7;
+
+                return $"M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}";
+            }
+
+            public static string Format(__Method9Struct state, global::System.Exception? ex) => state.ToString();
+
+            public int Count => 8;
+
+            public global::System.Collections.Generic.KeyValuePair<string, object?> this[int index]
+            {
+                get => index switch
+                {
+                    0 => new global::System.Collections.Generic.KeyValuePair<string, object?>("p1", this._p1),
+                    1 => new global::System.Collections.Generic.KeyValuePair<string, object?>("p2", this._p2),
+                    2 => new global::System.Collections.Generic.KeyValuePair<string, object?>("p3", this._p3),
+                    3 => new global::System.Collections.Generic.KeyValuePair<string, object?>("p4", this._p4),
+                    4 => new global::System.Collections.Generic.KeyValuePair<string, object?>("p5", this._p5),
+                    5 => new global::System.Collections.Generic.KeyValuePair<string, object?>("p6", this._p6),
+                    6 => new global::System.Collections.Generic.KeyValuePair<string, object?>("p7", this._p7),
+                    7 => new global::System.Collections.Generic.KeyValuePair<string, object?>("{OriginalFormat}", "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"),
+
+                    _ => throw new global::System.IndexOutOfRangeException(nameof(index)),  // return the same exception LoggerMessage.Define returns in this case
+                };
+            }
+
+            public global::System.Collections.Generic.IEnumerator<global::System.Collections.Generic.KeyValuePair<string, object?>> GetEnumerator()
+            {
+                for (int i = 0; i < 8; i++)
+                {
+                    yield return this[i];
+                }
+            }
+
+            global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
+        }
+
+        [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)
+        {
+            if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error))
+            {
+                logger.Log(
+                    global::Microsoft.Extensions.Logging.LogLevel.Error,
+                    new global::Microsoft.Extensions.Logging.EventId(8, nameof(Method9)),
+                    new __Method9Struct(p1, p2, p3, p4, p5, p6, p7),
+                    null,
+                    __Method9Struct.Format);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt
new file mode 100644 (file)
index 0000000..866b046
--- /dev/null
@@ -0,0 +1,21 @@
+// <auto-generated/>
+#nullable enable
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    partial class TestWithOneParam 
+    {
+        [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); 
+
+        [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)
+        {
+            if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error))
+            {
+                __M0Callback(logger, a1, null);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs
new file mode 100644 (file)
index 0000000..7e0661d
--- /dev/null
@@ -0,0 +1,437 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.Logging.Generators.Tests.TestClasses;
+using Xunit;
+
+namespace Microsoft.Extensions.Logging.Generators.Tests
+{
+    [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)]
+    public class LoggerMessageGeneratedCodeTests
+    {
+        [Fact]
+        public void BasicTests()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+            NoNamespace.CouldNotOpenSocket(logger, "microsoft.com");
+            Assert.Equal(LogLevel.Critical, logger.LastLogLevel);
+            Assert.Null(logger.LastException);
+            Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            Level1.OneLevelNamespace.CouldNotOpenSocket(logger, "microsoft.com");
+            Assert.Equal(LogLevel.Critical, logger.LastLogLevel);
+            Assert.Null(logger.LastException);
+            Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            Level1.Level2.TwoLevelNamespace.CouldNotOpenSocket(logger, "microsoft.com");
+            Assert.Equal(LogLevel.Critical, logger.LastLogLevel);
+            Assert.Null(logger.LastException);
+            Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+        }
+
+        [Fact]
+        public void EnableTest()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+            logger.Enabled = false;
+            NoNamespace.CouldNotOpenSocket(logger, "microsoft.com");
+            Assert.Equal(0, logger.CallCount);          // ensure the logger doesn't get called when it is disabled
+        }
+
+        [Fact]
+        public void ArgTest()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+            ArgTestExtensions.Method1(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M1", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ArgTestExtensions.Method2(logger, "arg1");
+            Assert.Null(logger.LastException);
+            Assert.Equal("M2 arg1", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ArgTestExtensions.Method3(logger, "arg1", 2);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M3 arg1 2", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ArgTestExtensions.Method4(logger, new InvalidOperationException("A"));
+            Assert.Equal("A", logger.LastException!.Message);
+            Assert.Equal("M4", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ArgTestExtensions.Method5(logger, new InvalidOperationException("A"), new InvalidOperationException("B"));
+            Assert.Equal("A", logger.LastException!.Message);
+            Assert.Equal("M5 System.InvalidOperationException: B", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ArgTestExtensions.Method6(logger, new InvalidOperationException("A"), 2);
+            Assert.Equal("A", logger.LastException!.Message);
+            Assert.Equal("M6 2", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ArgTestExtensions.Method7(logger, 1, new InvalidOperationException("B"));
+            Assert.Equal("B", logger.LastException!.Message);
+            Assert.Equal("M7 1", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ArgTestExtensions.Method8(logger, 1, 2, 3, 4, 5, 6, 7);
+            Assert.Equal("M81234567", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ArgTestExtensions.Method9(logger, 1, 2, 3, 4, 5, 6, 7);
+            Assert.Equal("M9 1 2 3 4 5 6 7", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ArgTestExtensions.Method10(logger, 1);
+            Assert.Equal("M101", logger.LastFormattedString);
+            Assert.Equal(1, logger.CallCount);
+        }
+
+        [Fact]
+        public void CollectionTest()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+            CollectionTestExtensions.M0(logger);
+            TestCollection(1, logger);
+
+            logger.Reset();
+            CollectionTestExtensions.M1(logger, 0);
+            TestCollection(2, logger);
+
+            logger.Reset();
+            CollectionTestExtensions.M2(logger, 0, 1);
+            TestCollection(3, logger);
+
+            logger.Reset();
+            CollectionTestExtensions.M3(logger, 0, 1, 2);
+            TestCollection(4, logger);
+
+            logger.Reset();
+            CollectionTestExtensions.M4(logger, 0, 1, 2, 3);
+            TestCollection(5, logger);
+
+            logger.Reset();
+            CollectionTestExtensions.M5(logger, 0, 1, 2, 3, 4);
+            TestCollection(6, logger);
+
+            logger.Reset();
+            CollectionTestExtensions.M6(logger, 0, 1, 2, 3, 4, 5);
+            TestCollection(7, logger);
+
+            logger.Reset();
+            CollectionTestExtensions.M7(logger, 0, 1, 2, 3, 4, 5, 6);
+            TestCollection(8, logger);
+
+            logger.Reset();
+            CollectionTestExtensions.M8(logger, 0, 1, 2, 3, 4, 5, 6, 7);
+            TestCollection(9, logger);
+
+            logger.Reset();
+            CollectionTestExtensions.M9(logger, LogLevel.Critical, 0, new ArgumentException("Foo"), 1);
+            TestCollection(3, logger);
+
+            Assert.True(true);
+        }
+
+        [Fact]
+        public void MessageTests()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+            MessageTestExtensions.M0(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal(string.Empty, logger.LastFormattedString);
+            Assert.Equal(LogLevel.Trace, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            MessageTestExtensions.M1(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal(string.Empty, logger.LastFormattedString);
+            Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/dotnet/roslyn/issues/52527")]
+        public void MessageTests_SuppressWarning_WarnAsError_NoError()
+        {
+            // Diagnostics produced by source generators do not respect the /warnAsError or /noWarn compiler flags.
+            // These are handled fine by the logger generator and generate warnings. Unfortunately, the warning suppression is
+            // not being observed by the C# compiler at the moment, so having these here causes build warnings.
+#if false
+            var logger = new MockLogger();
+
+            logger.Reset();
+            MessageTestExtensions.M2(logger, "Foo", "Bar");
+            Assert.Null(logger.LastException);
+            Assert.Equal(string.Empty, logger.LastFormattedString);
+            AssertLastState(logger,
+                new KeyValuePair<string, object?>("p1", "Foo"),
+                new KeyValuePair<string, object?>("p2", "Bar"),
+                new KeyValuePair<string, object?>("{OriginalFormat}", string.Empty));
+            Assert.Equal(LogLevel.Trace, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            MessageTestExtensions.M3(logger, "Foo", 42);
+            Assert.Null(logger.LastException);
+            Assert.Equal(string.Empty, logger.LastFormattedString);
+            AssertLastState(logger,
+                new KeyValuePair<string, object?>("p1", "Foo"),
+                new KeyValuePair<string, object?>("p2", 42),
+                new KeyValuePair<string, object?>("{OriginalFormat}", string.Empty));
+            Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+#endif
+        }
+
+        [Fact]
+        public void InstanceTests()
+        {
+            var logger = new MockLogger();
+            var o = new TestInstances(logger);
+
+            logger.Reset();
+            o.M0();
+            Assert.Null(logger.LastException);
+            Assert.Equal("M0", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Error, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            o.M1("Foo");
+            Assert.Null(logger.LastException);
+            Assert.Equal("M1 Foo", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Trace, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+        }
+
+        [Fact]
+        public void LevelTests()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+            LevelTestExtensions.M0(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M0", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Trace, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            LevelTestExtensions.M1(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M1", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            LevelTestExtensions.M2(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M2", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Information, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            LevelTestExtensions.M3(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M3", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Warning, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            LevelTestExtensions.M4(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M4", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Error, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            LevelTestExtensions.M5(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M5", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Critical, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            LevelTestExtensions.M6(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M6", logger.LastFormattedString);
+            Assert.Equal(LogLevel.None, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            LevelTestExtensions.M7(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M7", logger.LastFormattedString);
+            Assert.Equal((LogLevel)42, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            LevelTestExtensions.M8(logger, LogLevel.Critical);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M8", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Critical, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            LevelTestExtensions.M9(LogLevel.Trace, logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M9", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Trace, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+        }
+
+        [Fact]
+        public void ExceptionTests()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+            ExceptionTestExtensions.M0(logger, new ArgumentException("Foo"), new ArgumentException("Bar"));
+            Assert.Equal("Foo", logger.LastException!.Message);
+            Assert.Equal("M0 System.ArgumentException: Bar", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Trace, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+
+            logger.Reset();
+            ExceptionTestExtensions.M1(new ArgumentException("Foo"), logger, new ArgumentException("Bar"));
+            Assert.Equal("Foo", logger.LastException!.Message);
+            Assert.Equal("M1 System.ArgumentException: Bar", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+        }
+
+        [Fact]
+        public void EventNameTests()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+            EventNameTestExtensions.M0(logger);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M0", logger.LastFormattedString);
+            Assert.Equal(LogLevel.Trace, logger.LastLogLevel);
+            Assert.Equal(1, logger.CallCount);
+            Assert.Equal("CustomEventName", logger.LastEventId.Name);
+        }
+
+        [Fact]
+        public void TemplateTests()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+            TemplateTestExtensions.M0(logger, 0);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M0 0", logger.LastFormattedString);
+            AssertLastState(logger,
+                new KeyValuePair<string, object?>("A1", 0),
+                new KeyValuePair<string, object?>("{OriginalFormat}", "M0 {A1}"));
+
+            logger.Reset();
+            TemplateTestExtensions.M1(logger, 42);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M1 42 42", logger.LastFormattedString);
+            AssertLastState(logger,
+                new KeyValuePair<string, object?>("A1", 42),
+                new KeyValuePair<string, object?>("{OriginalFormat}", "M1 {A1} {A1}"));
+
+            logger.Reset();
+            TemplateTestExtensions.M2(logger, 42, 43, 44, 45, 46, 47, 48);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M2 42 43 44 45 46 47 48", logger.LastFormattedString);
+            AssertLastState(logger,
+                new KeyValuePair<string, object?>("A1", 42),
+                new KeyValuePair<string, object?>("a2", 43),
+                new KeyValuePair<string, object?>("A3", 44),
+                new KeyValuePair<string, object?>("a4", 45),
+                new KeyValuePair<string, object?>("A5", 46),
+                new KeyValuePair<string, object?>("a6", 47),
+                new KeyValuePair<string, object?>("A7", 48),
+                new KeyValuePair<string, object?>("{OriginalFormat}", "M2 {A1} {a2} {A3} {a4} {A5} {a6} {A7}"));
+
+            logger.Reset();
+            TemplateTestExtensions.M3(logger, 42, 43);
+            Assert.Null(logger.LastException);
+            Assert.Equal("M3 43 42", logger.LastFormattedString);
+            AssertLastState(logger,
+                new KeyValuePair<string, object?>("A1", 42),
+                new KeyValuePair<string, object?>("a2", 43),
+                new KeyValuePair<string, object?>("{OriginalFormat}", "M3 {a2} {A1}"));
+
+        }
+
+        private static void AssertLastState(MockLogger logger, params KeyValuePair<string, object?>[] expected)
+        {
+            var rol = (IReadOnlyList<KeyValuePair<string, object?>>)logger.LastState!;
+            int count = 0;
+            foreach (var kvp in expected)
+            {
+                Assert.Equal(kvp.Key, rol[count].Key);
+                Assert.Equal(kvp.Value, rol[count].Value);
+                count++;
+            }
+        }
+
+        private static void TestCollection(int expected, MockLogger logger)
+        {
+            var rol = (logger.LastState as IReadOnlyList<KeyValuePair<string, object?>>)!;
+            Assert.NotNull(rol);
+
+            Assert.Equal(expected, rol.Count);
+            for (int i = 0; i < expected; i++)
+            {
+                if (i != expected - 1)
+                {
+                    var kvp = new KeyValuePair<string, object?>($"p{i}", i);
+                    Assert.Equal(kvp, rol[i]);
+                }
+            }
+
+            int count = 0;
+            foreach (var actual in rol)
+            {
+                if (count != expected - 1)
+                {
+                    var kvp = new KeyValuePair<string, object?>($"p{count}", count);
+                    Assert.Equal(kvp, actual);
+                }
+
+                count++;
+            }
+
+            Assert.Equal(expected, count);
+            Assert.Throws<IndexOutOfRangeException>(() => rol[expected]);
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs
new file mode 100644 (file)
index 0000000..c66a267
--- /dev/null
@@ -0,0 +1,123 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using SourceGenerators.Tests;
+using Xunit;
+
+namespace Microsoft.Extensions.Logging.Generators.Tests
+{
+    [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)]
+    public class LoggerMessageGeneratorEmitterTests
+    {
+        [Fact]
+        public async Task TestEmitter()
+        {
+            // The functionality of the resulting code is tested via LoggerMessageGeneratedCodeTests.cs
+            string[] sources = Directory.GetFiles("TestClasses");
+            foreach (var src in sources)
+            {
+                var testSourceCode = await File.ReadAllTextAsync(src).ConfigureAwait(false);
+
+                var (d, r) = await RoslynTestUtils.RunGenerator(
+                    new LoggerMessageGenerator(),
+                    new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly },
+                    new[] { testSourceCode }).ConfigureAwait(false);
+
+                Assert.Empty(d);
+                Assert.Single(r);
+            }
+        }
+
+        [Fact]
+        public async Task TestBaseline_TestWithOneParam_Success()
+        {
+            string testSourceCode = @"
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class TestWithOneParam
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = ""M0 {A1}"")]
+        public static partial void M0(ILogger logger, int a1);
+    }
+}";
+            await VerifyAgainstBaselineUsingFile("TestWithOneParam.generated.txt", testSourceCode);
+        }
+
+        [Fact]
+        public async Task TestBaseline_TestWithMoreThan6Params_Success()
+        {
+            // TODO: Remove support for more than 6 arguments
+            string testSourceCode = @"
+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);
+    }
+}";
+            await VerifyAgainstBaselineUsingFile("TestWithMoreThan6Params.generated.txt", testSourceCode);
+        }
+
+        [Fact]
+        public async Task TestBaseline_TestWithDynamicLogLevel_Success()
+        {
+            string testSourceCode = @"
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class TestWithDynamicLogLevel
+    {
+        [LoggerMessage(EventId = 9, Message = ""M9"")]
+        public static partial void M9(LogLevel level, ILogger logger);
+    }
+}";
+            await VerifyAgainstBaselineUsingFile("TestWithDynamicLogLevel.generated.txt", testSourceCode);
+        }
+
+        private async Task VerifyAgainstBaselineUsingFile(string filename, string testSourceCode)
+        {
+            string[] expectedLines = await File.ReadAllLinesAsync(Path.Combine("Baselines", filename)).ConfigureAwait(false);
+
+            var (d, r) = await RoslynTestUtils.RunGenerator(
+                new LoggerMessageGenerator(),
+                new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly },
+                new[] { testSourceCode }).ConfigureAwait(false);
+
+            Assert.Empty(d);
+            Assert.Single(r);
+
+            Assert.True(CompareLines(expectedLines, r[0].SourceText,
+                out string errorMessage), errorMessage);
+        }
+
+        private bool CompareLines(string[] expectedLines, SourceText sourceText, out string message)
+        {
+            if (expectedLines.Length != sourceText.Lines.Count)
+            {
+                message = string.Format("Line numbers do not match. Expected: {0} lines, but generated {1}",
+                    expectedLines.Length, sourceText.Lines.Count);
+                return false;
+            }
+            int index = 0;
+            foreach (TextLine textLine in sourceText.Lines)
+            {
+                string expectedLine = expectedLines[index];
+                if (!expectedLine.Equals(textLine.ToString(), StringComparison.Ordinal))
+                {
+                    message = string.Format("Line {0} does not match.{1}Expected Line:{1}{2}{1}Actual Line:{1}{3}",
+                        textLine.LineNumber, Environment.NewLine, expectedLine, textLine);
+                    return false;
+                }
+                index++;
+            }
+            message = string.Empty;
+            return true;
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs
new file mode 100644 (file)
index 0000000..1aabf53
--- /dev/null
@@ -0,0 +1,657 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using SourceGenerators.Tests;
+using Xunit;
+
+namespace Microsoft.Extensions.Logging.Generators.Tests
+{
+    [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)]
+    public class LoggerMessageGeneratorParserTests
+    {
+        [Fact]
+        public async Task InvalidMethodName()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    static partial void __M1(ILogger logger);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.InvalidLoggingMethodName.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task MissingLogLevel()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Message = ""M1"")]
+                    static partial void M1(ILogger logger);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.MissingLogLevel.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task InvalidMethodBody()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    static partial void M1(ILogger logger);
+
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    static partial void M1(ILogger logger)
+                    {
+                    }
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.LoggingMethodHasBody.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task MissingTemplate()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""This is a message without foo"")]
+                    static partial void M1(ILogger logger, string foo);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task MissingArgument()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""{foo}"")]
+                    static partial void M1(ILogger logger);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.TemplateHasNoCorrespondingArgument.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task NeedlessQualifierInMessage()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = ""INFO: this is an informative message"")]
+                    static partial void M1(ILogger logger);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.RedundantQualifierInMessage.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task NeedlessExceptionInMessage()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {ex} {ex2}"")]
+                    static partial void M1(ILogger logger, System.Exception ex, System.Exception ex2);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.ShouldntMentionExceptionInMessage.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task NeedlessLogLevelInMessage()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Message = ""M1 {l1} {l2}"")]
+                    static partial void M1(ILogger logger, LogLevel l1, LogLevel l2);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.ShouldntMentionLogLevelInMessage.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task NeedlessLoggerInMessage()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {logger}"")]
+                    static partial void M1(ILogger logger);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.ShouldntMentionLoggerInMessage.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task DoubleLogLevel_InAttributeAndAsParameterButMissingInTemplate_ProducesDiagnostic()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    static partial void M1(ILogger logger, LogLevel levelParam);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task LogLevelDoublySet_AndInMessageTemplate_ProducesDiagnostic()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {level2}"")]
+                    static partial void M1(ILogger logger, LogLevel level1, LogLevel level2);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task DoubleLogLevel_FirstOneSetAsMethodParameter_SecondOneInMessageTemplate_Supported()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Message = ""M1 {level2}"")]
+                    static partial void M1(ILogger logger, LogLevel level1, LogLevel level2);
+                }
+            ");
+
+            Assert.Empty(diagnostics);
+        }
+
+#if false
+        // TODO: can't have the same template with different casing
+        [Fact]
+        public async Task InconsistentTemplateCasing()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {p1} {P1}"")]
+                    static partial void M1(ILogger logger, int p1, int P1);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.InconsistentTemplateCasing.Id, diagnostics[0].Id);
+        }
+
+        // TODO: can't have malformed format strings (like dangling {, etc)
+        [Fact]
+        public async Task MalformedFormatString()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {p1} {P1}"")]
+                    static partial void M1(ILogger logger, int p1, int P1);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.MalformedFormatStrings.Id, diagnostics[0].Id);
+        }
+#endif
+
+        [Fact]
+        public async Task InvalidParameterName()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {__foo}"")]
+                    static partial void M1(ILogger logger, string __foo);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.InvalidLoggingMethodParameterName.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task NestedType()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    public partial class Nested
+                    {
+                        [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                        static partial void M1(ILogger logger);
+                    }
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.LoggingMethodInNestedType.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task MissingExceptionType()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                namespace System
+                {
+                    public class Object {}
+                    public class Void {}
+                    public class String {}
+                    public struct DateTime {}
+                }
+                namespace System.Collections
+                {
+                    public interface IEnumerable {}
+                }
+                namespace Microsoft.Extensions.Logging
+                {
+                    public enum LogLevel {}
+                    public interface ILogger {}
+                }
+                namespace Microsoft.Extensions.Logging
+                {
+                    public class LoggerMessageAttribute {}
+                }
+                partial class C
+                {
+                }
+            ", false, includeBaseReferences: false, includeLoggingReferences: false);
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task MissingStringType()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                namespace System
+                {
+                    public class Object {}
+                    public class Void {}
+                    public class Exception {}
+                    public struct DateTime {}
+                }
+                namespace System.Collections
+                {
+                    public interface IEnumerable {}
+                }
+                namespace Microsoft.Extensions.Logging
+                {
+                    public enum LogLevel {}
+                    public interface ILogger {}
+                }
+                namespace Microsoft.Extensions.Logging
+                {
+                    public class LoggerMessageAttribute {}
+                }
+                partial class C
+                {
+                }
+            ", false, includeBaseReferences: false, includeLoggingReferences: false);
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task MissingEnumerableType()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                namespace System
+                {
+                    public class Object {}
+                    public class Void {}
+                    public class Exception {}
+                    public struct DateTime {}
+                    public class String {}
+                }
+                namespace Microsoft.Extensions.Logging
+                {
+                    public enum LogLevel {}
+                    public interface ILogger {}
+                }
+                namespace Microsoft.Extensions.Logging
+                {
+                    public class LoggerMessageAttribute {}
+                }
+                partial class C
+                {
+                }
+            ", false, includeBaseReferences: false, includeLoggingReferences: false);
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task MissingLoggerMessageAttributeType()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                }
+            ", false, includeLoggingReferences: false);
+
+            Assert.Empty(diagnostics);
+        }
+
+        [Fact]
+        public async Task MissingILoggerType()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                namespace Microsoft.Extensions.Logging
+                {
+                    public sealed class LoggerMessageAttribute : System.Attribute {}
+                }
+                partial class C
+                {
+                }
+            ", false, includeLoggingReferences: false);
+
+            Assert.Empty(diagnostics);
+        }
+
+        [Fact]
+        public async Task MissingLogLevelType()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                namespace Microsoft.Extensions.Logging
+                {
+                    public sealed class LoggerMessageAttribute : System.Attribute {}
+                }
+                namespace Microsoft.Extensions.Logging
+                {
+                    public interface ILogger {}
+                }
+                partial class C
+                {
+                }
+            ", false, includeLoggingReferences: false);
+
+            Assert.Empty(diagnostics);
+        }
+
+        [Fact]
+        public async Task EventIdReuse()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class MyClass
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    static partial void M1(ILogger logger);
+
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    static partial void M2(ILogger logger);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.ShouldntReuseEventIds.Id, diagnostics[0].Id);
+            Assert.Contains("in class MyClass", diagnostics[0].GetMessage(), StringComparison.InvariantCulture);
+        }
+
+        [Fact]
+        public async Task MethodReturnType()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    public static partial int M1(ILogger logger);
+
+                    public static partial int M1(ILogger logger) { return 0; }
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.LoggingMethodMustReturnVoid.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task MissingILogger()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {p1}"")]
+                    static partial void M1(int p1);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.MissingLoggerArgument.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task NotStatic()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    partial void M1(ILogger logger);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.LoggingMethodShouldBeStatic.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task NoILoggerField()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    public partial void M1();
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.MissingLoggerField.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task MultipleILoggerFields()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    public ILogger _logger1;
+                    public ILogger _logger2;
+
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    public partial void M1();
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.MultipleLoggerFields.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task NotPartial()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    static void M1(ILogger logger) {}
+                }
+            ");
+
+            Assert.Equal(2, diagnostics.Count);
+            Assert.Equal(DiagnosticDescriptors.LoggingMethodMustBePartial.Id, diagnostics[0].Id);
+            Assert.Equal(DiagnosticDescriptors.LoggingMethodHasBody.Id, diagnostics[1].Id);
+        }
+
+        [Fact]
+        public async Task MethodGeneric()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    static partial void M1<T>(ILogger logger);
+                }
+            ");
+
+            Assert.Single(diagnostics);
+            Assert.Equal(DiagnosticDescriptors.LoggingMethodIsGeneric.Id, diagnostics[0].Id);
+        }
+
+        [Fact]
+        public async Task Templates()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = ""M1"")]
+                    static partial void M1(ILogger logger);
+
+                    [LoggerMessage(EventId = 2, Level = LogLevel.Debug, Message = ""M2 {arg1} {arg2}"")]
+                    static partial void M2(ILogger logger, string arg1, string arg2);
+
+                    [LoggerMessage(EventId = 3, Level = LogLevel.Debug, Message = ""M3 {arg1"")]
+                    static partial void M3(ILogger logger);
+
+                    [LoggerMessage(EventId = 4, Level = LogLevel.Debug, Message = ""M4 arg1}"")]
+                    static partial void M4(ILogger logger);
+
+                    [LoggerMessage(EventId = 5, Level = LogLevel.Debug, Message = ""M5 {"")]
+                    static partial void M5(ILogger logger);
+
+                    [LoggerMessage(EventId = 6, Level = LogLevel.Debug, Message = ""}M6 "")]
+                    static partial void M6(ILogger logger);
+
+                    [LoggerMessage(EventId = 7, Level = LogLevel.Debug, Message = ""M7 {{arg1}}"")]
+                    static partial void M7(ILogger logger);
+                }
+            ");
+
+            Assert.Empty(diagnostics);
+        }
+
+        [Fact]
+        public async Task Cancellation()
+        {
+            await Assert.ThrowsAsync<OperationCanceledException>(async () =>
+                await RunGenerator(@"
+                partial class C
+                {
+                    [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
+                    static partial void M1(ILogger logger);
+                }
+            ", cancellationToken: new CancellationToken(true)));
+        }
+
+        [Fact]
+        public async Task SourceErrors()
+        {
+            IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
+                static partial class C
+                {
+                    // bogus argument type
+                    [LoggerMessage(EventId = 0, Level = "", Message = ""Hello"")]
+                    static partial void M1(ILogger logger);
+
+                    // missing parameter name
+                    [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = ""Hello"")]
+                    static partial void M2(ILogger);
+
+                    // bogus parameter type
+                    [LoggerMessage(EventId = 2, Level = LogLevel.Debug, Message = ""Hello"")]
+                    static partial void M3(XILogger logger);
+
+                    // attribute applied to something other than a method
+                    [LoggerMessage(EventId = 4, Message = ""Hello"")]
+                    int M5;
+                }
+            ");
+
+            Assert.Empty(diagnostics);    // should fail quietly on broken code
+        }
+
+        private static async Task<IReadOnlyList<Diagnostic>> RunGenerator(
+            string code,
+            bool wrap = true,
+            bool inNamespace = true,
+            bool includeBaseReferences = true,
+            bool includeLoggingReferences = true,
+            CancellationToken cancellationToken = default)
+        {
+            var text = code;
+            if (wrap)
+            {
+                var nspaceStart = "namespace Test {";
+                var nspaceEnd = "}";
+                if (!inNamespace)
+                {
+                    nspaceStart = "";
+                    nspaceEnd = "";
+                }
+
+                text = $@"
+                    {nspaceStart}
+                    using Microsoft.Extensions.Logging;
+                    {code}
+                    {nspaceEnd}
+                ";
+            }
+
+            Assembly[]? refs = null;
+            if (includeLoggingReferences)
+            {
+                refs = new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly };
+            }
+
+            var (d, r) = await RoslynTestUtils.RunGenerator(
+                new LoggerMessageGenerator(),
+                refs,
+                new[] { text },
+                includeBaseReferences: includeBaseReferences,
+                cancellationToken: cancellationToken).ConfigureAwait(false);
+
+            return d;
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj
new file mode 100644 (file)
index 0000000..329b67c
--- /dev/null
@@ -0,0 +1,31 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
+    <EnableDefaultItems>true</EnableDefaultItems>
+    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="$(CommonPath)..\tests\SourceGenerators\RoslynTestUtils.cs"
+             Link="SourceGenerators\RoslynTestUtils.cs" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Logging\src\Microsoft.Extensions.Logging.csproj" />
+    <PackageReference Include="Microsoft.CodeAnalysis" Version="$(MicrosoftCodeAnalysisVersion)" />
+    <PackageReference Include="SQLitePCLRaw.bundle_green" Version="$(SQLitePCLRawbundle_greenVersion)" />
+    <ProjectReference Include="..\..\gen\Microsoft.Extensions.Logging.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="TestClasses\*.cs">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Baselines\*.generated.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs
new file mode 100644 (file)
index 0000000..3fa8899
--- /dev/null
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Microsoft.Extensions.Logging;
+
+namespace Microsoft.Extensions.Logging.Generators.Tests
+{
+    /// <summary>
+    /// A logger which captures the last log state logged to it.
+    /// </summary>
+    internal class MockLogger : ILogger
+    {
+        public LogLevel LastLogLevel { get; private set; }
+        public EventId LastEventId { get; private set; }
+        public object? LastState { get; private set; }
+        public Exception? LastException { get; private set; }
+        public string? LastFormattedString { get; private set; }
+        public bool Enabled { get; set; }
+        public int CallCount { get; private set; }
+
+        /// <summary>
+        /// Dummy disposable type, for use with BeginScope.
+        /// </summary>
+        private class Disposable : IDisposable
+        {
+            public void Dispose()
+            {
+                // nothing
+            }
+        }
+
+        public MockLogger()
+        {
+            Reset();
+        }
+
+        public IDisposable BeginScope<TState>(TState state)
+        {
+            return new Disposable();
+        }
+
+        public bool IsEnabled(LogLevel logLevel)
+        {
+            return Enabled;
+        }
+
+        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
+        {
+            LastLogLevel = logLevel;
+            LastEventId = eventId;
+            LastState = state;
+            LastException = exception;
+            LastFormattedString = formatter(state, exception);
+            CallCount++;
+        }
+
+        public void Reset()
+        {
+            LastLogLevel = (LogLevel)(-1);
+            LastEventId = default;
+            LastState = null;
+            LastException = null;
+            LastFormattedString = null;
+            CallCount = 0;
+            Enabled = true;
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs
new file mode 100644 (file)
index 0000000..b0f1cb3
--- /dev/null
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class ArgTestExtensions
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M1")]
+        public static partial void Method1(ILogger logger);
+
+        [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = "M2 {p1}")]
+        public static partial void Method2(ILogger logger, string p1);
+
+        [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "M3 {p1} {p2}")]
+        public static partial void Method3(ILogger logger, string p1, int p2);
+
+        [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "M4")]
+        public static partial void Method4(ILogger logger, InvalidOperationException p1);
+
+        [LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "M5 {p2}")]
+        public static partial void Method5(ILogger logger, System.InvalidOperationException p1, System.InvalidOperationException p2);
+
+        [LoggerMessage(EventId = 5, Level = LogLevel.Error, Message = "M6 {p2}")]
+        public static partial void Method6(ILogger logger, System.InvalidOperationException p1, int p2);
+
+        [LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "M7 {p1}")]
+        public static partial void Method7(ILogger logger, int p1, System.InvalidOperationException p2);
+
+        [LoggerMessage(EventId = 7, Level = LogLevel.Error, Message = "M8{p1}{p2}{p3}{p4}{p5}{p6}{p7}")]
+        public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7);
+
+        [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);
+
+        [LoggerMessage(EventId = 9, Level = LogLevel.Error, Message = "M10{p1}")]
+        public static partial void Method10(ILogger logger, int p1);
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs
new file mode 100644 (file)
index 0000000..74d4b99
--- /dev/null
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class CollectionTestExtensions
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M0")]
+        public static partial void M0(ILogger logger);
+
+        [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = "M1{p0}")]
+        public static partial void M1(ILogger logger, int p0);
+
+        [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "M2{p0}{p1}")]
+        public static partial void M2(ILogger logger, int p0, int p1);
+
+        [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "M3{p0}{p1}{p2}")]
+        public static partial void M3(ILogger logger, int p0, int p1, int p2);
+
+        [LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "M4{p0}{p1}{p2}{p3}")]
+        public static partial void M4(ILogger logger, int p0, int p1, int p2, int p3);
+
+        [LoggerMessage(EventId = 5, Level = LogLevel.Error, Message = "M5{p0}{p1}{p2}{p3}{p4}")]
+        public static partial void M5(ILogger logger, int p0, int p1, int p2, int p3, int p4);
+
+        [LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "M6{p0}{p1}{p2}{p3}{p4}{p5}")]
+        public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5);
+
+        [LoggerMessage(EventId = 7, Level = LogLevel.Error, Message = "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")]
+        public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6);
+
+        [LoggerMessage(EventId = 8, Level = LogLevel.Error, Message = "M8{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}")]
+        public static partial void M8(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7);
+
+        [LoggerMessage(EventId = 9, Message = "M8{p0}{p1}")]
+        public static partial void M9(ILogger logger, LogLevel level, int p0, System.Exception ex, int p1);
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs
new file mode 100644 (file)
index 0000000..65cfd41
--- /dev/null
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class EnumerableTestExtensions
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M0")]
+        public static partial void M0(ILogger logger);
+
+        [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = "M1{p0}")]
+        public static partial void M1(ILogger logger, IEnumerable<int> p0);
+
+        [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "M2{p0}{p1}")]
+        public static partial void M2(ILogger logger, int p0, IEnumerable<int> p1);
+
+        [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "M3{p0}{p1}{p2}")]
+        public static partial void M3(ILogger logger, int p0, IEnumerable<int> p1, int p2);
+
+        [LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "M4{p0}{p1}{p2}{p3}")]
+        public static partial void M4(ILogger logger, int p0, IEnumerable<int> p1, int p2, int p3);
+
+        [LoggerMessage(EventId = 5, Level = LogLevel.Error, Message = "M5{p0}{p1}{p2}{p3}{p4}")]
+        public static partial void M5(ILogger logger, int p0, IEnumerable<int> p1, int p2, int p3, int p4);
+
+        [LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "M6{p0}{p1}{p2}{p3}{p4}{p5}")]
+        public static partial void M6(ILogger logger, int p0, IEnumerable<int> p1, int p2, int p3, int p4, int p5);
+
+        [LoggerMessage(EventId = 7, Level = LogLevel.Error, Message = "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")]
+        public static partial void M7(ILogger logger, int p0, IEnumerable<int> p1, int p2, int p3, int p4, int p5, int p6);
+
+        [LoggerMessage(EventId = 8, Level = LogLevel.Error, Message = "M8{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}")]
+        public static partial void M8(ILogger logger, int p0, IEnumerable<int> p1, int p2, int p3, int p4, int p5, int p6, int p7);
+
+        [LoggerMessage(EventId = 9, Level = LogLevel.Error, Message = "M9{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}{p8}")]
+        public static partial void M9(ILogger logger, int p0, IEnumerable<int> p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8);
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs
new file mode 100644 (file)
index 0000000..4c0ddf3
--- /dev/null
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class EventNameTestExtensions
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Trace, Message = "M0", EventName = "CustomEventName")]
+        public static partial void M0(ILogger logger);
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs
new file mode 100644 (file)
index 0000000..45b01e5
--- /dev/null
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class ExceptionTestExtensions
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Trace, Message = "M0 {ex2}")]
+        public static partial void M0(ILogger logger, Exception ex1, Exception ex2);
+
+        [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = "M1 {ex2}")]
+        public static partial void M1(Exception ex1, ILogger logger, Exception ex2);
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs
new file mode 100644 (file)
index 0000000..5726aa0
--- /dev/null
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class LevelTestExtensions
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Trace, Message = "M0")]
+        public static partial void M0(ILogger logger);
+
+        [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = "M1")]
+        public static partial void M1(ILogger logger);
+
+        [LoggerMessage(EventId = 2, Level = LogLevel.Information, Message = "M2")]
+        public static partial void M2(ILogger logger);
+
+        [LoggerMessage(EventId = 3, Level = LogLevel.Warning, Message = "M3")]
+        public static partial void M3(ILogger logger);
+
+        [LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "M4")]
+        public static partial void M4(ILogger logger);
+
+        [LoggerMessage(EventId = 5, Level = LogLevel.Critical, Message = "M5")]
+        public static partial void M5(ILogger logger);
+
+        [LoggerMessage(EventId = 6, Level = LogLevel.None, Message = "M6")]
+        public static partial void M6(ILogger logger);
+
+        [LoggerMessage(EventId = 7, Level = (LogLevel)42, Message = "M7")]
+        public static partial void M7(ILogger logger);
+
+        [LoggerMessage(EventId = 8, Message = "M8")]
+        public static partial void M8(ILogger logger, LogLevel level);
+
+        [LoggerMessage(EventId = 9, Message = "M9")]
+        public static partial void M9(LogLevel level, ILogger logger);
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs
new file mode 100644 (file)
index 0000000..a308492
--- /dev/null
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma warning disable SYSLIB0027
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class MessageTestExtensions
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Trace)]
+        public static partial void M0(ILogger logger);
+
+        [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = "")]
+        public static partial void M1(ILogger logger);
+
+#if false
+        // Diagnostics produced by source generators do not respect the /warnAsError or /noWarn compiler flags.
+        // Disabled due to https://github.com/dotnet/roslyn/issues/52527
+        //
+        // These are handled fine by the logger generator and generate warnings. Unfortunately, the above warning suppression is
+        // not being observed by the C# compiler at the moment, so having these here causes build warnings.
+
+        [LoggerMessage(EventId = 2, Level = LogLevel.Trace)]
+        public static partial void M2(ILogger logger, string p1, string p2);
+
+        [LoggerMessage(EventId = 3, Level = LogLevel.Debug, Message = "")]
+        public static partial void M3(ILogger logger, string p1, int p2);
+
+        [LoggerMessage(EventId = 4, Level = LogLevel.Debug, Message = "{p1}")]
+        public static partial void M4(ILogger logger, string p1, int p2, int p3);
+#endif
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs
new file mode 100644 (file)
index 0000000..5abecef
--- /dev/null
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Logging;
+
+// Used to test use outside of a namespace
+internal static partial class NoNamespace
+{
+    [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")]
+    public static partial void CouldNotOpenSocket(ILogger logger, string hostName);
+}
+
+namespace Level1
+{
+    // used to test use inside a one-level namespace
+    internal static partial class OneLevelNamespace
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")]
+        public static partial void CouldNotOpenSocket(ILogger logger, string hostName);
+    }
+}
+
+namespace Level1
+{
+    namespace Level2
+    {
+        // used to test use inside a two-level namespace
+        internal static partial class TwoLevelNamespace
+        {
+            [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")]
+            public static partial void CouldNotOpenSocket(ILogger logger, string hostName);
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/README.md b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/README.md
new file mode 100644 (file)
index 0000000..f6cf4fd
--- /dev/null
@@ -0,0 +1,7 @@
+The source files in this directory serve two purposes:
+
+1. They are used to trigger the source generator during compilation of the test suite itself. The resulting generated code
+is then tested by LoggerMessageGeneratedCodeTests.cs. This ensures the generated code works reliably.
+
+2.They are loaded as a file from `LoggerMessageGeneratorEmitterTests.cs`, and then fed manually to the parser and then the generator
+This is used strictly to calculate code coverage attained by the first case above.
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs
new file mode 100644 (file)
index 0000000..044aa14
--- /dev/null
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    // test particular method signature variations are generated correctly
+    internal static partial class SignatureTestExtensions
+    {
+        // extension method
+        [LoggerMessage(EventId = 10, Level = LogLevel.Critical, Message = "Message11")]
+        internal static partial void M11(this ILogger logger);
+
+        public static void Combo(ILogger logger)
+        {
+            logger.M11();
+        }
+    }
+
+    // test particular method signature variations are generated correctly
+    internal partial class SignatureTestExtensions<T>
+        where T : class
+    {
+        public static void Combo(ILogger logger, ILogger<int> logger2)
+        {
+            M1(logger);
+            M2(logger);
+            M3(logger);
+            M4(logger2);
+            M5(logger, new[] { "A" });
+            M6(logger);
+            M8(logger);
+            M9(logger);
+            M10(logger, null);
+            M11(logger, "A", LogLevel.Debug, "B");
+        }
+
+        // normal public method
+        [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Message1")]
+        public static partial void M1(ILogger logger);
+
+        // internal method
+        [LoggerMessage(EventId = 1, Level = LogLevel.Critical, Message = "Message2")]
+        internal static partial void M2(ILogger logger);
+
+        // private method
+        [LoggerMessage(EventId = 2, Level = LogLevel.Critical, Message = "Message3")]
+        private static partial void M3(ILogger logger);
+
+        // generic ILogger
+        [LoggerMessage(EventId = 3, Level = LogLevel.Critical, Message = "Message4")]
+        private static partial void M4(ILogger<int> logger);
+
+        // random type method parameter
+        [LoggerMessage(EventId = 4, Level = LogLevel.Critical, Message = "Message5 {items}")]
+        private static partial void M5(ILogger logger, System.Collections.IEnumerable items);
+
+        // line feeds and quotes in the message string
+        [LoggerMessage(EventId = 5, Level = LogLevel.Critical, Message = "Message6\n\"\r")]
+        private static partial void M6(ILogger logger);
+
+        // generic parameter
+        [LoggerMessage(EventId = 6, Level = LogLevel.Critical, Message = "Message7 {p1}\n\"\r")]
+        private static partial void M7(ILogger logger, T p1);
+
+        // normal public method
+        [LoggerMessage(EventId = 7, Level = LogLevel.Critical, Message = "Message8")]
+        private protected static partial void M8(ILogger logger);
+
+        // internal method
+        [LoggerMessage(EventId = 8, Level = LogLevel.Critical, Message = "Message9")]
+        protected internal static partial void M9(ILogger logger);
+
+        // nullable parameter
+        [LoggerMessage(EventId = 9, Level = LogLevel.Critical, Message = "Message10 {optional}")]
+        internal static partial void M10(ILogger logger, string? optional);
+
+        // dynamic log level
+        [LoggerMessage(EventId = 10, Message = "Message11 {p1} {p2}")]
+        internal static partial void M11(ILogger logger, string p1, LogLevel level, string p2);
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs
new file mode 100644 (file)
index 0000000..840e5bc
--- /dev/null
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    internal static partial class TemplateTestExtensions
+    {
+        [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M0 {A1}")]
+        public static partial void M0(ILogger logger, int a1);
+
+        [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = "M1 {A1} {A1}")]
+        public static partial void M1(ILogger logger, int a1);
+
+        [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "M2 {A1} {a2} {A3} {a4} {A5} {a6} {A7}")]
+        public static partial void M2(ILogger logger, int a1, int a2, int a3, int a4, int a5, int a6, int a7);
+
+        [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "M3 {a2} {A1}")]
+        public static partial void M3(ILogger logger, int a1, int a2);
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs
new file mode 100644 (file)
index 0000000..13ed72b
--- /dev/null
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+    public partial class TestInstances
+    {
+        private readonly ILogger _myLogger;
+
+        public TestInstances(ILogger logger)
+        {
+            _myLogger = logger;
+        }
+
+        [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M0")]
+        public partial void M0();
+
+        [LoggerMessage(EventId = 1, Level = LogLevel.Trace, Message = "M1 {p1}")]
+        public partial void M1(string p1);
+    }
+}
index 2a2f28c..c0738c4 100644 (file)
@@ -9,6 +9,8 @@
              Exclude="@(ProjectExclusions)" />
     <NonNetCoreAppProject Include="@(_allSrc)"
                           Exclude="@(NetCoreAppLibrary->'%(Identity)\src\%(Identity).csproj')" />
+    <NonNetCoreAppProject Include="$(MSBuildThisFileDirectory)*\gen\*.csproj"
+                          Exclude="@(ProjectExclusions)" />
     <NetCoreAppProject Include="$(CoreLibProject);
                                 @(_allSrc);
                                 $(MSBuildThisFileDirectory)Microsoft.VisualBasic.Core\src\Microsoft.VisualBasic.Core.vbproj;
index e894ba1..dd96186 100644 (file)
     <TestTrimming Condition="'$(TestTrimming)' == ''">false</TestTrimming>
   </PropertyGroup>
 
+  <ItemGroup Condition="'$(TargetsMobile)' == 'true'">
+    <!-- Microsoft.CodeAnalysis.* assemblies missing in the virtual file system for Browser and the bundle for the mobile platforms -->
+    <ProjectExclusions Include="$(MSBuildThisFileDirectory)Microsoft.Extensions.Logging\tests\Microsoft.Extensions.Logging.Generators.Tests\Microsoft.Extensions.Logging.Generators.Tests.csproj" />
+  </ItemGroup>
+
   <!-- Projects that don't support code coverage measurement. -->
   <ItemGroup Condition="'$(Coverage)' == 'true'">
     <ProjectExclusions Include="$(CommonTestPath)Common.Tests.csproj" />