diff --git a/CHANGELOG.md b/CHANGELOG.md index af7e39cf2..709aa5b3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This beta release is built on top of [OpenTelemetry .NET](https://github.com/ope - Support configuring `OTEL_*` settings using `App.config` and `Web.config`. - Add support for Quartz traces instrumentation. - Add support for EntityFrameworkCore traces instrumentations. +- Add support for custom OpenTelemetry .NET SDK through `OTEL_DOTNET_AUTO_SETUP_SDK`. ### Changed diff --git a/Directory.Packages.props b/Directory.Packages.props index 8a673e00e..f22d87c72 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,8 @@ + + diff --git a/OpenTelemetry.AutoInstrumentation.sln b/OpenTelemetry.AutoInstrumentation.sln index f6332abb7..1fd9e3632 100644 --- a/OpenTelemetry.AutoInstrumentation.sln +++ b/OpenTelemetry.AutoInstrumentation.sln @@ -151,6 +151,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.AspNet.NetF EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.MultipleAppDomains.NetFramework", "test\test-applications\integrations\TestApplication.MultipleAppDomains.NetFramework\TestApplication.MultipleAppDomains.NetFramework.csproj", "{A0338846-2AF8-4D96-ADA7-EBD4F010D08F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.CustomSdk", "test\test-applications\integrations\TestApplication.CustomSdk\TestApplication.CustomSdk.csproj", "{7B363F47-1DB8-44CF-8388-A41F9366C9DE}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.Quartz", "test\test-applications\integrations\TestApplication.Quartz\TestApplication.Quartz.csproj", "{B48780CE-8AAB-44D3-9CD1-8491584352B5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "test\Benchmarks\Benchmarks.csproj", "{B1E71654-E784-4A39-BE45-8671E027B6D7}" @@ -713,6 +715,18 @@ Global {A0338846-2AF8-4D96-ADA7-EBD4F010D08F}.Release|x64.Build.0 = Release|x64 {A0338846-2AF8-4D96-ADA7-EBD4F010D08F}.Release|x86.ActiveCfg = Release|x86 {A0338846-2AF8-4D96-ADA7-EBD4F010D08F}.Release|x86.Build.0 = Release|x86 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Debug|Any CPU.ActiveCfg = Debug|x64 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Debug|Any CPU.Build.0 = Debug|x64 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Debug|x64.ActiveCfg = Debug|x64 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Debug|x64.Build.0 = Debug|x64 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Debug|x86.ActiveCfg = Debug|x86 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Debug|x86.Build.0 = Debug|x86 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Release|Any CPU.ActiveCfg = Release|x64 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Release|Any CPU.Build.0 = Release|x64 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Release|x64.ActiveCfg = Release|x64 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Release|x64.Build.0 = Release|x64 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Release|x86.ActiveCfg = Release|x86 + {7B363F47-1DB8-44CF-8388-A41F9366C9DE}.Release|x86.Build.0 = Release|x86 {B48780CE-8AAB-44D3-9CD1-8491584352B5}.Debug|Any CPU.ActiveCfg = Debug|x64 {B48780CE-8AAB-44D3-9CD1-8491584352B5}.Debug|Any CPU.Build.0 = Debug|x64 {B48780CE-8AAB-44D3-9CD1-8491584352B5}.Debug|x64.ActiveCfg = Debug|x64 @@ -804,6 +818,7 @@ Global {E1E55FB4-FCFF-4231-9CE7-C6671A3F5421} = {E409ADD3-9574-465C-AB09-4324D205CC7C} {5FDB5603-21D3-446A-8B50-36DACCF7C2FF} = {E409ADD3-9574-465C-AB09-4324D205CC7C} {A0338846-2AF8-4D96-ADA7-EBD4F010D08F} = {E409ADD3-9574-465C-AB09-4324D205CC7C} + {7B363F47-1DB8-44CF-8388-A41F9366C9DE} = {E409ADD3-9574-465C-AB09-4324D205CC7C} {B48780CE-8AAB-44D3-9CD1-8491584352B5} = {E409ADD3-9574-465C-AB09-4324D205CC7C} {B1E71654-E784-4A39-BE45-8671E027B6D7} = {5C915382-C886-457D-8641-9E766D8E5A17} {036A56A4-4EEF-42E3-9857-B9B00D302BF9} = {E409ADD3-9574-465C-AB09-4324D205CC7C} diff --git a/docs/config.md b/docs/config.md index fd537448a..59c2988eb 100644 --- a/docs/config.md +++ b/docs/config.md @@ -42,6 +42,7 @@ with environment variables taking precedence over `App.config` or `Web.config` f |--------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------| | `OTEL_DOTNET_AUTO_HOME` | Installation location. | | | `OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES` | Names of the executable files that the profiler cannot instrument. Supports multiple comma-separated values, for example: `ReservedProcess.exe,powershell.exe`. If unset, the profiler attaches to all processes by default. | | +| `OTEL_DOTNET_AUTO_SETUP_SDK` | Controls whether auto-instrumentation should set up OpenTelemetry .NET SDK at startup. | `true` | ## Resources diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 3360e0157..2dfc9a131 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -9,8 +9,6 @@ - - diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/ConfigurationKeys.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/ConfigurationKeys.cs index 54dfc4ad7..fb46499d6 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/ConfigurationKeys.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/ConfigurationKeys.cs @@ -50,6 +50,11 @@ internal static class ConfigurationKeys /// public const string ProviderPlugins = "OTEL_DOTNET_AUTO_PLUGINS"; + /// + /// Configuration key for setting up OpenTelemetry .NET SDK. + /// + public const string SetupSdk = "OTEL_DOTNET_AUTO_SETUP_SDK"; + /// /// Configuration keys for traces. /// diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/DelayedInitialization.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/DelayedInitialization.cs new file mode 100644 index 000000000..a429c7dcf --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/DelayedInitialization.cs @@ -0,0 +1,101 @@ +// +// 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.Runtime.CompilerServices; +using OpenTelemetry.AutoInstrumentation.Loading; +using OpenTelemetry.AutoInstrumentation.Loading.Initializers; +using OpenTelemetry.AutoInstrumentation.Plugins; + +namespace OpenTelemetry.AutoInstrumentation.Configurations; + +internal static class DelayedInitialization +{ + internal static class Traces + { + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddAspNet(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { +#if NET462 + new AspNetInitializer(lazyInstrumentationLoader, pluginManager); +#elif NET6_0_OR_GREATER + lazyInstrumentationLoader.Add(new AspNetCoreInitializer(pluginManager)); +#endif + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddHttpClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { + new HttpClientInitializer(lazyInstrumentationLoader, pluginManager); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddGrpcClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { + lazyInstrumentationLoader.Add(new GrpcClientInitializer(pluginManager)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddSqlClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { + new SqlClientInitializer(lazyInstrumentationLoader, pluginManager); + } + +#if NET6_0_OR_GREATER + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddMySqlClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { + lazyInstrumentationLoader.Add(new MySqlDataInitializer(pluginManager)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddEntityFrameworkCore(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { + lazyInstrumentationLoader.Add(new EntityFrameworkCoreInitializer(pluginManager)); + } +#endif + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddWcf(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { + lazyInstrumentationLoader.Add(new WcfInitializer(pluginManager)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddQuartz(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { + lazyInstrumentationLoader.Add(new QuartzInitializer(pluginManager)); + } + } + + internal static class Metrics + { + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddAspNet(LazyInstrumentationLoader lazyInstrumentationLoader) + { +#if NET462 + new AspNetMetricsInitializer(lazyInstrumentationLoader); +#elif NET6_0_OR_GREATER + lazyInstrumentationLoader.Add(new AspNetCoreMetricsInitializer()); +#endif + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddHttpClient(LazyInstrumentationLoader lazyInstrumentationLoader) + { + new HttpClientMetricsInitializer(lazyInstrumentationLoader); + } + } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationMetricHelper.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationMetricHelper.cs index b270a536d..ca36b9226 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationMetricHelper.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationMetricHelper.cs @@ -82,23 +82,19 @@ internal static class EnvironmentConfigurationMetricHelper [MethodImpl(MethodImplOptions.NoInlining)] public static MeterProviderBuilder AddAspNetInstrumentation(MeterProviderBuilder builder, LazyInstrumentationLoader lazyInstrumentationLoader) { + DelayedInitialization.Metrics.AddAspNet(lazyInstrumentationLoader); #if NET462 - new AspNetMetricsInitializer(lazyInstrumentationLoader); - builder.AddMeter("OpenTelemetry.Instrumentation.AspNet"); #elif NET6_0_OR_GREATER - lazyInstrumentationLoader.Add(new AspNetCoreMetricsInitializer()); - builder.AddMeter("OpenTelemetry.Instrumentation.AspNetCore"); #endif - return builder; } [MethodImpl(MethodImplOptions.NoInlining)] public static MeterProviderBuilder AddHttpClientInstrumentation(MeterProviderBuilder builder, LazyInstrumentationLoader lazyInstrumentationLoader) { - new HttpClientMetricsInitializer(lazyInstrumentationLoader); + DelayedInitialization.Metrics.AddHttpClient(lazyInstrumentationLoader); return builder.AddMeter("OpenTelemetry.Instrumentation.Http"); } diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationTracerHelper.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationTracerHelper.cs index ec7ed7a8c..611532ff9 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationTracerHelper.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationTracerHelper.cs @@ -113,7 +113,7 @@ internal static class EnvironmentConfigurationTracerHelper [MethodImpl(MethodImplOptions.NoInlining)] public static TracerProviderBuilder AddWcfInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader) { - lazyInstrumentationLoader.Add(new WcfInitializer(pluginManager)); + DelayedInitialization.Traces.AddWcf(lazyInstrumentationLoader, pluginManager); return builder.AddSource("OpenTelemetry.Instrumentation.Wcf"); } @@ -121,7 +121,7 @@ internal static class EnvironmentConfigurationTracerHelper [MethodImpl(MethodImplOptions.NoInlining)] public static TracerProviderBuilder AddHttpClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader) { - new HttpClientInitializer(lazyInstrumentationLoader, pluginManager); + DelayedInitialization.Traces.AddHttpClient(lazyInstrumentationLoader, pluginManager); #if NETFRAMEWORK builder.AddSource("OpenTelemetry.Instrumentation.Http.HttpWebRequest"); @@ -137,17 +137,13 @@ internal static class EnvironmentConfigurationTracerHelper [MethodImpl(MethodImplOptions.NoInlining)] public static TracerProviderBuilder AddAspNetInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader) { + DelayedInitialization.Traces.AddAspNet(lazyInstrumentationLoader, pluginManager); #if NET462 - new AspNetInitializer(lazyInstrumentationLoader, pluginManager); - builder.AddSource(OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.AspNetSourceName); #elif NET6_0_OR_GREATER - lazyInstrumentationLoader.Add(new AspNetCoreInitializer(pluginManager)); - builder.AddSource("OpenTelemetry.Instrumentation.AspNetCore"); builder.AddLegacySource("Microsoft.AspNetCore.Hosting.HttpRequestIn"); #endif - return builder; } @@ -155,7 +151,7 @@ internal static class EnvironmentConfigurationTracerHelper [MethodImpl(MethodImplOptions.NoInlining)] public static TracerProviderBuilder AddMySqlClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader) { - lazyInstrumentationLoader.Add(new MySqlDataInitializer(pluginManager)); + DelayedInitialization.Traces.AddMySqlClient(lazyInstrumentationLoader, pluginManager); return builder.AddSource("OpenTelemetry.Instrumentation.MySqlData"); } @@ -164,7 +160,7 @@ internal static class EnvironmentConfigurationTracerHelper [MethodImpl(MethodImplOptions.NoInlining)] public static TracerProviderBuilder AddSqlClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader) { - new SqlClientInitializer(lazyInstrumentationLoader, pluginManager); + DelayedInitialization.Traces.AddSqlClient(lazyInstrumentationLoader, pluginManager); return builder.AddSource("OpenTelemetry.Instrumentation.SqlClient"); } @@ -172,7 +168,7 @@ internal static class EnvironmentConfigurationTracerHelper [MethodImpl(MethodImplOptions.NoInlining)] public static TracerProviderBuilder AddGrpcClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader) { - lazyInstrumentationLoader.Add(new GrpcClientInitializer(pluginManager)); + DelayedInitialization.Traces.AddGrpcClient(lazyInstrumentationLoader, pluginManager); builder.AddSource("OpenTelemetry.Instrumentation.GrpcNetClient"); builder.AddLegacySource("Grpc.Net.Client.GrpcOut"); @@ -183,7 +179,7 @@ internal static class EnvironmentConfigurationTracerHelper [MethodImpl(MethodImplOptions.NoInlining)] public static TracerProviderBuilder AddQuartzInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader) { - lazyInstrumentationLoader.Add(new QuartzInitializer(pluginManager)); + DelayedInitialization.Traces.AddQuartz(lazyInstrumentationLoader, pluginManager); return builder.AddSource("OpenTelemetry.Instrumentation.Quartz") .AddLegacySource("Quartz.Job.Execute") @@ -194,7 +190,7 @@ internal static class EnvironmentConfigurationTracerHelper [MethodImpl(MethodImplOptions.NoInlining)] public static TracerProviderBuilder AddEntityFrameworkCoreInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader) { - lazyInstrumentationLoader.Add(new EntityFrameworkCoreInitializer(pluginManager)); + DelayedInitialization.Traces.AddEntityFrameworkCore(lazyInstrumentationLoader, pluginManager); return builder.AddSource("OpenTelemetry.Instrumentation.EntityFrameworkCore"); } diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/GeneralSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/GeneralSettings.cs index 9578e0c02..39d9b115f 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/GeneralSettings.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/GeneralSettings.cs @@ -30,6 +30,11 @@ internal class GeneralSettings : Settings /// public bool FlushOnUnhandledException { get; private set; } + /// + /// Gets a value indicating whether OpenTelemetry .NET SDK should be set up. + /// + public bool SetupSdk { get; private set; } + protected override void OnLoad(Configuration configuration) { var providerPlugins = configuration.GetString(ConfigurationKeys.ProviderPlugins); @@ -42,5 +47,6 @@ internal class GeneralSettings : Settings } FlushOnUnhandledException = configuration.GetBool(ConfigurationKeys.FlushOnUnhandledException) ?? false; + SetupSdk = configuration.GetBool(ConfigurationKeys.SetupSdk) ?? true; } } diff --git a/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs b/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs index fc4ae57d7..ab4e14d8c 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs @@ -131,26 +131,43 @@ internal static class Instrumentation if (TracerSettings.Value.TracesEnabled) { - var builder = Sdk - .CreateTracerProviderBuilder() - .ConfigureResource(ResourceConfigurator.Configure) - .UseEnvironmentVariables(LazyInstrumentationLoader, TracerSettings.Value, _pluginManager) - .InvokePlugins(_pluginManager); + if (GeneralSettings.Value.SetupSdk) + { + var builder = Sdk + .CreateTracerProviderBuilder() + .ConfigureResource(ResourceConfigurator.Configure) + .UseEnvironmentVariables(LazyInstrumentationLoader, TracerSettings.Value, _pluginManager) + .InvokePlugins(_pluginManager); - _tracerProvider = builder.Build(); - Logger.Information("OpenTelemetry tracer initialized."); + _tracerProvider = builder.Build(); + Logger.Information("OpenTelemetry tracer initialized."); + } + else + { + AddLazilyLoadedTraceInstrumentations(LazyInstrumentationLoader, _pluginManager, TracerSettings.Value.EnabledInstrumentations); + Logger.Information("Initialized lazily-loaded trace instrumentations without initializing sdk."); + } } if (MetricSettings.Value.MetricsEnabled) { - var builder = Sdk - .CreateMeterProviderBuilder() - .ConfigureResource(ResourceConfigurator.Configure) - .UseEnvironmentVariables(LazyInstrumentationLoader, MetricSettings.Value, _pluginManager) - .InvokePlugins(_pluginManager); + if (GeneralSettings.Value.SetupSdk) + { + var builder = Sdk + .CreateMeterProviderBuilder() + .ConfigureResource(ResourceConfigurator.Configure) + .UseEnvironmentVariables(LazyInstrumentationLoader, MetricSettings.Value, _pluginManager) + .InvokePlugins(_pluginManager); - _meterProvider = builder.Build(); - Logger.Information("OpenTelemetry meter initialized."); + _meterProvider = builder.Build(); + Logger.Information("OpenTelemetry meter initialized."); + } + else + { + AddLazilyLoadedMetricInstrumentations(LazyInstrumentationLoader, MetricSettings.Value.EnabledInstrumentations); + + Logger.Information("Initialized lazily-loaded metric instrumentations without initializing sdk."); + } } } catch (Exception ex) @@ -165,6 +182,84 @@ internal static class Instrumentation } } + private static void AddLazilyLoadedMetricInstrumentations(LazyInstrumentationLoader lazyInstrumentationLoader, IList enabledInstrumentations) + { + foreach (var instrumentation in enabledInstrumentations) + { + switch (instrumentation) + { + case MetricInstrumentation.AspNet: + DelayedInitialization.Metrics.AddAspNet(lazyInstrumentationLoader); + break; + case MetricInstrumentation.HttpClient: + DelayedInitialization.Metrics.AddHttpClient(lazyInstrumentationLoader); + break; + case MetricInstrumentation.NetRuntime: + break; + case MetricInstrumentation.Process: + break; + case MetricInstrumentation.NServiceBus: + break; + default: + Logger.Warning($"Configured metric intrumentation type is not supported: {instrumentation}"); + break; + } + } + } + + private static void AddLazilyLoadedTraceInstrumentations(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, IList enabledInstrumentations) + { + foreach (var instrumentation in enabledInstrumentations) + { + switch (instrumentation) + { + case TracerInstrumentation.AspNet: + DelayedInitialization.Traces.AddAspNet(lazyInstrumentationLoader, pluginManager); + break; + case TracerInstrumentation.HttpClient: + DelayedInitialization.Traces.AddHttpClient(lazyInstrumentationLoader, pluginManager); + break; + case TracerInstrumentation.GrpcNetClient: + DelayedInitialization.Traces.AddGrpcClient(lazyInstrumentationLoader, pluginManager); + break; + case TracerInstrumentation.SqlClient: + DelayedInitialization.Traces.AddSqlClient(lazyInstrumentationLoader, pluginManager); + break; + case TracerInstrumentation.Wcf: + DelayedInitialization.Traces.AddWcf(lazyInstrumentationLoader, pluginManager); + break; + case TracerInstrumentation.Quartz: + DelayedInitialization.Traces.AddQuartz(lazyInstrumentationLoader, pluginManager); + break; +#if NET6_0_OR_GREATER + case TracerInstrumentation.MySqlData: + DelayedInitialization.Traces.AddMySqlClient(LazyInstrumentationLoader, pluginManager); + break; + case TracerInstrumentation.EntityFrameworkCore: + DelayedInitialization.Traces.AddEntityFrameworkCore(LazyInstrumentationLoader, pluginManager); + break; + case TracerInstrumentation.MongoDB: + break; + case TracerInstrumentation.StackExchangeRedis: + break; + case TracerInstrumentation.MassTransit: + break; +#endif + case TracerInstrumentation.GraphQL: + break; + case TracerInstrumentation.Npgsql: + break; + case TracerInstrumentation.NServiceBus: + break; + case TracerInstrumentation.Elasticsearch: + break; + default: + Logger.Warning($"Configured trace intrumentation type is not supported: {instrumentation}"); + break; + } + } + } + private static void OnExit(object? sender, EventArgs e) { if (Interlocked.Exchange(ref _isExiting, value: 1) != 0) diff --git a/test/IntegrationTests/CustomSdkTests.cs b/test/IntegrationTests/CustomSdkTests.cs new file mode 100644 index 000000000..8bfb7a4f3 --- /dev/null +++ b/test/IntegrationTests/CustomSdkTests.cs @@ -0,0 +1,73 @@ +// +// 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 NET6_0_OR_GREATER +using Google.Protobuf; +using IntegrationTests.Helpers; +using OpenTelemetry.Proto.Trace.V1; +using Xunit.Abstractions; + +namespace IntegrationTests; + +[Collection(RedisCollection.Name)] +public class CustomSdkTests : TestHelper +{ + private readonly RedisFixture _redis; + + public CustomSdkTests(ITestOutputHelper output, RedisFixture redis) + : base("CustomSdk", output) + { + _redis = redis; + } + + [Fact] + [Trait("Category", "EndToEnd")] + [Trait("Containers", "Linux")] + public void SubmitsTraces_CustomSdk() + { + using var collector = new MockSpansCollector(Output); + SetExporter(collector); + + // ensure spans are exported by custom sdk with custom resource + collector.ResourceExpector.Expect("test_attr", "added_manually"); + collector.Expect("OpenTelemetry.Instrumentation.StackExchangeRedis", span => !IsTopLevel(span)); + +#if NET7_0_OR_GREATER + collector.Expect("System.Net.Http", span => !IsTopLevel(span)); +#else + collector.Expect("OpenTelemetry.Instrumentation.Http.HttpClient", span => !IsTopLevel(span)); +#endif + collector.Expect("TestApplication.CustomSdk", span => IsTopLevel(span)); + + EnableBytecodeInstrumentation(); + SetEnvironmentVariable("OTEL_DOTNET_AUTO_SETUP_SDK", "false"); + SetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL", "http/protobuf"); + + RunTestApplication(new() + { + Arguments = $"--redis {_redis.Port}" + }); + + collector.AssertExpectations(); + collector.ResourceExpector.AssertExpectations(); + } + + private static bool IsTopLevel(Span span) + { + return span.ParentSpanId == ByteString.Empty; + } +} +#endif diff --git a/test/test-applications/integrations/TestApplication.CustomSdk/Program.cs b/test/test-applications/integrations/TestApplication.CustomSdk/Program.cs new file mode 100644 index 000000000..6dbf989d9 --- /dev/null +++ b/test/test-applications/integrations/TestApplication.CustomSdk/Program.cs @@ -0,0 +1,84 @@ +// +// 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.Diagnostics; +using System.Net.Http; +using OpenTelemetry; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using StackExchange.Redis; +using TestApplication.Shared; + +namespace TestApplication.CustomSdk; + +public static class Program +{ + private static readonly ActivitySource ActivitySource = new( + "TestApplication.CustomSdk"); + + public static async Task Main(string[] args) + { + ConsoleHelper.WriteSplashScreen(args); + + // if auto-instrumentation is not injecting sdk + // then it's client's code responsibility + // to subscribe to all activity sources + using var tracerProvider = Sdk + .CreateTracerProviderBuilder() + // bytecode instrumentation + .AddSource("OpenTelemetry.Instrumentation.StackExchangeRedis") + // lazily-loaded instrumentation + .AddSource("OpenTelemetry.Instrumentation.Http.HttpClient") + .AddSource("System.Net.Http") // This works only System.Net.Http >= 7.0.0 + .AddLegacySource("System.Net.Http.HttpRequestOut") + // custom activity source + .AddSource("TestApplication.CustomSdk") + .ConfigureResource(builder => + builder.AddAttributes(new[] { new KeyValuePair("test_attr", "added_manually") })) + .AddOtlpExporter() + .AddConsoleExporter() + .Build(); + + var redisPort = GetRedisPort(args); + + var connectionString = $@"127.0.0.1:{redisPort}"; + + using (var activity = ActivitySource.StartActivity("Manual")) + { + using (var connection = await ConnectionMultiplexer.ConnectAsync(connectionString)) + { + var db = connection.GetDatabase(); + + db.Ping(); + } + + using var client = new HttpClient(); + await client.GetStringAsync("https://www.bing.com"); + } + + Thread.Sleep(TimeSpan.FromSeconds(10)); + } + + private static string GetRedisPort(string[] args) + { + if (args.Length > 1) + { + return args[1]; + } + + return "6379"; + } +} diff --git a/test/test-applications/integrations/TestApplication.CustomSdk/TestApplication.CustomSdk.csproj b/test/test-applications/integrations/TestApplication.CustomSdk/TestApplication.CustomSdk.csproj new file mode 100644 index 000000000..f0260bf7b --- /dev/null +++ b/test/test-applications/integrations/TestApplication.CustomSdk/TestApplication.CustomSdk.csproj @@ -0,0 +1,17 @@ + + + + Exe + + + + + + + + + + + + +