diff --git a/sdk/@launchdarkly/observability-dotnet/AspSampleApp/AspSampleApp.csproj b/sdk/@launchdarkly/observability-dotnet/AspSampleApp/AspSampleApp.csproj
index bd1fbb1ed..85a10af52 100644
--- a/sdk/@launchdarkly/observability-dotnet/AspSampleApp/AspSampleApp.csproj
+++ b/sdk/@launchdarkly/observability-dotnet/AspSampleApp/AspSampleApp.csproj
@@ -7,6 +7,7 @@
+
diff --git a/sdk/@launchdarkly/observability-dotnet/AspSampleApp/Program.cs b/sdk/@launchdarkly/observability-dotnet/AspSampleApp/Program.cs
index de3580e5a..273227309 100644
--- a/sdk/@launchdarkly/observability-dotnet/AspSampleApp/Program.cs
+++ b/sdk/@launchdarkly/observability-dotnet/AspSampleApp/Program.cs
@@ -1,20 +1,24 @@
using LaunchDarkly.Observability;
+using LaunchDarkly.Sdk;
+using LaunchDarkly.Sdk.Server;
+using LaunchDarkly.Sdk.Server.Integrations;
var builder = WebApplication.CreateBuilder(args);
-// Add services to the container.
-// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
-builder.Services.AddLaunchDarklyObservability(
- Environment.GetEnvironmentVariable("LAUNCHDARKLY_SDK_KEY"),
- ldBuilder =>
- {
- ldBuilder.WithServiceName("ryan-test-service");
- }
-);
+var config = Configuration.Builder(Environment.GetEnvironmentVariable("LAUNCHDARKLY_SDK_KEY"))
+ .Plugins(new PluginConfigurationBuilder()
+ .Add(ObservabilityPlugin.Builder(builder.Services)
+ .WithServiceName("ryan-test-service")
+ .WithServiceVersion("0.0.0")
+ .Build())).Build();
+
+// Building the LdClient with the Observability plugin. This line will add services to the web application.
+var client = new LdClient(config);
+// Client must be built before this line.
var app = builder.Build();
// Configure the HTTP request pipeline.
@@ -33,11 +37,13 @@
app.MapGet("/weatherforecast", () =>
{
+ var isMercury =
+ client.BoolVariation("isMercury", Context.New(ContextKind.Of("request"), Guid.NewGuid().ToString()));
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
- Random.Shared.Next(-20, 55),
+ Random.Shared.Next(isMercury ? -170 : -20, isMercury ? 400 : 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
diff --git a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/BaseBuilder.cs b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/BaseBuilder.cs
new file mode 100644
index 000000000..b5f6655ec
--- /dev/null
+++ b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/BaseBuilder.cs
@@ -0,0 +1,116 @@
+using System;
+
+namespace LaunchDarkly.Observability
+{
+ ///
+ /// Base builder which allows for methods to be shared between building a config directly and building a plugin.
+ ///
+ /// This uses the CRTP pattern to allow the individual builder methods to return instances of the derived builder
+ /// type.
+ ///
+ ///
+ public class BaseBuilder where TBuilder : BaseBuilder
+ {
+ private const string DefaultOtlpEndpoint = "https://otel.observability.app.launchdarkly.com:4318";
+ private const string DefaultBackendUrl = "https://pub.observability.app.launchdarkly.com";
+ private string _otlpEndpoint = DefaultOtlpEndpoint;
+ private string _backendUrl = DefaultBackendUrl;
+ private string _serviceName = string.Empty;
+ private string _environment = string.Empty;
+ private string _serviceVersion = string.Empty;
+
+ protected BaseBuilder()
+ {
+ }
+
+ ///
+ /// Set the OTLP endpoint.
+ ///
+ /// For most configurations, the OTLP endpoint will not need to be set.
+ ///
+ ///
+ /// Setting the endpoint to null will reset the builder value to the default.
+ ///
+ ///
+ /// The OTLP exporter endpoint URL.
+ /// A reference to this builder.
+ public TBuilder WithOtlpEndpoint(string otlpEndpoint)
+ {
+ _otlpEndpoint = otlpEndpoint ?? DefaultOtlpEndpoint;
+ return (TBuilder)this;
+ }
+
+ ///
+ /// Set the back-end URL for non-telemetry operations.
+ ///
+ /// For most configurations, the backend url will not need to be set.
+ ///
+ ///
+ /// Setting the url to null will reset the builder value to the default.
+ ///
+ ///
+ /// The back-end URL used for non-telemetry operations.
+ /// A reference to this builder.
+ public TBuilder WithBackendUrl(string backendUrl)
+ {
+ _backendUrl = backendUrl ?? DefaultBackendUrl;
+ return (TBuilder)this;
+ }
+
+ ///
+ /// Set the service name.
+ ///
+ /// The logical service name used in telemetry resource attributes.
+ /// A reference to this builder.
+ public TBuilder WithServiceName(string serviceName)
+ {
+ _serviceName = serviceName ?? string.Empty;
+ return (TBuilder)this;
+ }
+
+ ///
+ /// Set the service version.
+ ///
+ ///
+ /// The version of the service that will be added to resource attributes when a service name is provided.
+ ///
+ /// A reference to this builder.
+ public TBuilder WithServiceVersion(string serviceVersion)
+ {
+ _serviceVersion = serviceVersion ?? string.Empty;
+ return (TBuilder)this;
+ }
+
+ ///
+ /// Set the environment name.
+ ///
+ /// The environment name (for example, "prod" or "staging").
+ /// A reference to this builder.
+ public TBuilder WithEnvironment(string environment)
+ {
+ _environment = environment ?? string.Empty;
+ return (TBuilder)this;
+ }
+
+ ///
+ /// Build an immutable instance.
+ ///
+ /// The constructed .
+ internal ObservabilityConfig BuildConfig(string sdkKey)
+ {
+ if (sdkKey == null)
+ {
+ throw new ArgumentNullException(nameof(sdkKey),
+ "SDK key cannot be null when creating an ObservabilityConfig builder.");
+ }
+
+ return new ObservabilityConfig(
+ _otlpEndpoint,
+ _backendUrl,
+ _serviceName,
+ _environment,
+ _serviceVersion,
+ sdkKey);
+ }
+ }
+}
diff --git a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/Class1.cs b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/Class1.cs
deleted file mode 100644
index bedb65f37..000000000
--- a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/Class1.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace LaunchDarkly.Observability
-{
- public class Class1
- {
-
- }
-}
diff --git a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/LaunchDarkly.Observability.csproj b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/LaunchDarkly.Observability.csproj
index 571a94c90..7a695c14f 100644
--- a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/LaunchDarkly.Observability.csproj
+++ b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/LaunchDarkly.Observability.csproj
@@ -54,7 +54,7 @@
-
+
diff --git a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityConfig.cs b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityConfig.cs
index a031cd3af..81f094e1d 100644
--- a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityConfig.cs
+++ b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityConfig.cs
@@ -8,7 +8,7 @@ public struct ObservabilityConfig
/// The configured OTLP endpoint.
///
public string OtlpEndpoint { get; }
-
+
///
/// The configured back-end URL.
///
@@ -16,6 +16,7 @@ public struct ObservabilityConfig
///
///
public string BackendUrl { get; }
+
///
/// The name of the service.
///
@@ -24,20 +25,23 @@ public struct ObservabilityConfig
///
///
public string ServiceName { get; }
+
///
/// The version of the service.
///
public string ServiceVersion { get; }
+
///
/// The environment for the service.
///
public string Environment { get; }
+
///
/// The LaunchDarkly SDK key.
///
public string SdkKey { get; }
- private ObservabilityConfig(
+ internal ObservabilityConfig(
string otlpEndpoint,
string backendUrl,
string serviceName,
@@ -56,111 +60,21 @@ private ObservabilityConfig(
///
/// Create a new builder for .
///
- /// The LaunchDarkly SDK key used for authentication and resource attributes.
- /// A new instance for configuring observability.
- internal static Builder CreateBuilder(string sdkKey) => new Builder(sdkKey);
+ /// A new instance for configuring observability.
+ internal static ObservabilityConfigBuilder Builder() => new ObservabilityConfigBuilder();
///
- /// Fluent builder for .
+ /// Builder for building an observability configuration.
///
- public sealed class Builder
+ public class ObservabilityConfigBuilder : BaseBuilder
{
- private const string DefaultOtlpEndpoint = "https://otel.observability.app.launchdarkly.com:4318";
- private const string DefaultBackendUrl = "https://pub.observability.app.launchdarkly.com";
- private string _otlpEndpoint = DefaultOtlpEndpoint;
- private string _backendUrl = DefaultBackendUrl;
- private string _serviceName = string.Empty;
- private string _environment = string.Empty;
- private string _serviceVersion = string.Empty;
- private readonly string _sdkKey;
-
- internal Builder(string sdkKey)
- {
- _sdkKey = sdkKey ?? throw new ArgumentNullException(nameof(sdkKey), "SDK key cannot be null when creating an ObservabilityConfig builder.");
- }
-
- ///
- /// Set the OTLP endpoint.
- ///
- /// For most configurations, the OTLP endpoint will not need to be set.
- ///
- ///
- /// Setting the endpoint to null will reset the builder value to the default.
- ///
- ///
- /// The OTLP exporter endpoint URL.
- /// A reference to this builder.
- public Builder WithOtlpEndpoint(string otlpEndpoint)
- {
- _otlpEndpoint = otlpEndpoint ?? DefaultOtlpEndpoint;
- return this;
- }
-
- ///
- /// Set the back-end URL for non-telemetry operations.
- ///
- /// For most configurations, the backend url will not need to be set.
- ///
- ///
- /// Setting the url to null will reset the builder value to the default.
- ///
- ///
- /// The back-end URL used for non-telemetry operations.
- /// A reference to this builder.
- public Builder WithBackendUrl(string backendUrl)
- {
- _backendUrl = backendUrl ?? DefaultBackendUrl;
- return this;
- }
-
- ///
- /// Set the service name.
- ///
- /// The logical service name used in telemetry resource attributes.
- /// A reference to this builder.
- public Builder WithServiceName(string serviceName)
- {
- _serviceName = serviceName ?? string.Empty;
- return this;
- }
-
- ///
- /// Set the service version.
- ///
- ///
- /// The version of the service that will be added to resource attributes when a service name is provided.
- ///
- /// A reference to this builder.
- public Builder WithServiceVersion(string serviceVersion)
- {
- _serviceVersion = serviceVersion ?? string.Empty;
- return this;
- }
-
- ///
- /// Set the environment name.
- ///
- /// The environment name (for example, "prod" or "staging").
- /// A reference to this builder.
- public Builder WithEnvironment(string environment)
+ internal ObservabilityConfigBuilder()
{
- _environment = environment ?? string.Empty;
- return this;
}
- ///
- /// Build an immutable instance.
- ///
- /// The constructed .
- public ObservabilityConfig Build()
+ internal ObservabilityConfig Build(string sdkKey)
{
- return new ObservabilityConfig(
- _otlpEndpoint,
- _backendUrl,
- _serviceName,
- _environment,
- _serviceVersion,
- _sdkKey);
+ return BuildConfig(sdkKey);
}
}
}
diff --git a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityExtensions.cs b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityExtensions.cs
index 156c41f1d..9061a12da 100644
--- a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityExtensions.cs
+++ b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityExtensions.cs
@@ -8,8 +8,8 @@
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
-namespace LaunchDarkly.Observability {
-
+namespace LaunchDarkly.Observability
+{
///
/// Static class containing extension methods for configuring observability
///
@@ -25,38 +25,24 @@ public static class ObservabilityExtensions
private const string TracesPath = "/v1/traces";
private const string LogsPath = "/v1/logs";
private const string MetricsPath = "/v1/metrics";
- private static IEnumerable> GetResourceAttributes(ObservabilityConfig config)
+
+ private static IEnumerable> GetResourceAttributes(ObservabilityConfig config)
{
- var attrs = new List>();
+ var attrs = new List>();
- if (!string.IsNullOrWhiteSpace(config.Environment))
- {
- attrs.Add(new KeyValuePair("deployment.environment.name", config.Environment));
- }
+ if (!string.IsNullOrWhiteSpace(config.Environment))
+ {
+ attrs.Add(new KeyValuePair("deployment.environment.name", config.Environment));
+ }
- attrs.Add(new KeyValuePair("highlight.project_id", config.SdkKey));
+ attrs.Add(new KeyValuePair("highlight.project_id", config.SdkKey));
- return attrs;
+ return attrs;
}
- ///
- /// Add the LaunchDarkly Observability services. This function would typically be called by the LaunchDarkly
- /// Observability plugin. This should only be called by the end user if the Observability plugin needs to be
- /// initialized earlier than the LaunchDarkly client.
- ///
- /// The service collection
- /// The LaunchDarkly SDK
- /// A method to configure the services
- /// The service collection
- public static IServiceCollection AddLaunchDarklyObservability(
- this IServiceCollection services,
- string sdkKey,
- Action configure)
+ internal static void AddLaunchDarklyObservabilityWithConfig(this IServiceCollection services,
+ ObservabilityConfig config)
{
- var builder = ObservabilityConfig.CreateBuilder(sdkKey);
- configure(builder);
-
- var config = builder.Build();
var resourceAttributes = GetResourceAttributes(config);
var resourceBuilder = ResourceBuilder.CreateDefault();
@@ -68,20 +54,13 @@ public static IServiceCollection AddLaunchDarklyObservability(
services.AddOpenTelemetry().WithTracing(tracing =>
{
-
tracing.SetResourceBuilder(resourceBuilder)
.AddHttpClientInstrumentation()
.AddGrpcClientInstrumentation()
.AddWcfInstrumentation()
.AddQuartzInstrumentation()
- .AddAspNetCoreInstrumentation(options =>
- {
- options.RecordException = true;
- })
- .AddSqlClientInstrumentation(options =>
- {
- options.SetDbStatementForText = true;
- })
+ .AddAspNetCoreInstrumentation(options => { options.RecordException = true; })
+ .AddSqlClientInstrumentation(options => { options.SetDbStatementForText = true; })
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri(config.OtlpEndpoint + TracesPath);
@@ -116,6 +95,27 @@ public static IServiceCollection AddLaunchDarklyObservability(
Protocol = ExportProtocol
})));
});
+ }
+
+ ///
+ /// Add the LaunchDarkly Observability services. This function would typically be called by the LaunchDarkly
+ /// Observability plugin. This should only be called by the end user if the Observability plugin needs to be
+ /// initialized earlier than the LaunchDarkly client.
+ ///
+ /// The service collection
+ /// The LaunchDarkly SDK
+ /// A method to configure the services
+ /// The service collection
+ public static IServiceCollection AddLaunchDarklyObservability(
+ this IServiceCollection services,
+ string sdkKey,
+ Action configure)
+ {
+ var builder = ObservabilityConfig.Builder();
+ configure(builder);
+
+ var config = builder.Build(sdkKey);
+ AddLaunchDarklyObservabilityWithConfig(services, config);
return services;
}
}
diff --git a/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityPlugin.cs b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityPlugin.cs
new file mode 100644
index 000000000..cf010bcbd
--- /dev/null
+++ b/sdk/@launchdarkly/observability-dotnet/src/LaunchDarkly.Observability/ObservabilityPlugin.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using LaunchDarkly.Sdk.Integrations.Plugins;
+using LaunchDarkly.Sdk.Server.Hooks;
+using LaunchDarkly.Sdk.Server.Interfaces;
+using LaunchDarkly.Sdk.Server.Plugins;
+using LaunchDarkly.Sdk.Server.Telemetry;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace LaunchDarkly.Observability
+{
+ public class ObservabilityPlugin : Plugin
+ {
+ private readonly ObservabilityPluginBuilder _config;
+ private readonly IServiceCollection _services;
+
+ ///
+ /// Construct a plugin which is intended to be used with already configured observability services.
+ ///
+ /// In a typical configuration, this method will not need to be used.
+ ///
+ ///
+ /// This method only needs to be used when observability related functionality must be intialized before it
+ /// is possible to initialize the LaunchDarkly SDK.
+ ///
+ ///
+ /// an observability plugin instance
+ public static ObservabilityPlugin ForExistingServices() => new ObservabilityPlugin();
+
+ ///
+ /// Create a new builder for .
+ ///
+ /// When using this builder, LaunchDarkly client must be constructed before your application is built.
+ /// For example:
+ ///
+ /// var builder = WebApplication.CreateBuilder(args);
+ ///
+ ///
+ /// var config = Configuration.Builder(Environment.GetEnvironmentVariable("your-sdk-key")
+ /// .Plugins(new PluginConfigurationBuilder()
+ /// .Add(ObservabilityPlugin.Builder(builder.Services)
+ /// .WithServiceName("ryan-test-service")
+ /// .WithServiceVersion("0.0.0")
+ /// .Build())).Build();
+ /// // Building the LdClient with the Observability plugin. This line will add services to the web application.
+ /// var client = new LdClient(config);
+ ///
+ /// // Client must be built before this line.
+ /// var app = builder.Build();
+ ///
+ ///
+ ///
+ /// The service collection for dependency injection.
+ /// A new instance for configuring the observability plugin.
+ public static ObservabilityPluginBuilder Builder(IServiceCollection services) =>
+ new ObservabilityPluginBuilder(services);
+
+ internal ObservabilityPlugin(IServiceCollection services, ObservabilityPluginBuilder config) : base(
+ "LaunchDarkly.Observability")
+ {
+ _config = config;
+ _services = services;
+ }
+
+ internal ObservabilityPlugin() : base("LaunchDarkly.Observability")
+ {
+ _services = null;
+ _config = null;
+ }
+
+ ///
+ public override void Register(ILdClient client, EnvironmentMetadata metadata)
+ {
+ if (_services == null || _config == null) return;
+ var config = _config.BuildConfig(metadata.Credential);
+ _services.AddLaunchDarklyObservabilityWithConfig(config);
+ }
+
+ ///
+ public override IList GetHooks(EnvironmentMetadata metadata)
+ {
+ return new List
+ {
+ TracingHook.Builder().IncludeValue().Build()
+ };
+ }
+
+ ///
+ /// Used to build an instance of the Observability Plugin.
+ ///
+ public sealed class ObservabilityPluginBuilder : BaseBuilder
+ {
+ private readonly IServiceCollection _services;
+
+ internal ObservabilityPluginBuilder(IServiceCollection services) : base()
+ {
+ _services = services ?? throw new ArgumentNullException(nameof(services),
+ "Service collection cannot be null when creating an ObservabilityPlugin builder.");
+ }
+
+ ///
+ /// Build an instance with the configured settings.
+ ///
+ /// The constructed .
+ public ObservabilityPlugin Build()
+ {
+ return new ObservabilityPlugin(_services, this);
+ }
+ }
+ }
+}
diff --git a/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/ObservabilityConfigBuilderTests.cs b/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/ObservabilityConfigBuilderTests.cs
index aa0f61a60..cc80fbc1b 100644
--- a/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/ObservabilityConfigBuilderTests.cs
+++ b/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/ObservabilityConfigBuilderTests.cs
@@ -1,6 +1,5 @@
using LaunchDarkly.Observability;
using NUnit.Framework;
-using System;
namespace LaunchDarkly.Observability.Test
{
@@ -10,13 +9,13 @@ public class ObservabilityConfigBuilderTests
[Test]
public void Build_WithAllFields_SetsValues()
{
- var config = ObservabilityConfig.CreateBuilder("sdk-123")
+ var config = ObservabilityConfig.Builder()
.WithOtlpEndpoint("https://otlp.example.com")
.WithBackendUrl("https://backend.example.com")
.WithServiceName("service-a")
.WithServiceVersion("1.0.0")
.WithEnvironment("prod")
- .Build();
+ .Build("sdk-123");
Assert.Multiple(() =>
{
@@ -32,7 +31,7 @@ public void Build_WithAllFields_SetsValues()
[Test]
public void Build_WithoutSettingFields_UsesDefaults()
{
- var config = ObservabilityConfig.CreateBuilder("sdk-xyz").Build();
+ var config = ObservabilityConfig.Builder().Build("sdk-xyz");
Assert.Multiple(() =>
{
@@ -48,13 +47,13 @@ public void Build_WithoutSettingFields_UsesDefaults()
[Test]
public void WithMethods_HandleNullValues_ResetsToDefaults()
{
- var config = ObservabilityConfig.CreateBuilder("sdk-null")
+ var config = ObservabilityConfig.Builder()
.WithOtlpEndpoint(null)
.WithBackendUrl(null)
.WithServiceName(null)
.WithServiceVersion(null)
.WithEnvironment(null)
- .Build();
+ .Build("my-sdk-key");
Assert.Multiple(() =>
{
@@ -63,21 +62,21 @@ public void WithMethods_HandleNullValues_ResetsToDefaults()
Assert.That(config.ServiceName, Is.EqualTo(string.Empty));
Assert.That(config.ServiceVersion, Is.EqualTo(string.Empty));
Assert.That(config.Environment, Is.EqualTo(string.Empty));
- Assert.That(config.SdkKey, Is.EqualTo("sdk-null"));
+ Assert.That(config.SdkKey, Is.EqualTo("my-sdk-key"));
});
}
[Test]
public void Build_ProducesImmutableConfig()
{
- var builder = ObservabilityConfig.CreateBuilder("sdk-immutable")
+ var builder = ObservabilityConfig.Builder()
.WithOtlpEndpoint("e1")
.WithBackendUrl("b1")
.WithServiceName("s1")
.WithServiceVersion("v1")
.WithEnvironment("env1");
- var first = builder.Build();
+ var first = builder.Build("my-sdk-key");
// Change builder afterward
builder
@@ -95,16 +94,8 @@ public void Build_ProducesImmutableConfig()
Assert.That(first.ServiceName, Is.EqualTo("s1"));
Assert.That(first.ServiceVersion, Is.EqualTo("v1"));
Assert.That(first.Environment, Is.EqualTo("env1"));
- Assert.That(first.SdkKey, Is.EqualTo("sdk-immutable"));
+ Assert.That(first.SdkKey, Is.EqualTo("my-sdk-key"));
});
}
-
- [Test]
- public void CreateBuilder_WithNullSdkKey_ThrowsArgumentNullException()
- {
- var exception = Assert.Throws(() => ObservabilityConfig.CreateBuilder(null));
- Assert.That(exception.ParamName, Is.EqualTo("sdkKey"));
- Assert.That(exception.Message, Does.Contain("SDK key cannot be null when creating an ObservabilityConfig builder"));
- }
}
}
diff --git a/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/ObservabilityPluginBuilderTests.cs b/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/ObservabilityPluginBuilderTests.cs
new file mode 100644
index 000000000..3623679ea
--- /dev/null
+++ b/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/ObservabilityPluginBuilderTests.cs
@@ -0,0 +1,69 @@
+using NUnit.Framework;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace LaunchDarkly.Observability.Test
+{
+ [TestFixture]
+ public class ObservabilityPluginBuilderTests
+ {
+ private IServiceCollection _services;
+
+ [SetUp]
+ public void SetUp()
+ {
+ _services = new ServiceCollection();
+ }
+
+ [Test]
+ public void CreateBuilder_WithValidParameters_CreatesBuilder()
+ {
+ var builder = ObservabilityPlugin.Builder(_services);
+
+ Assert.That(builder, Is.Not.Null);
+ Assert.That(builder, Is.InstanceOf());
+ }
+
+ [Test]
+ public void CreateBuilder_WithNullServices_ThrowsArgumentNullException()
+ {
+ var exception = Assert.Throws(() =>
+ ObservabilityPlugin.Builder(null));
+ Assert.Multiple(() =>
+ {
+ Assert.That(exception, Is.Not.Null);
+ Assert.That(exception.ParamName, Is.EqualTo("services"));
+ Assert.That(exception.Message,
+ Does.Contain("Service collection cannot be null when creating an ObservabilityPlugin builder"));
+ });
+ }
+
+ [Test]
+ public void Build_WithAllFields_CreatesPluginWithConfiguration()
+ {
+ var plugin = ObservabilityPlugin.Builder(_services)
+ .WithOtlpEndpoint("https://otlp.example.com")
+ .WithBackendUrl("https://backend.example.com")
+ .WithServiceName("service-a")
+ .WithServiceVersion("1.0.0")
+ .WithEnvironment("prod")
+ .Build();
+
+ Assert.That(plugin, Is.InstanceOf());
+ }
+
+ [Test]
+ public void Build_WithNullValues_HandlesNullsCorrectly()
+ {
+ var plugin = ObservabilityPlugin.Builder(_services)
+ .WithOtlpEndpoint(null)
+ .WithBackendUrl(null)
+ .WithServiceName(null)
+ .WithServiceVersion(null)
+ .WithEnvironment(null)
+ .Build();
+
+ Assert.That(plugin, Is.InstanceOf());
+ }
+ }
+}
diff --git a/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/UnitTest1.cs b/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/UnitTest1.cs
deleted file mode 100644
index 457ea3606..000000000
--- a/sdk/@launchdarkly/observability-dotnet/test/LaunchDarkly.Observability.Tests/UnitTest1.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using NUnit.Framework;
-
-namespace LaunchDarkly.Observability.Test
-{
- public class Tests
- {
- [SetUp]
- public void Setup()
- {
- }
-
- [Test]
- public void Test1()
- {
- Assert.Pass();
- }
- }
-}
\ No newline at end of file