From 1eec84a0b87303fbc0e21da7997926123d4b7b62 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 25 Sep 2020 11:19:58 -0700 Subject: [PATCH 1/4] ilogger integration --- OpenTelemetry.sln | 7 ++ build/Common.props | 2 + docs/Directory.Build.props | 1 + docs/logs/getting-started/Program.cs | 32 +++++++ docs/logs/getting-started/README.md | 3 + .../getting-started/getting-started.csproj | 6 ++ src/OpenTelemetry/Logs/OpenTelemetryLogger.cs | 93 +++++++++++++++++++ .../Logs/OpenTelemetryLoggerOptions.cs | 26 ++++++ .../Logs/OpenTelemetryLoggerProvider.cs | 65 +++++++++++++ .../Logs/OpenTelemetryLoggingExtensions.cs | 48 ++++++++++ src/OpenTelemetry/OpenTelemetry.csproj | 5 + 11 files changed, 288 insertions(+) create mode 100644 docs/logs/getting-started/Program.cs create mode 100644 docs/logs/getting-started/README.md create mode 100644 docs/logs/getting-started/getting-started.csproj create mode 100644 src/OpenTelemetry/Logs/OpenTelemetryLogger.cs create mode 100644 src/OpenTelemetry/Logs/OpenTelemetryLoggerOptions.cs create mode 100644 src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs create mode 100644 src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 1e214bee3cb..94fc0021654 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -196,6 +196,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentati EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.GrpcService", "examples\GrpcService\Examples.GrpcService.csproj", "{DB942F5A-D571-4DEA-B1A7-B6BE0E24E6ED}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "getting-started", "docs\logs\getting-started\getting-started.csproj", "{B3F03725-23A0-4582-9526-F6A7E38F35CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -378,6 +380,10 @@ Global {DB942F5A-D571-4DEA-B1A7-B6BE0E24E6ED}.Debug|Any CPU.Build.0 = Debug|Any CPU {DB942F5A-D571-4DEA-B1A7-B6BE0E24E6ED}.Release|Any CPU.ActiveCfg = Release|Any CPU {DB942F5A-D571-4DEA-B1A7-B6BE0E24E6ED}.Release|Any CPU.Build.0 = Release|Any CPU + {B3F03725-23A0-4582-9526-F6A7E38F35CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3F03725-23A0-4582-9526-F6A7E38F35CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3F03725-23A0-4582-9526-F6A7E38F35CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3F03725-23A0-4582-9526-F6A7E38F35CC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -406,6 +412,7 @@ Global {FCDCF532-A163-40DA-80B7-7530AA1182C4} = {5B7FB835-3FFF-4BC2-99C5-A5B5FAE3C818} {B26BE278-C9DA-4067-A0EE-6A4227B3DC87} = {3862190B-E2C5-418E-AFDC-DB281FB5C705} {DB942F5A-D571-4DEA-B1A7-B6BE0E24E6ED} = {E359BB2B-9AEC-497D-B321-7DF2450C3B8E} + {B3F03725-23A0-4582-9526-F6A7E38F35CC} = {3862190B-E2C5-418E-AFDC-DB281FB5C705} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521} diff --git a/build/Common.props b/build/Common.props index 3a72e340d98..fbea8bc8cd9 100644 --- a/build/Common.props +++ b/build/Common.props @@ -31,6 +31,8 @@ [3.3.0] [16.7.1] [2.1.0,5.0) + [2.1.0,6.0) + [2.1.0,6.0) [1.0.0,2.0) [1.0.0,2.0) [12.0.2,13.0) diff --git a/docs/Directory.Build.props b/docs/Directory.Build.props index 36cb1aa1b19..8ce176a76ef 100644 --- a/docs/Directory.Build.props +++ b/docs/Directory.Build.props @@ -11,6 +11,7 @@ Please sort alphabetically. Refer to https://docs.microsoft.com/en-us/nuget/concepts/package-versioning for semver syntax. --> + [5.0.0-rc.1.20451.14,6.0) [0.4.0-beta.2,1.0) [0.4.0-beta.2,1.0) diff --git a/docs/logs/getting-started/Program.cs b/docs/logs/getting-started/Program.cs new file mode 100644 index 00000000000..65b5142b2ff --- /dev/null +++ b/docs/logs/getting-started/Program.cs @@ -0,0 +1,32 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using Microsoft.Extensions.Logging; + +public class Program +{ + public static void Main() + { + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(); + }); + var logger = loggerFactory.CreateLogger(); + + logger.LogInformation("Hello, World!"); + logger.LogInformation("Hello from {name} {price}.", "artichoke", 3.99); + } +} diff --git a/docs/logs/getting-started/README.md b/docs/logs/getting-started/README.md new file mode 100644 index 00000000000..6941cf47115 --- /dev/null +++ b/docs/logs/getting-started/README.md @@ -0,0 +1,3 @@ +# Getting Started with OpenTelemetry .NET in 5 Minutes + +TBD diff --git a/docs/logs/getting-started/getting-started.csproj b/docs/logs/getting-started/getting-started.csproj new file mode 100644 index 00000000000..fed46f5b594 --- /dev/null +++ b/docs/logs/getting-started/getting-started.csproj @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLogger.cs b/src/OpenTelemetry/Logs/OpenTelemetryLogger.cs new file mode 100644 index 00000000000..d2a6cb6aa7b --- /dev/null +++ b/src/OpenTelemetry/Logs/OpenTelemetryLogger.cs @@ -0,0 +1,93 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETSTANDARD2_0 +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.Extensions.Logging; + +namespace OpenTelemetry.Logs +{ + internal class OpenTelemetryLogger : ILogger + { + private readonly string name; + + internal OpenTelemetryLogger(string name, OpenTelemetryLoggerOptions options) + { + this.name = name ?? throw new ArgumentNullException(nameof(name)); + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + } + + internal IExternalScopeProvider ScopeProvider { get; set; } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!this.IsEnabled(logLevel)) + { + return; + } + + var timestamp = DateTime.UtcNow; + + if (state is IReadOnlyCollection> dict) + { + var isUnstructuredLog = dict.Count == 1; + + // TODO: remove the console output after finished the plumbing work to log processors/exporters + if (isUnstructuredLog) + { + foreach (var entry in dict) + { + Console.WriteLine($"{this.name}({logLevel}, Id={eventId}): {entry.Value}"); + } + } + else + { + Console.WriteLine($"{this.name}({logLevel}, Id={eventId}):"); + foreach (var entry in dict) + { + if (string.Equals(entry.Key, "{OriginalFormat}", StringComparison.Ordinal)) + { + Console.WriteLine($" $format: {entry.Value}"); + continue; + } + + Console.WriteLine($" {entry.Key}: {entry.Value}"); + } + } + + if (exception != null) + { + Console.WriteLine($" $exception: {exception}"); + } + } + } + + public bool IsEnabled(LogLevel logLevel) + { + return logLevel != LogLevel.None; + } + + public IDisposable BeginScope(TState state) => this.ScopeProvider?.Push(state) ?? null; + } +} +#endif diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggerOptions.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggerOptions.cs new file mode 100644 index 00000000000..f44baf424f2 --- /dev/null +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggerOptions.cs @@ -0,0 +1,26 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETSTANDARD2_0 +using Microsoft.Extensions.Logging; + +namespace OpenTelemetry.Logs +{ + public class OpenTelemetryLoggerOptions + { + } +} +#endif diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs new file mode 100644 index 00000000000..c7359445cab --- /dev/null +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs @@ -0,0 +1,65 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETSTANDARD2_0 +using System.Collections.Concurrent; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace OpenTelemetry.Logs +{ + [ProviderAlias("OpenTelemetry")] + public class OpenTelemetryLoggerProvider : ILoggerProvider, ISupportExternalScope + { + private readonly IOptionsMonitor options; + private readonly ConcurrentDictionary loggers; + private IExternalScopeProvider scopeProvider; + + public OpenTelemetryLoggerProvider(IOptionsMonitor options) + { + this.options = options; + this.loggers = new ConcurrentDictionary(); + } + + internal IExternalScopeProvider ScopeProvider + { + get + { + if (this.scopeProvider == null) + { + this.scopeProvider = new LoggerExternalScopeProvider(); + } + + return this.scopeProvider; + } + } + + void ISupportExternalScope.SetScopeProvider(IExternalScopeProvider scopeProvider) + { + this.scopeProvider = scopeProvider; + } + + public ILogger CreateLogger(string name) + { + return this.loggers.GetOrAdd(name, name => new OpenTelemetryLogger(name, this.options.CurrentValue)); + } + + public void Dispose() + { + } + } +} +#endif diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs new file mode 100644 index 00000000000..bc44746b855 --- /dev/null +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs @@ -0,0 +1,48 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETSTANDARD2_0 +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Configuration; +using OpenTelemetry.Logs; + +namespace Microsoft.Extensions.Logging +{ + public static class OpenTelemetryLoggingExtensions + { + public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder, Action configure = null) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.AddConfiguration(); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + + if (configure != null) + { + builder.Services.Configure(configure); + } + + return builder; + } + } +} +#endif diff --git a/src/OpenTelemetry/OpenTelemetry.csproj b/src/OpenTelemetry/OpenTelemetry.csproj index 7089e30f320..05f4a7c9bb5 100644 --- a/src/OpenTelemetry/OpenTelemetry.csproj +++ b/src/OpenTelemetry/OpenTelemetry.csproj @@ -18,6 +18,11 @@ + + + + + From 44bdf06a93b8aba5b33ddcf5a9db5bcce181b437 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 25 Sep 2020 11:29:55 -0700 Subject: [PATCH 2/4] update changelog --- src/OpenTelemetry.Api/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index 0575e5078d5..823e7c21b8f 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added `ILogger`/`Microsoft.Extensions.Logging` integration + ([#1308](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1308)) + ## 0.6.0-beta.1 Released 2020-Sep-15 From f3c0e3f7734db4dd0a1decb2738b51cceb14c324 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 25 Sep 2020 12:48:03 -0700 Subject: [PATCH 3/4] s/name/categoryName/g --- src/OpenTelemetry/Logs/OpenTelemetryLogger.cs | 10 ++++---- .../Logs/OpenTelemetryLoggerProvider.cs | 23 +++++++++++++++---- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLogger.cs b/src/OpenTelemetry/Logs/OpenTelemetryLogger.cs index d2a6cb6aa7b..1f3fd82f9ea 100644 --- a/src/OpenTelemetry/Logs/OpenTelemetryLogger.cs +++ b/src/OpenTelemetry/Logs/OpenTelemetryLogger.cs @@ -25,11 +25,11 @@ namespace OpenTelemetry.Logs { internal class OpenTelemetryLogger : ILogger { - private readonly string name; + private readonly string categoryName; - internal OpenTelemetryLogger(string name, OpenTelemetryLoggerOptions options) + internal OpenTelemetryLogger(string categoryName, OpenTelemetryLoggerOptions options) { - this.name = name ?? throw new ArgumentNullException(nameof(name)); + this.categoryName = categoryName ?? throw new ArgumentNullException(nameof(categoryName)); if (options == null) { @@ -57,12 +57,12 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except { foreach (var entry in dict) { - Console.WriteLine($"{this.name}({logLevel}, Id={eventId}): {entry.Value}"); + Console.WriteLine($"{this.categoryName}({logLevel}, Id={eventId}): {entry.Value}"); } } else { - Console.WriteLine($"{this.name}({logLevel}, Id={eventId}):"); + Console.WriteLine($"{this.categoryName}({logLevel}, Id={eventId}):"); foreach (var entry in dict) { if (string.Equals(entry.Key, "{OriginalFormat}", StringComparison.Ordinal)) diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs index c7359445cab..b84ff8ab755 100644 --- a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs @@ -15,7 +15,8 @@ // #if NETSTANDARD2_0 -using System.Collections.Concurrent; +using System; +using System.Collections.Generic; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -25,13 +26,13 @@ namespace OpenTelemetry.Logs public class OpenTelemetryLoggerProvider : ILoggerProvider, ISupportExternalScope { private readonly IOptionsMonitor options; - private readonly ConcurrentDictionary loggers; + private readonly IDictionary loggers; private IExternalScopeProvider scopeProvider; public OpenTelemetryLoggerProvider(IOptionsMonitor options) { this.options = options; - this.loggers = new ConcurrentDictionary(); + this.loggers = new Dictionary(StringComparer.Ordinal); } internal IExternalScopeProvider ScopeProvider @@ -52,9 +53,21 @@ void ISupportExternalScope.SetScopeProvider(IExternalScopeProvider scopeProvider this.scopeProvider = scopeProvider; } - public ILogger CreateLogger(string name) + public ILogger CreateLogger(string categoryName) { - return this.loggers.GetOrAdd(name, name => new OpenTelemetryLogger(name, this.options.CurrentValue)); + lock (this.loggers) + { + ILogger logger; + + if (this.loggers.TryGetValue(categoryName, out logger)) + { + return logger; + } + + logger = new OpenTelemetryLogger(categoryName, this.options.CurrentValue); + this.loggers.Add(categoryName, logger); + return logger; + } } public void Dispose() From 9ab3291f82c15f005b46b55aa152d3a3a851d301 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 25 Sep 2020 12:59:41 -0700 Subject: [PATCH 4/4] TODO comment --- src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs index b84ff8ab755..66aae355095 100644 --- a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs @@ -50,6 +50,7 @@ internal IExternalScopeProvider ScopeProvider void ISupportExternalScope.SetScopeProvider(IExternalScopeProvider scopeProvider) { + // TODO: set existing loggers this.scopeProvider = scopeProvider; }