diff --git a/build/Common.props b/build/Common.props index deed26cd9ca..e7a089f2b96 100644 --- a/build/Common.props +++ b/build/Common.props @@ -31,10 +31,11 @@ [2.1.1,6.0) [3.3.3] [17.3.0] + [3.1.0,) [2.1.0,) [3.1.0,) [3.1.0,) - [3.1.0,) + [5.0.0,) [1.0.0,2.0) [1.0.0,2.0) [0.12.1,0.13) diff --git a/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md b/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md index 041b2df43eb..f60481649c0 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Added support for loading environment variables from `IConfiguration` when + using the `AddJaegerExporter` extension + ([#3720](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3720)) + ## 1.4.0-beta.1 Released 2022-Sep-29 diff --git a/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterHelperExtensions.cs index c9da743562d..6a6bd83efc0 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterHelperExtensions.cs @@ -62,10 +62,15 @@ public static TracerProviderBuilder AddJaegerExporter( name ??= Options.DefaultName; - if (configure != null) + builder.ConfigureServices(services => { - builder.ConfigureServices(services => services.Configure(name, configure)); - } + if (configure != null) + { + services.Configure(name, configure); + } + + services.RegisterOptionsFactory(configuration => new JaegerExporterOptions(configuration)); + }); return builder.ConfigureBuilder((sp, builder) => { diff --git a/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterOptions.cs b/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterOptions.cs index b992048d338..f2946388b90 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterOptions.cs @@ -17,6 +17,7 @@ using System; using System.Diagnostics; using System.Net.Http; +using Microsoft.Extensions.Configuration; using OpenTelemetry.Internal; using OpenTelemetry.Trace; @@ -43,37 +44,45 @@ public class JaegerExporterOptions internal static readonly Func DefaultHttpClientFactory = () => new HttpClient(); + /// + /// Initializes a new instance of the class. + /// public JaegerExporterOptions() + : this(new ConfigurationBuilder().AddEnvironmentVariables().Build()) { - if (EnvironmentVariableHelper.LoadString(OTelProtocolEnvVarKey, out string protocolEnvVar)) + } + + internal JaegerExporterOptions(IConfiguration configuration) + { + if (configuration.TryGetValue( + OTelProtocolEnvVarKey, + JaegerExporterProtocolParser.TryParse, + out var protocol)) { - if (JaegerExporterProtocolParser.TryParse(protocolEnvVar, out var protocol)) - { - this.Protocol = protocol; - } - else - { - throw new FormatException($"{OTelProtocolEnvVarKey} environment variable has an invalid value: '{protocolEnvVar}'"); - } + this.Protocol = protocol; } - if (EnvironmentVariableHelper.LoadString(OTelAgentHostEnvVarKey, out string agentHostEnvVar)) + if (configuration.TryGetStringValue(OTelAgentHostEnvVarKey, out var agentHost)) { - this.AgentHost = agentHostEnvVar; + this.AgentHost = agentHost; } - if (EnvironmentVariableHelper.LoadNumeric(OTelAgentPortEnvVarKey, out int agentPortEnvVar)) + if (configuration.TryGetIntValue(OTelAgentPortEnvVarKey, out var agentPort)) { - this.AgentPort = agentPortEnvVar; + this.AgentPort = agentPort; } - if (EnvironmentVariableHelper.LoadString(OTelEndpointEnvVarKey, out string endpointEnvVar) - && Uri.TryCreate(endpointEnvVar, UriKind.Absolute, out Uri endpoint)) + if (configuration.TryGetUriValue(OTelEndpointEnvVarKey, out var endpoint)) { this.Endpoint = endpoint; } } + /// + /// Gets or sets the to use when + /// communicating to Jaeger. Default value: . + /// public JaegerExportProtocol Protocol { get; set; } = JaegerExportProtocol.UdpCompactThrift; /// diff --git a/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterProtocolParser.cs b/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterProtocolParser.cs index 52fb109350f..2a770135541 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterProtocolParser.cs +++ b/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterProtocolParser.cs @@ -20,7 +20,7 @@ internal static class JaegerExporterProtocolParser { public static bool TryParse(string value, out JaegerExportProtocol result) { - switch (value?.Trim()) + switch (value.Trim().ToLower()) { case "udp/thrift.compact": result = JaegerExportProtocol.UdpCompactThrift; diff --git a/src/OpenTelemetry.Exporter.Jaeger/OpenTelemetry.Exporter.Jaeger.csproj b/src/OpenTelemetry.Exporter.Jaeger/OpenTelemetry.Exporter.Jaeger.csproj index abd411f0df3..2720fd1a2b2 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/OpenTelemetry.Exporter.Jaeger.csproj +++ b/src/OpenTelemetry.Exporter.Jaeger/OpenTelemetry.Exporter.Jaeger.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md index 352288c42a8..eb922b12f2b 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Added support for loading environment variables from `IConfiguration` when + using the `AddZipkinExporter` extension + ([#3759](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3759)) + ## 1.4.0-beta.1 Released 2022-Sep-29 diff --git a/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj b/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj index 0aee4bd6334..c5e14a100de 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj +++ b/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterHelperExtensions.cs index 261cecd1a64..93a43014874 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterHelperExtensions.cs @@ -62,10 +62,15 @@ public static TracerProviderBuilder AddZipkinExporter( name ??= Options.DefaultName; - if (configure != null) + builder.ConfigureServices(services => { - builder.ConfigureServices(services => services.Configure(name, configure)); - } + if (configure != null) + { + services.Configure(name, configure); + } + + services.RegisterOptionsFactory(configuration => new ZipkinExporterOptions(configuration)); + }); return builder.ConfigureBuilder((sp, builder) => { diff --git a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterOptions.cs b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterOptions.cs index 9c354574a72..d51eb007b81 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterOptions.cs @@ -17,6 +17,7 @@ using System; using System.Diagnostics; using System.Net.Http; +using Microsoft.Extensions.Configuration; using OpenTelemetry.Internal; using OpenTelemetry.Trace; @@ -44,8 +45,13 @@ public sealed class ZipkinExporterOptions /// Initializes zipkin endpoint. /// public ZipkinExporterOptions() + : this(new ConfigurationBuilder().AddEnvironmentVariables().Build()) { - if (EnvironmentVariableHelper.LoadUri(ZipkinEndpointEnvVar, out Uri endpoint)) + } + + internal ZipkinExporterOptions(IConfiguration configuration) + { + if (configuration.TryGetUriValue(ZipkinEndpointEnvVar, out var endpoint)) { this.Endpoint = endpoint; } diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 23af195b8d5..9fca6a77d4e 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* Use `Activity.Status` and `Activity.StatusDescription` properties instead of + `OpenTelemetry.Trace.Status` and `OpenTelemetry.Trace.Status.Description` + respectively to set activity status. + ([#3118](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3118)) + ([#3751](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3751)) + ## 1.0.0-rc9.7 Released 2022-Sep-29 diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs index 4f360bcbf18..aa6f2ccc4e3 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs @@ -146,17 +146,7 @@ public override void OnEventWritten(string name, object payload) return; } - try - { - if (activity.IsAllDataRequested) - { - activity.SetStatus(Status.Unset); - } - } - finally - { - activity.Stop(); - } + activity.Stop(); } break; @@ -180,7 +170,7 @@ public override void OnEventWritten(string name, object payload) { if (this.exceptionFetcher.TryFetch(payload, out Exception exception) && exception != null) { - activity.SetStatus(Status.Error.WithDescription(exception.Message)); + activity.SetStatus(ActivityStatusCode.Error, exception.Message); if (this.options.RecordException) { diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs index 9417df4798d..fde30c97575 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs @@ -181,18 +181,17 @@ private void OnEndExecute(EventWrittenEventArgs eventData) if (activity.IsAllDataRequested) { int compositeState = (int)eventData.Payload[1]; - if ((compositeState & 0b001) == 0b001) + if ((compositeState & 0b001) != 0b001) { - activity.SetStatus(Status.Unset); - } - else if ((compositeState & 0b010) == 0b010) - { - var errorText = $"SqlExceptionNumber {eventData.Payload[2]} thrown."; - activity.SetStatus(Status.Error.WithDescription(errorText)); - } - else - { - activity.SetStatus(Status.Error.WithDescription("Unknown Sql failure.")); + if ((compositeState & 0b010) == 0b010) + { + var errorText = $"SqlExceptionNumber {eventData.Payload[2]} thrown."; + activity.SetStatus(ActivityStatusCode.Error, errorText); + } + else + { + activity.SetStatus(ActivityStatusCode.Error, "Unknown Sql failure."); + } } } } diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index a189a19f0c5..5ea2486a319 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -12,6 +12,10 @@ `BatchLogRecordExportProcessor.OnEnd` is fired. ([#3731](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3731)) +* Added support for loading environment variables from `IConfiguration` when + using `TracerProviderBuilder` or `MeterProviderBuilder` + ([#3720](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3720)) + ## 1.4.0-beta.1 Released 2022-Sep-29 diff --git a/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs b/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs new file mode 100644 index 00000000000..ab136d3a4f0 --- /dev/null +++ b/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs @@ -0,0 +1,42 @@ +// +// 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. +// + +#nullable enable + +using System.Diagnostics; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection; + +internal static class ProviderBuilderServiceCollectionExtensions +{ + public static IServiceCollection AddOpenTelemetryProviderBuilderServices(this IServiceCollection services) + { + Debug.Assert(services != null, "services was null"); + + services.AddOptions(); + + // Note: When using a host builder IConfiguration is automatically + // registered and this registration will no-op. This only runs for + // Sdk.Create* style or when manually creating a ServiceCollection. The + // point of this registration is to make IConfiguration available in + // those cases. + services!.TryAddSingleton(sp => new ConfigurationBuilder().AddEnvironmentVariables().Build()); + + return services!; + } +} diff --git a/src/OpenTelemetry/Internal/ConfigurationExtensions.cs b/src/OpenTelemetry/Internal/ConfigurationExtensions.cs new file mode 100644 index 00000000000..dc22bbf673c --- /dev/null +++ b/src/OpenTelemetry/Internal/ConfigurationExtensions.cs @@ -0,0 +1,164 @@ +// +// 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. +// + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics; +#if !NETFRAMEWORK && !NETSTANDARD2_0 +using System.Diagnostics.CodeAnalysis; +#endif +using System.Globalization; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace OpenTelemetry.Internal; + +internal static class ConfigurationExtensions +{ + public delegate bool TryParseFunc( + string value, +#if !NETFRAMEWORK && !NETSTANDARD2_0 + [NotNullWhen(true)] +#endif + out T? parsedValue); + + public static bool TryGetStringValue( + this IConfiguration configuration, + string key, +#if !NETFRAMEWORK && !NETSTANDARD2_0 + [NotNullWhen(true)] +#endif + out string? value) + { + value = configuration.GetValue(key, null); + + return !string.IsNullOrWhiteSpace(value); + } + + public static bool TryGetUriValue( + this IConfiguration configuration, + string key, +#if !NETFRAMEWORK && !NETSTANDARD2_0 + [NotNullWhen(true)] +#endif + out Uri? value) + { + if (!configuration.TryGetStringValue(key, out var stringValue)) + { + value = null; + return false; + } + + if (!Uri.TryCreate(stringValue, UriKind.Absolute, out value)) + { + throw new FormatException($"{key} environment variable has an invalid value: '{stringValue}'"); + } + + return true; + } + + public static bool TryGetIntValue( + this IConfiguration configuration, + string key, + out int value) + { + if (!configuration.TryGetStringValue(key, out var stringValue)) + { + value = default; + return false; + } + + if (!int.TryParse(stringValue, NumberStyles.None, CultureInfo.InvariantCulture, out value)) + { + throw new FormatException($"{key} environment variable has an invalid value: '{stringValue}'"); + } + + return true; + } + + public static bool TryGetValue( + this IConfiguration configuration, + string key, + TryParseFunc tryParseFunc, +#if !NETFRAMEWORK && !NETSTANDARD2_0 + [NotNullWhen(true)] +#endif + out T? value) + { + if (!configuration.TryGetStringValue(key, out var stringValue)) + { + value = default; + return false; + } + + if (!tryParseFunc(stringValue!, out value)) + { + throw new FormatException($"{key} environment variable has an invalid value: '{stringValue}'"); + } + + return true; + } + + public static IServiceCollection RegisterOptionsFactory( + this IServiceCollection services, + Func optionsFactoryFunc) + where T : class + { + Debug.Assert(services != null, "services was null"); + Debug.Assert(optionsFactoryFunc != null, "optionsFactoryFunc was null"); + + services!.TryAddSingleton>(sp => + { + return new DelegatingOptionsFactory( + optionsFactoryFunc!, + sp.GetRequiredService(), + sp.GetServices>(), + sp.GetServices>(), + sp.GetServices>()); + }); + + return services!; + } + + private sealed class DelegatingOptionsFactory : OptionsFactory + where T : class + { + private readonly Func optionsFactoryFunc; + private readonly IConfiguration configuration; + + public DelegatingOptionsFactory( + Func optionsFactoryFunc, + IConfiguration configuration, + IEnumerable> setups, + IEnumerable> postConfigures, + IEnumerable> validations) + : base(setups, postConfigures, validations) + { + Debug.Assert(optionsFactoryFunc != null, "optionsFactoryFunc was null"); + Debug.Assert(configuration != null, "configuration was null"); + + this.optionsFactoryFunc = optionsFactoryFunc!; + this.configuration = configuration!; + } + + protected override T CreateInstance(string name) + => this.optionsFactoryFunc(this.configuration); + } +} diff --git a/src/OpenTelemetry/Internal/EnvironmentVariableHelper.cs b/src/OpenTelemetry/Internal/EnvironmentVariableHelper.cs index f6ea9783590..6a745e4ad11 100644 --- a/src/OpenTelemetry/Internal/EnvironmentVariableHelper.cs +++ b/src/OpenTelemetry/Internal/EnvironmentVariableHelper.cs @@ -76,6 +76,25 @@ public static bool LoadNumeric(string envVarKey, out int result) return false; } + return LoadNumeric(envVarKey, value, out result); + } + + /// + /// Reads an environment variable and parses is as a + /// + /// numeric value - a non-negative decimal integer. + /// + /// The name of the environment variable. + /// The value of the environment variable. + /// The parsed value of the environment variable. + /// + /// Returns true when a non-empty value was read; otherwise, false. + /// + /// + /// Thrown when failed to parse the non-empty value. + /// + public static bool LoadNumeric(string envVarKey, string value, out int result) + { if (!int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out result)) { throw new FormatException($"{envVarKey} environment variable has an invalid value: '{value}'"); diff --git a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderBase.cs b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderBase.cs index 186926a5799..80482d7b5cd 100644 --- a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderBase.cs +++ b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderBase.cs @@ -52,7 +52,7 @@ internal MeterProviderBuilderBase(IServiceCollection services) { Guard.ThrowIfNull(services); - services.AddOptions(); + services.AddOpenTelemetryProviderBuilderServices(); services.TryAddSingleton(sp => new MeterProviderSdk(sp, ownsServiceProvider: false)); this.services = services; @@ -65,7 +65,7 @@ protected MeterProviderBuilderBase() { var services = new ServiceCollection(); - services.AddOptions(); + services.AddOpenTelemetryProviderBuilderServices(); this.services = services; this.ownsServices = true; diff --git a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderServiceCollectionHelper.cs b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderServiceCollectionHelper.cs index e53f412828d..779e7f017f6 100644 --- a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderServiceCollectionHelper.cs +++ b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderServiceCollectionHelper.cs @@ -42,7 +42,7 @@ internal static IServiceCollection RegisterConfigureStateCallback( Debug.Assert(services != null, "services was null"); Debug.Assert(configure != null, "configure was null"); - return services.AddSingleton(new ConfigureMeterProviderBuilderStateCallbackRegistration(configure!)); + return services!.AddSingleton(new ConfigureMeterProviderBuilderStateCallbackRegistration(configure!)); } internal static void InvokeRegisteredConfigureStateCallbacks( @@ -52,7 +52,7 @@ internal static void InvokeRegisteredConfigureStateCallbacks( Debug.Assert(serviceProvider != null, "serviceProvider was null"); Debug.Assert(state != null, "state was null"); - var callbackRegistrations = serviceProvider.GetServices(); + var callbackRegistrations = serviceProvider!.GetServices(); foreach (var callbackRegistration in callbackRegistrations) { diff --git a/src/OpenTelemetry/OpenTelemetry.csproj b/src/OpenTelemetry/OpenTelemetry.csproj index 542fcc9e407..c05c686b45b 100644 --- a/src/OpenTelemetry/OpenTelemetry.csproj +++ b/src/OpenTelemetry/OpenTelemetry.csproj @@ -17,8 +17,10 @@ + + diff --git a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs index a747395308c..223f07e798c 100644 --- a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs +++ b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs @@ -51,7 +51,7 @@ internal TracerProviderBuilderBase(IServiceCollection services) { Guard.ThrowIfNull(services); - services.AddOptions(); + services.AddOpenTelemetryProviderBuilderServices(); services.TryAddSingleton(sp => new TracerProviderSdk(sp, ownsServiceProvider: false)); this.services = services; @@ -64,7 +64,7 @@ protected TracerProviderBuilderBase() { var services = new ServiceCollection(); - services.AddOptions(); + services.AddOpenTelemetryProviderBuilderServices(); this.services = services; this.ownsServices = true; diff --git a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderServiceCollectionHelper.cs b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderServiceCollectionHelper.cs index ae68e0881b5..5de52f1a495 100644 --- a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderServiceCollectionHelper.cs +++ b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderServiceCollectionHelper.cs @@ -42,7 +42,7 @@ internal static IServiceCollection RegisterConfigureStateCallback( Debug.Assert(services != null, "services was null"); Debug.Assert(configure != null, "configure was null"); - return services.AddSingleton(new ConfigureTracerProviderBuilderStateCallbackRegistration(configure!)); + return services!.AddSingleton(new ConfigureTracerProviderBuilderStateCallbackRegistration(configure!)); } internal static void InvokeRegisteredConfigureStateCallbacks( @@ -52,7 +52,7 @@ internal static void InvokeRegisteredConfigureStateCallbacks( Debug.Assert(serviceProvider != null, "serviceProvider was null"); Debug.Assert(state != null, "state was null"); - var callbackRegistrations = serviceProvider.GetServices(); + var callbackRegistrations = serviceProvider!.GetServices(); foreach (var callbackRegistration in callbackRegistrations) { diff --git a/test/OpenTelemetry.Exporter.Jaeger.Tests/JaegerExporterOptionsTests.cs b/test/OpenTelemetry.Exporter.Jaeger.Tests/JaegerExporterOptionsTests.cs index 33143faf16f..42b0548be66 100644 --- a/test/OpenTelemetry.Exporter.Jaeger.Tests/JaegerExporterOptionsTests.cs +++ b/test/OpenTelemetry.Exporter.Jaeger.Tests/JaegerExporterOptionsTests.cs @@ -15,6 +15,8 @@ // using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; using Xunit; namespace OpenTelemetry.Exporter.Jaeger.Tests @@ -93,6 +95,29 @@ public void JaegerExporterOptions_EnvironmentVariableNames() Assert.Equal("OTEL_EXPORTER_JAEGER_ENDPOINT", JaegerExporterOptions.OTelEndpointEnvVarKey); } + [Fact] + public void JaegerExporterOptions_FromConfigurationTest() + { + var values = new Dictionary() + { + [JaegerExporterOptions.OTelProtocolEnvVarKey] = "http/thrift.binary", + [JaegerExporterOptions.OTelAgentHostEnvVarKey] = "jaeger-host", + [JaegerExporterOptions.OTelAgentPortEnvVarKey] = "123", + [JaegerExporterOptions.OTelEndpointEnvVarKey] = "http://custom-endpoint:12345", + }; + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(values) + .Build(); + + var options = new JaegerExporterOptions(configuration); + + Assert.Equal("jaeger-host", options.AgentHost); + Assert.Equal(123, options.AgentPort); + Assert.Equal(JaegerExportProtocol.HttpBinaryThrift, options.Protocol); + Assert.Equal(new Uri("http://custom-endpoint:12345"), options.Endpoint); + } + private static void ClearEnvVars() { Environment.SetEnvironmentVariable(JaegerExporterOptions.OTelProtocolEnvVarKey, null); diff --git a/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs b/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs index 4cd2d97d2e5..3c19c2c106c 100644 --- a/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs +++ b/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs @@ -23,6 +23,7 @@ using System.Net; using System.Net.Http; using System.Text; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry.Exporter.Zipkin.Implementation; using OpenTelemetry.Resources; @@ -207,6 +208,23 @@ public void ErrorGettingUriFromEnvVarSetsDefaultEndpointValue() } } + [Fact] + public void EndpointConfigurationUsingIConfiguration() + { + var values = new Dictionary() + { + [ZipkinExporterOptions.ZipkinEndpointEnvVar] = "http://custom-endpoint:12345", + }; + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(values) + .Build(); + + var options = new ZipkinExporterOptions(configuration); + + Assert.Equal(new Uri("http://custom-endpoint:12345"), options.Endpoint); + } + [Fact] public void UserHttpFactoryCalled() { diff --git a/test/OpenTelemetry.Extensions.Hosting.Tests/HostingMeterExtensionTests.cs b/test/OpenTelemetry.Extensions.Hosting.Tests/HostingMeterExtensionTests.cs index f4acf44b4e2..4553ae33926 100644 --- a/test/OpenTelemetry.Extensions.Hosting.Tests/HostingMeterExtensionTests.cs +++ b/test/OpenTelemetry.Extensions.Hosting.Tests/HostingMeterExtensionTests.cs @@ -15,13 +15,14 @@ // using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using OpenTelemetry.Metrics; using Xunit; -using static OpenTelemetry.Extensions.Hosting.Tests.HostingTracerExtensionTests; namespace OpenTelemetry.Extensions.Hosting.Tests { @@ -195,6 +196,49 @@ public void AddOpenTelemetryMetrics_MultipleCallsConfigureSingleProvider() Assert.Single(providers); } + [Fact] + public async Task AddOpenTelemetryMetrics_HostConfigurationHonoredTest() + { + bool configureBuilderCalled = false; + + var builder = new HostBuilder() + .ConfigureAppConfiguration(builder => + { + builder.AddInMemoryCollection(new Dictionary + { + ["TEST_KEY"] = "TEST_KEY_VALUE", + }); + }) + .ConfigureServices(services => + { + services.AddOpenTelemetryMetrics(builder => + { + builder.ConfigureBuilder((sp, builder) => + { + configureBuilderCalled = true; + + var configuration = sp.GetRequiredService(); + + var testKeyValue = configuration.GetValue("TEST_KEY", null); + + Assert.Equal("TEST_KEY_VALUE", testKeyValue); + }); + }); + }); + + var host = builder.Build(); + + Assert.False(configureBuilderCalled); + + await host.StartAsync(); + + Assert.True(configureBuilderCalled); + + await host.StopAsync(); + + host.Dispose(); + } + private static MeterProviderBuilder AddMyFeature(MeterProviderBuilder meterProviderBuilder) { meterProviderBuilder.ConfigureServices(services => services.AddSingleton()); diff --git a/test/OpenTelemetry.Extensions.Hosting.Tests/HostingTracerExtensionTests.cs b/test/OpenTelemetry.Extensions.Hosting.Tests/HostingTracerExtensionTests.cs index 131e9038139..22432e3f4a7 100644 --- a/test/OpenTelemetry.Extensions.Hosting.Tests/HostingTracerExtensionTests.cs +++ b/test/OpenTelemetry.Extensions.Hosting.Tests/HostingTracerExtensionTests.cs @@ -15,9 +15,11 @@ // using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using OpenTelemetry.Trace; @@ -198,6 +200,49 @@ public void AddOpenTelemetryTracing_MultipleCallsConfigureSingleProvider() Assert.Single(providers); } + [Fact] + public async Task AddOpenTelemetryTracing_HostConfigurationHonoredTest() + { + bool configureBuilderCalled = false; + + var builder = new HostBuilder() + .ConfigureAppConfiguration(builder => + { + builder.AddInMemoryCollection(new Dictionary + { + ["TEST_KEY"] = "TEST_KEY_VALUE", + }); + }) + .ConfigureServices(services => + { + services.AddOpenTelemetryTracing(builder => + { + builder.ConfigureBuilder((sp, builder) => + { + configureBuilderCalled = true; + + var configuration = sp.GetRequiredService(); + + var testKeyValue = configuration.GetValue("TEST_KEY", null); + + Assert.Equal("TEST_KEY_VALUE", testKeyValue); + }); + }); + }); + + var host = builder.Build(); + + Assert.False(configureBuilderCalled); + + await host.StartAsync(); + + Assert.True(configureBuilderCalled); + + await host.StopAsync(); + + host.Dispose(); + } + private static TracerProviderBuilder AddMyFeature(TracerProviderBuilder tracerProviderBuilder) { tracerProviderBuilder.ConfigureServices(services => services.AddSingleton()); diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientTests.cs index 6c1979d0d58..68cc737246d 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientTests.cs @@ -326,13 +326,13 @@ private static void VerifyActivityData( if (!isFailure) { - Assert.Equal(Status.Unset, activity.GetStatus()); + Assert.Equal(ActivityStatusCode.Unset, activity.Status); } else { var status = activity.GetStatus(); - Assert.Equal(Status.Error.StatusCode, status.StatusCode); - Assert.NotNull(status.Description); + Assert.Equal(ActivityStatusCode.Error, activity.Status); + Assert.NotNull(activity.StatusDescription); if (recordException) { diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlEventSourceTests.netfx.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlEventSourceTests.netfx.cs index 66c3264cf6c..0db32cfdd4e 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlEventSourceTests.netfx.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlEventSourceTests.netfx.cs @@ -276,13 +276,12 @@ private static void VerifyActivityData( if (!isFailure) { - Assert.Equal(Status.Unset, activity.GetStatus()); + Assert.Equal(ActivityStatusCode.Unset, activity.Status); } else { - var status = activity.GetStatus(); - Assert.Equal(Status.Error.StatusCode, status.StatusCode); - Assert.NotNull(status.Description); + Assert.Equal(ActivityStatusCode.Error, activity.Status); + Assert.NotNull(activity.StatusDescription); } } diff --git a/test/OpenTelemetry.Tests/Metrics/MeterProviderBuilderExtensionsTests.cs b/test/OpenTelemetry.Tests/Metrics/MeterProviderBuilderExtensionsTests.cs index b4c96f2cc7c..7691f22f349 100644 --- a/test/OpenTelemetry.Tests/Metrics/MeterProviderBuilderExtensionsTests.cs +++ b/test/OpenTelemetry.Tests/Metrics/MeterProviderBuilderExtensionsTests.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using OpenTelemetry.Resources; @@ -199,6 +200,60 @@ public void SetAndConfigureResourceTest() Assert.Contains(provider.Resource.Attributes, kvp => kvp.Key == "key2" && (string)kvp.Value == "value2"); } + [Fact] + public void ConfigureBuilderIConfigurationAvailableTest() + { + Environment.SetEnvironmentVariable("TEST_KEY", "TEST_KEY_VALUE"); + + bool configureBuilderCalled = false; + + using var provider = Sdk.CreateMeterProviderBuilder() + .ConfigureBuilder((sp, builder) => + { + var configuration = sp.GetRequiredService(); + + configureBuilderCalled = true; + + var testKeyValue = configuration.GetValue("TEST_KEY", null); + + Assert.Equal("TEST_KEY_VALUE", testKeyValue); + }) + .Build(); + + Assert.True(configureBuilderCalled); + + Environment.SetEnvironmentVariable("TEST_KEY", null); + } + + [Fact] + public void ConfigureBuilderIConfigurationModifiableTest() + { + bool configureBuilderCalled = false; + + using var provider = Sdk.CreateMeterProviderBuilder() + .ConfigureServices(services => + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { ["TEST_KEY_2"] = "TEST_KEY_2_VALUE" }) + .Build(); + + services.AddSingleton(configuration); + }) + .ConfigureBuilder((sp, builder) => + { + var configuration = sp.GetRequiredService(); + + configureBuilderCalled = true; + + var testKey2Value = configuration.GetValue("TEST_KEY_2", null); + + Assert.Equal("TEST_KEY_2_VALUE", testKey2Value); + }) + .Build(); + + Assert.True(configureBuilderCalled); + } + private static void RunBuilderServiceLifecycleTest( MeterProviderBuilder builder, Func buildFunc, diff --git a/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs b/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs index 383e691d358..34cc5a8e3ec 100644 --- a/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs +++ b/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using OpenTelemetry.Resources; @@ -393,6 +394,60 @@ public void AddExporterNamedOptionsTest() Assert.Equal(1, namedOptionsConfigureInvocations); } + [Fact] + public void ConfigureBuilderIConfigurationAvailableTest() + { + Environment.SetEnvironmentVariable("TEST_KEY", "TEST_KEY_VALUE"); + + bool configureBuilderCalled = false; + + using var provider = Sdk.CreateTracerProviderBuilder() + .ConfigureBuilder((sp, builder) => + { + var configuration = sp.GetRequiredService(); + + configureBuilderCalled = true; + + var testKeyValue = configuration.GetValue("TEST_KEY", null); + + Assert.Equal("TEST_KEY_VALUE", testKeyValue); + }) + .Build(); + + Assert.True(configureBuilderCalled); + + Environment.SetEnvironmentVariable("TEST_KEY", null); + } + + [Fact] + public void ConfigureBuilderIConfigurationModifiableTest() + { + bool configureBuilderCalled = false; + + using var provider = Sdk.CreateTracerProviderBuilder() + .ConfigureServices(services => + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { ["TEST_KEY_2"] = "TEST_KEY_2_VALUE" }) + .Build(); + + services.AddSingleton(configuration); + }) + .ConfigureBuilder((sp, builder) => + { + var configuration = sp.GetRequiredService(); + + configureBuilderCalled = true; + + var testKey2Value = configuration.GetValue("TEST_KEY_2", null); + + Assert.Equal("TEST_KEY_2_VALUE", testKey2Value); + }) + .Build(); + + Assert.True(configureBuilderCalled); + } + private static void RunBuilderServiceLifecycleTest( TracerProviderBuilder builder, Func buildFunc,