diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index d661f7604c5..917ae4b448e 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -227,13 +227,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Pr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "correlation", "docs\logs\correlation\correlation.csproj", "{9A07D215-90AC-4BAF-BCDB-73D74FD3A5C5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Tests.Stress.Logs", "test\OpenTelemetry.Tests.Stress.Logs\OpenTelemetry.Tests.Stress.Logs.csproj", "{4298057B-24E0-47B3-BB76-C17E81AF6B39}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Tests.Stress.Logs", "test\OpenTelemetry.Tests.Stress.Logs\OpenTelemetry.Tests.Stress.Logs.csproj", "{5FC0660F-3757-4594-806B-4375E06177A3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.LoggingExtensions", "examples\LoggingExtensions\Examples.LoggingExtensions.csproj", "{F5EFF065-7AF5-4D7D-8038-CC419ABD8777}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Serilog", "src\OpenTelemetry.Extensions.Serilog\OpenTelemetry.Extensions.Serilog.csproj", "{F5062ED1-6B59-45FC-8E08-2F5D41A19864}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Serilog", "src\OpenTelemetry.Extensions.Serilog\OpenTelemetry.Extensions.Serilog.csproj", "{0D85558E-15B9-4251-BDBD-9CB7933B57E2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Tracing", "src\OpenTelemetry.Extensions.Tracing\OpenTelemetry.Extensions.Tracing.csproj", "{F1C65913-81EC-4297-A666-A66280E3E1B6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Serilog.Tests", "test\OpenTelemetry.Extensions.Serilog.Tests\OpenTelemetry.Extensions.Serilog.Tests.csproj", "{6A2C122A-C1CD-4B6B-AE09-2ABB7D3C50CE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.LoggingExtensions", "examples\LoggingExtensions\Examples.LoggingExtensions.csproj", "{CCBBAD69-4A7D-41F8-8697-6E3F043F7BD5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Serilog.Tests", "test\OpenTelemetry.Extensions.Serilog.Tests\OpenTelemetry.Extensions.Serilog.Tests.csproj", "{C470DC46-8DE6-4668-BD25-ADF851F6E1E2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -465,22 +467,26 @@ Global {9A07D215-90AC-4BAF-BCDB-73D74FD3A5C5}.Debug|Any CPU.Build.0 = Debug|Any CPU {9A07D215-90AC-4BAF-BCDB-73D74FD3A5C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {9A07D215-90AC-4BAF-BCDB-73D74FD3A5C5}.Release|Any CPU.Build.0 = Release|Any CPU - {4298057B-24E0-47B3-BB76-C17E81AF6B39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4298057B-24E0-47B3-BB76-C17E81AF6B39}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4298057B-24E0-47B3-BB76-C17E81AF6B39}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4298057B-24E0-47B3-BB76-C17E81AF6B39}.Release|Any CPU.Build.0 = Release|Any CPU - {F5EFF065-7AF5-4D7D-8038-CC419ABD8777}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5EFF065-7AF5-4D7D-8038-CC419ABD8777}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5EFF065-7AF5-4D7D-8038-CC419ABD8777}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5EFF065-7AF5-4D7D-8038-CC419ABD8777}.Release|Any CPU.Build.0 = Release|Any CPU - {0D85558E-15B9-4251-BDBD-9CB7933B57E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0D85558E-15B9-4251-BDBD-9CB7933B57E2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0D85558E-15B9-4251-BDBD-9CB7933B57E2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0D85558E-15B9-4251-BDBD-9CB7933B57E2}.Release|Any CPU.Build.0 = Release|Any CPU - {6A2C122A-C1CD-4B6B-AE09-2ABB7D3C50CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6A2C122A-C1CD-4B6B-AE09-2ABB7D3C50CE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6A2C122A-C1CD-4B6B-AE09-2ABB7D3C50CE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6A2C122A-C1CD-4B6B-AE09-2ABB7D3C50CE}.Release|Any CPU.Build.0 = Release|Any CPU + {5FC0660F-3757-4594-806B-4375E06177A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FC0660F-3757-4594-806B-4375E06177A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FC0660F-3757-4594-806B-4375E06177A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FC0660F-3757-4594-806B-4375E06177A3}.Release|Any CPU.Build.0 = Release|Any CPU + {F5062ED1-6B59-45FC-8E08-2F5D41A19864}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5062ED1-6B59-45FC-8E08-2F5D41A19864}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5062ED1-6B59-45FC-8E08-2F5D41A19864}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5062ED1-6B59-45FC-8E08-2F5D41A19864}.Release|Any CPU.Build.0 = Release|Any CPU + {F1C65913-81EC-4297-A666-A66280E3E1B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1C65913-81EC-4297-A666-A66280E3E1B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1C65913-81EC-4297-A666-A66280E3E1B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1C65913-81EC-4297-A666-A66280E3E1B6}.Release|Any CPU.Build.0 = Release|Any CPU + {CCBBAD69-4A7D-41F8-8697-6E3F043F7BD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCBBAD69-4A7D-41F8-8697-6E3F043F7BD5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCBBAD69-4A7D-41F8-8697-6E3F043F7BD5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCBBAD69-4A7D-41F8-8697-6E3F043F7BD5}.Release|Any CPU.Build.0 = Release|Any CPU + {C470DC46-8DE6-4668-BD25-ADF851F6E1E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C470DC46-8DE6-4668-BD25-ADF851F6E1E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C470DC46-8DE6-4668-BD25-ADF851F6E1E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C470DC46-8DE6-4668-BD25-ADF851F6E1E2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -518,7 +524,7 @@ Global {41B784AA-3301-4126-AF9F-1D59BD04B0BF} = {3277B1C0-BDFE-4460-9B0D-D9A661FB48DB} {6C7A1595-36D6-4229-BBB5-5A6B5791791D} = {3862190B-E2C5-418E-AFDC-DB281FB5C705} {9A07D215-90AC-4BAF-BCDB-73D74FD3A5C5} = {3862190B-E2C5-418E-AFDC-DB281FB5C705} - {F5EFF065-7AF5-4D7D-8038-CC419ABD8777} = {E359BB2B-9AEC-497D-B321-7DF2450C3B8E} + {CCBBAD69-4A7D-41F8-8697-6E3F043F7BD5} = {E359BB2B-9AEC-497D-B321-7DF2450C3B8E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521} diff --git a/examples/AspNetCore/Controllers/WeatherForecastController.cs b/examples/AspNetCore/Controllers/WeatherForecastController.cs index 54f19e63b4d..5f724a36412 100644 --- a/examples/AspNetCore/Controllers/WeatherForecastController.cs +++ b/examples/AspNetCore/Controllers/WeatherForecastController.cs @@ -24,7 +24,7 @@ public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching", + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching", }; private static readonly HttpClient HttpClient = new(); diff --git a/examples/LoggingExtensions/Examples.LoggingExtensions.csproj b/examples/LoggingExtensions/Examples.LoggingExtensions.csproj index 8e502ea56d8..c365a45c62c 100644 --- a/examples/LoggingExtensions/Examples.LoggingExtensions.csproj +++ b/examples/LoggingExtensions/Examples.LoggingExtensions.csproj @@ -9,6 +9,7 @@ + diff --git a/examples/LoggingExtensions/Program.cs b/examples/LoggingExtensions/Program.cs index 310e225337f..22b89381304 100644 --- a/examples/LoggingExtensions/Program.cs +++ b/examples/LoggingExtensions/Program.cs @@ -14,11 +14,12 @@ // limitations under the License. // +using System.Diagnostics.Tracing; using OpenTelemetry.Logs; using OpenTelemetry.Resources; using Serilog; -var resourceBuilder = ResourceBuilder.CreateDefault().AddService("Examples.LogEmitter"); +var resourceBuilder = ResourceBuilder.CreateDefault().AddService("Examples.LoggingExtensions"); // Note: It is important that OpenTelemetryLoggerProvider is disposed when the // app is shutdown. In this example we allow Serilog to do that by calling CloseAndFlush. @@ -30,6 +31,12 @@ .AddConsoleExporter(); }); +// Creates an OpenTelemetryEventSourceLogEmitter for routing EventSources with +// names matching OpenTelemetry* into logs +using var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter( + openTelemetryLoggerProvider, + (name) => name.StartsWith("OpenTelemetry") ? EventLevel.LogAlways : null); + // Configure Serilog global logger Log.Logger = new LoggerConfiguration() .WriteTo.OpenTelemetry(openTelemetryLoggerProvider, disposeProvider: true) // <- Register OpenTelemetry Serilog sink diff --git a/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.0/PublicAPI.Shipped.txt b/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.0/PublicAPI.Shipped.txt new file mode 100644 index 00000000000..7dc5c58110b --- /dev/null +++ b/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.0/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt new file mode 100644 index 00000000000..16452ff842c --- /dev/null +++ b/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -0,0 +1,5 @@ +#nullable enable +OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter +OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, string! name, System.Diagnostics.Tracing.EventLevel logLevel = System.Diagnostics.Tracing.EventLevel.LogAlways) -> void +OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, System.Func! shouldListenToFunc) -> void +override OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.Dispose() -> void diff --git a/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.1/PublicAPI.Shipped.txt b/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.1/PublicAPI.Shipped.txt new file mode 100644 index 00000000000..7dc5c58110b --- /dev/null +++ b/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.1/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt new file mode 100644 index 00000000000..16452ff842c --- /dev/null +++ b/src/OpenTelemetry.Extensions.Tracing/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt @@ -0,0 +1,5 @@ +#nullable enable +OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter +OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, string! name, System.Diagnostics.Tracing.EventLevel logLevel = System.Diagnostics.Tracing.EventLevel.LogAlways) -> void +OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, System.Func! shouldListenToFunc) -> void +override OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.Dispose() -> void diff --git a/src/OpenTelemetry.Extensions.Tracing/AssemblyInfo.cs b/src/OpenTelemetry.Extensions.Tracing/AssemblyInfo.cs new file mode 100644 index 00000000000..a51a83d9d1d --- /dev/null +++ b/src/OpenTelemetry.Extensions.Tracing/AssemblyInfo.cs @@ -0,0 +1,35 @@ +// +// 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 System; +using System.Runtime.CompilerServices; + +[assembly: CLSCompliant(false)] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)] + +#if SIGNED +internal static class AssemblyInfo +{ + public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898"; + public const string MoqPublicKey = ", PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7"; +} +#else +internal static class AssemblyInfo +{ + public const string PublicKey = ""; + public const string MoqPublicKey = ""; +} +#endif diff --git a/src/OpenTelemetry.Extensions.Tracing/CHANGELOG.md b/src/OpenTelemetry.Extensions.Tracing/CHANGELOG.md new file mode 100644 index 00000000000..63bfc986bdc --- /dev/null +++ b/src/OpenTelemetry.Extensions.Tracing/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## Unreleased + +Initial release. diff --git a/src/OpenTelemetry.Extensions.Tracing/OpenTelemetry.Extensions.Tracing.csproj b/src/OpenTelemetry.Extensions.Tracing/OpenTelemetry.Extensions.Tracing.csproj new file mode 100644 index 00000000000..eaa13a04b69 --- /dev/null +++ b/src/OpenTelemetry.Extensions.Tracing/OpenTelemetry.Extensions.Tracing.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.1;netstandard2.0 + Extensions for using OpenTelemetry with System.Diagnostics.Tracing + enable + AllEnabledByDefault + latest + + + + + + + + + + + diff --git a/src/OpenTelemetry.Extensions.Tracing/OpenTelemetryEventSourceLogEmitter.cs b/src/OpenTelemetry.Extensions.Tracing/OpenTelemetryEventSourceLogEmitter.cs new file mode 100644 index 00000000000..da1541b2709 --- /dev/null +++ b/src/OpenTelemetry.Extensions.Tracing/OpenTelemetryEventSourceLogEmitter.cs @@ -0,0 +1,218 @@ +// +// 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 System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Tracing; +using System.Globalization; +using System.Linq; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Logs +{ + /// + /// Implements an which will convert events into OpenTelemetry logs. + /// + public sealed class OpenTelemetryEventSourceLogEmitter : EventListener + { + private readonly bool includeFormattedMessage; + private readonly LogEmitter logEmitter; + private readonly object lockObj = new(); + private readonly Func shouldListenToFunc; + private readonly List eventSources = new(); + private readonly List? eventSourcesBeforeConstructor = new(); + + /// + /// Initializes a new instance of the class. + /// + /// . + /// Name of the to listen to. + /// Event level to capture. + public OpenTelemetryEventSourceLogEmitter( + OpenTelemetryLoggerProvider openTelemetryLoggerProvider, + string name, + EventLevel logLevel = EventLevel.LogAlways) + : this(openTelemetryLoggerProvider, (n) => n == name ? logLevel : null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// . + /// Callback function used to decide if + /// events should be captured for a given . + /// Return if no events should be + /// captured. + public OpenTelemetryEventSourceLogEmitter( + OpenTelemetryLoggerProvider openTelemetryLoggerProvider, + Func shouldListenToFunc) + { + Guard.ThrowIfNull(openTelemetryLoggerProvider); + Guard.ThrowIfNull(shouldListenToFunc); + + this.includeFormattedMessage = openTelemetryLoggerProvider.IncludeFormattedMessage; + this.logEmitter = openTelemetryLoggerProvider.CreateEmitter(); + this.shouldListenToFunc = shouldListenToFunc; + + lock (this.lockObj) + { + foreach (EventSource eventSource in this.eventSourcesBeforeConstructor) + { + this.ProcessSource(eventSource); + } + + this.eventSourcesBeforeConstructor = null; + } + } + + /// + public override void Dispose() + { + foreach (EventSource eventSource in this.eventSources) + { + this.DisableEvents(eventSource); + } + + this.eventSources.Clear(); + + base.Dispose(); + } + +#pragma warning disable CA1062 // Validate arguments of public methods + /// + protected override void OnEventSourceCreated(EventSource eventSource) + { + Debug.Assert(eventSource != null, "EventSource was null."); + + try + { + if (this.eventSourcesBeforeConstructor != null) + { + lock (this.lockObj) + { + if (this.eventSourcesBeforeConstructor != null) + { + this.eventSourcesBeforeConstructor.Add(eventSource!); + return; + } + } + } + + this.ProcessSource(eventSource!); + } + finally + { + base.OnEventSourceCreated(eventSource); + } + } +#pragma warning restore CA1062 // Validate arguments of public methods + +#pragma warning disable CA1062 // Validate arguments of public methods + /// + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + Debug.Assert(eventData != null, "EventData was null."); + + string? rawMessage = eventData!.Message; + + LogRecordData data = new(Activity.Current) + { +#if NETSTANDARD2_1_OR_GREATER + Timestamp = eventData.TimeStamp, +#endif + EventId = new EventId(eventData.EventId, eventData.EventName), + LogLevel = ConvertEventLevelToLogLevel(eventData.Level), + }; + + LogRecordAttributeList attributes = default; + + attributes.Add("event_source.name", eventData.EventSource.Name); + + if (eventData.ActivityId != Guid.Empty) + { + attributes.Add("event_source.activity_id", eventData.ActivityId); + } + + if (eventData.RelatedActivityId != Guid.Empty) + { + attributes.Add("event_source.related_activity_id", eventData.RelatedActivityId); + } + + int payloadCount = eventData.Payload?.Count ?? 0; + + if (payloadCount > 0 && payloadCount == eventData.PayloadNames?.Count) + { + for (int i = 0; i < payloadCount; i++) + { + string name = eventData.PayloadNames[i]; + + if (!string.IsNullOrEmpty(rawMessage) && !this.includeFormattedMessage) + { + // TODO: This code converts the event message from + // string.Format syntax (eg: "Some message {0} {1}") + // into structured log format (eg: "Some message + // {propertyName1} {propertyName2}") but it is + // expensive. Probably needs a cache. +#if NETSTANDARD2_0 + rawMessage = rawMessage.Replace($"{{{i}}}", $"{{{name}}}"); +#else + rawMessage = rawMessage.Replace($"{{{i}}}", $"{{{name}}}", StringComparison.Ordinal); +#endif + } + + attributes.Add(name, eventData.Payload![i]); + } + } + + if (!string.IsNullOrEmpty(rawMessage) && this.includeFormattedMessage && payloadCount > 0) + { + rawMessage = string.Format(CultureInfo.InvariantCulture, rawMessage, eventData.Payload!.ToArray()); + } + + data.Message = rawMessage; + + this.logEmitter.Emit(in data, in attributes); + } +#pragma warning restore CA1062 // Validate arguments of public methods + + private static LogLevel ConvertEventLevelToLogLevel(EventLevel eventLevel) + { + return eventLevel switch + { + EventLevel.Informational => LogLevel.Information, + EventLevel.Warning => LogLevel.Warning, + EventLevel.Error => LogLevel.Error, + EventLevel.Critical => LogLevel.Critical, + _ => LogLevel.Trace, + }; + } + + private void ProcessSource(EventSource eventSource) + { + EventLevel? eventLevel = this.shouldListenToFunc(eventSource.Name); + + if (eventLevel.HasValue) + { + this.eventSources.Add(eventSource); + this.EnableEvents(eventSource, eventLevel.Value, EventKeywords.All); + } + } + } +} diff --git a/src/OpenTelemetry.Extensions.Tracing/README.md b/src/OpenTelemetry.Extensions.Tracing/README.md new file mode 100644 index 00000000000..d14f85bd468 --- /dev/null +++ b/src/OpenTelemetry.Extensions.Tracing/README.md @@ -0,0 +1,30 @@ +# OpenTelemetry.Extensions.Tracing + +This project contains an +[EventListener](https://docs.microsoft.com/dotnet/api/system.diagnostics.tracing.eventlistener) +which can be used to translate events written to an +[EventSource](https://docs.microsoft.com/dotnet/api/system.diagnostics.tracing.eventsource) +into to OpenTelemetry logs. + +## Usage Example + +```csharp +// Step 1: Configure OpenTelemetryLoggerProvider... +var resourceBuilder = ResourceBuilder.CreateDefault().AddService("MyService"); + +using var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options => +{ + options + .SetResourceBuilder(resourceBuilder) + .AddConsoleExporter(); +}); + +// Step 2: Create OpenTelemetryEventSourceLogEmitter to listen to events... +using var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter( + openTelemetryLoggerProvider, + (name) => name.StartsWith("OpenTelemetry") ? EventLevel.LogAlways : null); +``` + +## References + +* [OpenTelemetry Project](https://opentelemetry.io/) diff --git a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt index ecaf006aa47..ff683034d4a 100644 --- a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt @@ -8,6 +8,7 @@ OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void OpenTelemetry.Logs.LogRecord.TraceId.set -> void OpenTelemetry.Logs.LogRecord.TraceState.set -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.IncludeFormattedMessage.get -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! diff --git a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt index 43156ee3c3a..a316ab5d377 100644 --- a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt @@ -9,6 +9,7 @@ OpenTelemetry.Logs.LogRecord.TraceId.set -> void OpenTelemetry.Logs.LogRecord.TraceState.set -> void OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.IncludeFormattedMessage.get -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void ~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index ecaf006aa47..ff683034d4a 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -8,6 +8,7 @@ OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void OpenTelemetry.Logs.LogRecord.TraceId.set -> void OpenTelemetry.Logs.LogRecord.TraceState.set -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.IncludeFormattedMessage.get -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! diff --git a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt index 43156ee3c3a..a316ab5d377 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt @@ -9,6 +9,7 @@ OpenTelemetry.Logs.LogRecord.TraceId.set -> void OpenTelemetry.Logs.LogRecord.TraceState.set -> void OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.IncludeFormattedMessage.get -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void ~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry/AssemblyInfo.cs b/src/OpenTelemetry/AssemblyInfo.cs index 0a79a36dfe5..99d1bfa8ad4 100644 --- a/src/OpenTelemetry/AssemblyInfo.cs +++ b/src/OpenTelemetry/AssemblyInfo.cs @@ -21,6 +21,7 @@ [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Tests" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting.Tests" + AssemblyInfo.PublicKey)] +[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Tracing" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Serilog" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)] [assembly: InternalsVisibleTo("Benchmarks" + AssemblyInfo.PublicKey)] diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs index d94769b7f59..672d4ef8250 100644 --- a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs @@ -33,7 +33,6 @@ namespace OpenTelemetry.Logs public class OpenTelemetryLoggerProvider : BaseProvider, ILoggerProvider, ISupportExternalScope { internal readonly bool IncludeScopes; - internal readonly bool IncludeFormattedMessage; internal readonly bool ParseStateValues; internal BaseProcessor? Processor; internal Resource Resource; @@ -90,6 +89,11 @@ internal OpenTelemetryLoggerProvider(OpenTelemetryLoggerOptions options) } } + /// + /// Gets a value indicating whether or not formatted messages should be included on log messages. + /// + public bool IncludeFormattedMessage { get; } + internal IExternalScopeProvider? ScopeProvider { get; private set; } internal ILogRecordPool LogRecordPool => this.threadStaticPool ?? LogRecordSharedPool.Current;