From 90e38e0d57ed52f8335c33f1edc308471ae6b793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Mon, 9 Dec 2024 13:43:37 +0100 Subject: [PATCH] Add support for SqlClient metrics --- CHANGELOG.md | 1 + docs/config.md | 23 ++++++--- .../Configurations/DelayedInitialization.cs | 8 ++- .../EnvironmentConfigurationMetricHelper.cs | 8 +++ .../Configurations/MetricInstrumentation.cs | 7 ++- .../Instrumentation.cs | 3 ++ .../Initializers/SqlClientInitializer.cs | 42 ++-------------- .../SqlClientMetricsInitializer.cs | 37 ++++++++++++++ .../SqlClientTracerInitializer.cs | 50 +++++++++++++++++++ .../SqlClientMicrosoftTests.cs | 35 +++++++++++++ .../SqlClientSystemDataTests.cs | 27 +++++++++- test/IntegrationTests/SqlClientSystemTests.cs | 35 +++++++++++++ .../Configurations/SettingsTests.cs | 1 + .../Program.cs | 14 ++++++ .../Program.cs | 14 ++++++ .../Program.cs | 14 ++++++ 16 files changed, 269 insertions(+), 50 deletions(-) create mode 100644 src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientMetricsInitializer.cs create mode 100644 src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientTracerInitializer.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aaff3a6e9..f75ae53091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h - Support for .NET9. - Support for [RabbitMQ.Client](https://www.nuget.org/packages/RabbitMQ.Client/) traces instrumentation for versions `7.0.0`+. +- Support for SqlClient metrics. ### Changed diff --git a/docs/config.md b/docs/config.md index 3bf904fe5e..19e352cfcd 100644 --- a/docs/config.md +++ b/docs/config.md @@ -181,18 +181,25 @@ due to lack of stable semantic convention. Metrics are stable, but particular instrumentation are in Experimental status due to lack of stable semantic convention. -| ID | Instrumented library | Documentation | Supported versions | Instrumentation type | Status | -|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------| -| `ASPNET` | ASP.NET Framework \[1\] **Not supported on .NET** | [ASP.NET metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.AspNet-1.9.0-beta.1/src/OpenTelemetry.Instrumentation.AspNet/README.md#list-of-metrics-produced) | * | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | -| `ASPNETCORE` | ASP.NET Core **Not supported on .NET Framework** | [ASP.NET Core metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.AspNetCore-1.10.0/src/OpenTelemetry.Instrumentation.AspNetCore/README.md#list-of-metrics-produced) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | -| `HTTPCLIENT` | [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) and [System.Net.HttpWebRequest](https://docs.microsoft.com/dotnet/api/system.net.httpwebrequest) | [HttpClient metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.Http-1.10.0/src/OpenTelemetry.Instrumentation.Http/README.md#list-of-metrics-produced) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | -| `NETRUNTIME` | [OpenTelemetry.Instrumentation.Runtime](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Runtime) | [Runtime metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.Runtime-1.10.0/src/OpenTelemetry.Instrumentation.Runtime/README.md#metrics) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | -| `PROCESS` | [OpenTelemetry.Instrumentation.Process](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Process) | [Process metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.Process-1.10.0-beta.1/src/OpenTelemetry.Instrumentation.Process/README.md#metrics) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | -| `NSERVICEBUS` | [NServiceBus](https://www.nuget.org/packages/NServiceBus) | [NServiceBus metrics](https://docs.particular.net/samples/open-telemetry/prometheus-grafana/#reporting-metric-values) | ≥8.0.0 & < 10.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | +| ID | Instrumented library | Documentation | Supported versions | Instrumentation type | Status | +|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| `ASPNET` | ASP.NET Framework \[1\] **Not supported on .NET** | [ASP.NET metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.AspNet-1.9.0-beta.1/src/OpenTelemetry.Instrumentation.AspNet/README.md#list-of-metrics-produced) | * | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | +| `ASPNETCORE` | ASP.NET Core **Not supported on .NET Framework** | [ASP.NET Core metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.AspNetCore-1.10.0/src/OpenTelemetry.Instrumentation.AspNetCore/README.md#list-of-metrics-produced) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | +| `HTTPCLIENT` | [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) and [System.Net.HttpWebRequest](https://docs.microsoft.com/dotnet/api/system.net.httpwebrequest) | [HttpClient metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.Http-1.10.0/src/OpenTelemetry.Instrumentation.Http/README.md#list-of-metrics-produced) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | +| `NETRUNTIME` | [OpenTelemetry.Instrumentation.Runtime](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Runtime) | [Runtime metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.Runtime-1.10.0/src/OpenTelemetry.Instrumentation.Runtime/README.md#metrics) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | +| `NSERVICEBUS` | [NServiceBus](https://www.nuget.org/packages/NServiceBus) | [NServiceBus metrics](https://docs.particular.net/samples/open-telemetry/prometheus-grafana/#reporting-metric-values) | ≥8.0.0 & < 10.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | +| `PROCESS` | [OpenTelemetry.Instrumentation.Process](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Process) | [Process metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/Instrumentation.Process-1.10.0-beta.1/src/OpenTelemetry.Instrumentation.Process/README.md#metrics) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | +| `SQLCLIENT` | [Microsoft.Data.SqlClient](https://www.nuget.org/packages/Microsoft.Data.SqlClient), [System.Data.SqlClient](https://www.nuget.org/packages/System.Data.SqlClient) \[2\] and `System.Data` (shipped with .NET Framework) | [SqlClient metrics](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/releases/tag/Instrumentation.SqlClient-1.10.0-beta.1) | * \[3\] | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) | \[1\]: The ASP.NET metrics are generated only if the `AspNet` trace instrumentation is also enabled. +\[2\]: `System.Data.SqlClient` is [deprecated](https://www.nuget.org/packages/System.Data.SqlClient/4.9.0#readme-body-tab). + +\[3\]: `Microsoft.Data.SqlClient` v3.* is not supported on .NET Framework, + due to [issue](https://github.com/open-telemetry/opentelemetry-dotnet/issues/4243). + `System.Data.SqlClient` is supported from version 4.8.5. + ### Logs instrumentations **Status**: [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md). diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/DelayedInitialization.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/DelayedInitialization.cs index 931a94b00b..c026b8c1b7 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/DelayedInitialization.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/DelayedInitialization.cs @@ -43,7 +43,7 @@ public static void AddGrpcClient(LazyInstrumentationLoader lazyInstrumentationLo [MethodImpl(MethodImplOptions.NoInlining)] public static void AddSqlClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings) { - new SqlClientInitializer(lazyInstrumentationLoader, pluginManager, tracerSettings); + new SqlClientTracerInitializer(lazyInstrumentationLoader, pluginManager, tracerSettings); } #if NET @@ -89,5 +89,11 @@ public static void AddHttpClient(LazyInstrumentationLoader lazyInstrumentationLo { new HttpClientMetricsInitializer(lazyInstrumentationLoader); } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void AddSqlClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { + new SqlClientMetricsInitializer(lazyInstrumentationLoader, pluginManager); + } } } diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationMetricHelper.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationMetricHelper.cs index 0199f7bc91..338f7eaf88 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationMetricHelper.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationMetricHelper.cs @@ -44,6 +44,7 @@ public static MeterProviderBuilder UseEnvironmentVariables( .AddMeter("Microsoft.AspNetCore.Diagnostics") .AddMeter("Microsoft.AspNetCore.RateLimiting"), #endif + MetricInstrumentation.SqlClient => Wrappers.AddSqlClientInstrumentation(builder, lazyInstrumentationLoader, pluginManager), _ => null, }; } @@ -115,6 +116,13 @@ public static MeterProviderBuilder AddProcessInstrumentation(MeterProviderBuilde return builder.AddProcessInstrumentation(); } + [MethodImpl(MethodImplOptions.NoInlining)] + public static MeterProviderBuilder AddSqlClientInstrumentation(MeterProviderBuilder builder, LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + { + DelayedInitialization.Metrics.AddSqlClient(lazyInstrumentationLoader, pluginManager); + return builder.AddMeter("OpenTelemetry.Instrumentation.SqlClient"); + } + // Exporters [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/MetricInstrumentation.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/MetricInstrumentation.cs index cfb66f4b5e..8d9ecc5131 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/MetricInstrumentation.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/MetricInstrumentation.cs @@ -39,6 +39,11 @@ internal enum MetricInstrumentation /// /// ASP.NET Core instrumentation. /// - AspNetCore = 6 + AspNetCore = 6, #endif + + /// + /// SqlClient instrumentation. + /// + SqlClient = 7 } diff --git a/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs b/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs index 74ad28e3a9..b1973df75d 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs @@ -296,6 +296,9 @@ private static void AddLazilyLoadedMetricInstrumentations(LazyInstrumentationLoa break; case MetricInstrumentation.NServiceBus: break; + case MetricInstrumentation.SqlClient: + DelayedInitialization.Metrics.AddSqlClient(lazyInstrumentationLoader, pluginManager); + break; default: Logger.Warning($"Configured metric instrumentation type is not supported: {instrumentation}"); if (FailFastSettings.Value.FailFast) diff --git a/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientInitializer.cs b/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientInitializer.cs index 887b4fe87a..b25e5ec2f7 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientInitializer.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientInitializer.cs @@ -1,23 +1,12 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -using System.Reflection; -using OpenTelemetry.AutoInstrumentation.Configurations; -using OpenTelemetry.AutoInstrumentation.Plugins; - namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers; -internal class SqlClientInitializer +internal abstract class SqlClientInitializer { - private readonly PluginManager _pluginManager; - private readonly TracerSettings _tracerSettings; - - private int _initialized; - - public SqlClientInitializer(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings) + protected SqlClientInitializer(LazyInstrumentationLoader lazyInstrumentationLoader) { - _pluginManager = pluginManager; - _tracerSettings = tracerSettings; lazyInstrumentationLoader.Add(new GenericInitializer("System.Data.SqlClient", InitializeOnFirstCall)); lazyInstrumentationLoader.Add(new GenericInitializer("Microsoft.Data.SqlClient", InitializeOnFirstCall)); @@ -26,30 +15,5 @@ public SqlClientInitializer(LazyInstrumentationLoader lazyInstrumentationLoader, #endif } - private void InitializeOnFirstCall(ILifespanManager lifespanManager) - { - if (Interlocked.Exchange(ref _initialized, value: 1) != default) - { - // InitializeOnFirstCall() was already called before - return; - } - - var instrumentationType = Type.GetType("OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentation, OpenTelemetry.Instrumentation.SqlClient")!; - - var options = new OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions - { - SetDbStatementForText = _tracerSettings.InstrumentationOptions.SqlClientSetDbStatementForText - }; - _pluginManager.ConfigureTracesOptions(options); - - var propertyInfo = instrumentationType.GetProperty("TracingOptions", BindingFlags.Static | BindingFlags.Public); - propertyInfo?.SetValue(null, options); - - var instrumentation = instrumentationType.InvokeMember("AddTracingHandle", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, null, []); - - if (instrumentation != null) - { - lifespanManager.Track(instrumentation); - } - } + protected abstract void InitializeOnFirstCall(ILifespanManager lifespanManager); } diff --git a/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientMetricsInitializer.cs b/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientMetricsInitializer.cs new file mode 100644 index 0000000000..73514c6d3b --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientMetricsInitializer.cs @@ -0,0 +1,37 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Reflection; +using OpenTelemetry.AutoInstrumentation.Plugins; + +namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers; + +internal sealed class SqlClientMetricsInitializer : SqlClientInitializer +{ + private readonly PluginManager _pluginManager; + + private int _initialized; + + public SqlClientMetricsInitializer(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager) + : base(lazyInstrumentationLoader) + { + _pluginManager = pluginManager; + } + + protected override void InitializeOnFirstCall(ILifespanManager lifespanManager) + { + if (Interlocked.Exchange(ref _initialized, value: 1) != default) + { + // InitializeOnFirstCall() was already called before + return; + } + + var instrumentationType = Type.GetType("OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentation, OpenTelemetry.Instrumentation.SqlClient")!; + var instrumentation = instrumentationType.InvokeMember("AddMetricHandle", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, null, []); + + if (instrumentation != null) + { + lifespanManager.Track(instrumentation); + } + } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientTracerInitializer.cs b/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientTracerInitializer.cs new file mode 100644 index 0000000000..24578c81c2 --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Loading/Initializers/SqlClientTracerInitializer.cs @@ -0,0 +1,50 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Reflection; +using OpenTelemetry.AutoInstrumentation.Configurations; +using OpenTelemetry.AutoInstrumentation.Plugins; + +namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers; + +internal sealed class SqlClientTracerInitializer : SqlClientInitializer +{ + private readonly PluginManager _pluginManager; + private readonly TracerSettings _tracerSettings; + + private int _initialized; + + public SqlClientTracerInitializer(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings) + : base(lazyInstrumentationLoader) + { + _pluginManager = pluginManager; + _tracerSettings = tracerSettings; + } + + protected override void InitializeOnFirstCall(ILifespanManager lifespanManager) + { + if (Interlocked.Exchange(ref _initialized, value: 1) != default) + { + // InitializeOnFirstCall() was already called before + return; + } + + var instrumentationType = Type.GetType("OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentation, OpenTelemetry.Instrumentation.SqlClient")!; + + var options = new OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions + { + SetDbStatementForText = _tracerSettings.InstrumentationOptions.SqlClientSetDbStatementForText + }; + _pluginManager.ConfigureTracesOptions(options); + + var propertyInfo = instrumentationType.GetProperty("TracingOptions", BindingFlags.Static | BindingFlags.Public); + propertyInfo?.SetValue(null, options); + + var instrumentation = instrumentationType.InvokeMember("AddTracingHandle", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, null, []); + + if (instrumentation != null) + { + lifespanManager.Track(instrumentation); + } + } +} diff --git a/test/IntegrationTests/SqlClientMicrosoftTests.cs b/test/IntegrationTests/SqlClientMicrosoftTests.cs index 01c662c417..1bef83782f 100644 --- a/test/IntegrationTests/SqlClientMicrosoftTests.cs +++ b/test/IntegrationTests/SqlClientMicrosoftTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using IntegrationTests.Helpers; +using OpenTelemetry.AutoInstrumentation.Configurations; using Xunit.Abstractions; namespace IntegrationTests; @@ -38,4 +39,38 @@ public void SubmitTraces(string packageVersion) collector.AssertExpectations(); } + + [SkippableTheory] + [Trait("Category", "EndToEnd")] + [Trait("Containers", "Linux")] + [MemberData(nameof(LibraryVersion.SqlClientMicrosoft), MemberType = typeof(LibraryVersion))] + public void SubmitMetrics(string packageVersion) + { + // Skip the test if fixture does not support current platform + _sqlServerFixture.SkipIfUnsupportedPlatform(); + + using var collector = new MockMetricsCollector(Output); + SetExporter(collector); + + collector.Expect("OpenTelemetry.Instrumentation.SqlClient"); + + SetEnvironmentVariable("LONG_RUNNING", "true"); + SetEnvironmentVariable("OTEL_METRIC_EXPORT_INTERVAL", "100"); + SetEnvironmentVariable(ConfigurationKeys.Traces.TracesEnabled, bool.FalseString); // make sure that traces instrumentation is not needed + + using var process = StartTestApplication(new TestSettings + { + Arguments = $"{_sqlServerFixture.Password} {_sqlServerFixture.Port}", + PackageVersion = packageVersion + }); + + try + { + collector.AssertExpectations(); + } + finally + { + process?.Kill(); + } + } } diff --git a/test/IntegrationTests/SqlClientSystemDataTests.cs b/test/IntegrationTests/SqlClientSystemDataTests.cs index b3e9f63351..5b98a99826 100644 --- a/test/IntegrationTests/SqlClientSystemDataTests.cs +++ b/test/IntegrationTests/SqlClientSystemDataTests.cs @@ -3,7 +3,7 @@ #if NETFRAMEWORK using IntegrationTests.Helpers; -using Xunit; +using OpenTelemetry.AutoInstrumentation.Configurations; using Xunit.Abstractions; namespace IntegrationTests; @@ -27,5 +27,30 @@ public void SubmitTraces() collector.AssertExpectations(); } + + [Fact] + [Trait("Category", "EndToEnd")] + public void SubmitMetrics() + { + using var collector = new MockMetricsCollector(Output); + SetExporter(collector); + + collector.Expect("OpenTelemetry.Instrumentation.SqlClient"); + + SetEnvironmentVariable("LONG_RUNNING", "true"); + SetEnvironmentVariable("OTEL_METRIC_EXPORT_INTERVAL", "100"); + SetEnvironmentVariable(ConfigurationKeys.Traces.TracesEnabled, bool.FalseString); // make sure that traces instrumentation is not needed + + using var process = StartTestApplication(); + + try + { + collector.AssertExpectations(); + } + finally + { + process?.Kill(); + } + } } #endif diff --git a/test/IntegrationTests/SqlClientSystemTests.cs b/test/IntegrationTests/SqlClientSystemTests.cs index 983792efa2..c3ce5a88d2 100644 --- a/test/IntegrationTests/SqlClientSystemTests.cs +++ b/test/IntegrationTests/SqlClientSystemTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using IntegrationTests.Helpers; +using OpenTelemetry.AutoInstrumentation.Configurations; using Xunit.Abstractions; namespace IntegrationTests; @@ -62,4 +63,38 @@ public void SubmitTraces(string packageVersion, bool dbStatementForText) collector.AssertExpectations(); } + + [SkippableTheory] + [Trait("Category", "EndToEnd")] + [Trait("Containers", "Linux")] + [MemberData(nameof(LibraryVersion.SqlClientSystem), MemberType = typeof(LibraryVersion))] + public void SubmitMetrics(string packageVersion) + { + // Skip the test if fixture does not support current platform + _sqlServerFixture.SkipIfUnsupportedPlatform(); + + using var collector = new MockMetricsCollector(Output); + SetExporter(collector); + + collector.Expect("OpenTelemetry.Instrumentation.SqlClient"); + + SetEnvironmentVariable("LONG_RUNNING", "true"); + SetEnvironmentVariable("OTEL_METRIC_EXPORT_INTERVAL", "100"); + SetEnvironmentVariable(ConfigurationKeys.Traces.TracesEnabled, bool.FalseString); // make sure that traces instrumentation is not needed + + using var process = StartTestApplication(new TestSettings + { + Arguments = $"{_sqlServerFixture.Password} {_sqlServerFixture.Port}", + PackageVersion = packageVersion + }); + + try + { + collector.AssertExpectations(); + } + finally + { + process?.Kill(); + } + } } diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index 4b0ee71a88..d319d2ee01 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -345,6 +345,7 @@ internal void TracerSettings_Instrumentations_SupportedValues(string tracerInstr #if NET [InlineData("ASPNETCORE", MetricInstrumentation.AspNetCore)] #endif + [InlineData("SQLCLIENT", MetricInstrumentation.SqlClient)] internal void MeterSettings_Instrumentations_SupportedValues(string meterInstrumentation, MetricInstrumentation expectedMetricInstrumentation) { Environment.SetEnvironmentVariable(ConfigurationKeys.Metrics.MetricsInstrumentationEnabled, "false"); diff --git a/test/test-applications/integrations/TestApplication.SqlClient.Microsoft/Program.cs b/test/test-applications/integrations/TestApplication.SqlClient.Microsoft/Program.cs index 453bab1582..2c877b24f2 100644 --- a/test/test-applications/integrations/TestApplication.SqlClient.Microsoft/Program.cs +++ b/test/test-applications/integrations/TestApplication.SqlClient.Microsoft/Program.cs @@ -1,6 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Diagnostics; using Microsoft.Data.SqlClient; using TestApplication.Shared; @@ -32,6 +33,19 @@ public static async Task Main(string[] args) { await ExecuteAsyncCommands(connection); } + + // The "LONG_RUNNING" environment variable is used by tests that access/receive + // data that takes time to be produced. + var longRunning = Environment.GetEnvironmentVariable("LONG_RUNNING"); + if (longRunning == "true") + { + // In this case it is necessary to ensure that the test has a chance to read the + // expected data, only by keeping the application alive for some time that can + // be ensured. Anyway, tests that set "LONG_RUNNING" env var to true are expected + // to kill the process directly. + Console.WriteLine("LONG_RUNNING is true, waiting for process to be killed..."); + Process.GetCurrentProcess().WaitForExit(); + } } private static void ExecuteCommands(SqlConnection connection) diff --git a/test/test-applications/integrations/TestApplication.SqlClient.System.NetFramework/Program.cs b/test/test-applications/integrations/TestApplication.SqlClient.System.NetFramework/Program.cs index 8cf1f3f5d8..1afcf6a148 100644 --- a/test/test-applications/integrations/TestApplication.SqlClient.System.NetFramework/Program.cs +++ b/test/test-applications/integrations/TestApplication.SqlClient.System.NetFramework/Program.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using System.Data.SqlClient; +using System.Diagnostics; using TestApplication.Shared; namespace TestApplication.SqlClient.System; @@ -31,6 +32,19 @@ public static async Task Main(string[] args) { await ExecuteAsyncCommands(connection); } + + // The "LONG_RUNNING" environment variable is used by tests that access/receive + // data that takes time to be produced. + var longRunning = Environment.GetEnvironmentVariable("LONG_RUNNING"); + if (longRunning == "true") + { + // In this case it is necessary to ensure that the test has a chance to read the + // expected data, only by keeping the application alive for some time that can + // be ensured. Anyway, tests that set "LONG_RUNNING" env var to true are expected + // to kill the process directly. + Console.WriteLine("LONG_RUNNING is true, waiting for process to be killed..."); + Process.GetCurrentProcess().WaitForExit(); + } } private static void ExecuteCommands(SqlConnection connection) diff --git a/test/test-applications/integrations/TestApplication.SqlClient.System/Program.cs b/test/test-applications/integrations/TestApplication.SqlClient.System/Program.cs index de1f0e333f..7af1da69fd 100644 --- a/test/test-applications/integrations/TestApplication.SqlClient.System/Program.cs +++ b/test/test-applications/integrations/TestApplication.SqlClient.System/Program.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using System.Data.SqlClient; +using System.Diagnostics; using TestApplication.Shared; namespace TestApplication.SqlClient.System; @@ -33,6 +34,19 @@ public static async Task Main(string[] args) { await ExecuteAsyncCommands(connection); } + + // The "LONG_RUNNING" environment variable is used by tests that access/receive + // data that takes time to be produced. + var longRunning = Environment.GetEnvironmentVariable("LONG_RUNNING"); + if (longRunning == "true") + { + // In this case it is necessary to ensure that the test has a chance to read the + // expected data, only by keeping the application alive for some time that can + // be ensured. Anyway, tests that set "LONG_RUNNING" env var to true are expected + // to kill the process directly. + Console.WriteLine("LONG_RUNNING is true, waiting for process to be killed..."); + Process.GetCurrentProcess().WaitForExit(); + } } private static void ExecuteCommands(SqlConnection connection)