Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="LaunchDarkly.ServerSdk" Version="8.10.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.19"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
</ItemGroup>
Expand Down
26 changes: 16 additions & 10 deletions sdk/@launchdarkly/observability-dotnet/AspSampleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;

namespace LaunchDarkly.Observability
{
/// <summary>
/// Base builder which allows for methods to be shared between building a config directly and building a plugin.
/// <remarks>
/// This uses the CRTP pattern to allow the individual builder methods to return instances of the derived builder
/// type.
/// </remarks>
/// </summary>
public class BaseBuilder<TBuilder> where TBuilder : BaseBuilder<TBuilder>
{
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()
{
}

/// <summary>
/// Set the OTLP endpoint.
/// <para>
/// For most configurations, the OTLP endpoint will not need to be set.
/// </para>
/// <para>
/// Setting the endpoint to null will reset the builder value to the default.
/// </para>
/// </summary>
/// <param name="otlpEndpoint">The OTLP exporter endpoint URL.</param>
/// <returns>A reference to this builder.</returns>
public TBuilder WithOtlpEndpoint(string otlpEndpoint)
{
_otlpEndpoint = otlpEndpoint ?? DefaultOtlpEndpoint;
return (TBuilder)this;
}

/// <summary>
/// Set the back-end URL for non-telemetry operations.
/// <para>
/// For most configurations, the backend url will not need to be set.
/// </para>
/// <para>
/// Setting the url to null will reset the builder value to the default.
/// </para>
/// </summary>
/// <param name="backendUrl">The back-end URL used for non-telemetry operations.</param>
/// <returns>A reference to this builder.</returns>
public TBuilder WithBackendUrl(string backendUrl)
{
_backendUrl = backendUrl ?? DefaultBackendUrl;
return (TBuilder)this;
}

/// <summary>
/// Set the service name.
/// </summary>
/// <param name="serviceName">The logical service name used in telemetry resource attributes.</param>
/// <returns>A reference to this builder.</returns>
public TBuilder WithServiceName(string serviceName)
{
_serviceName = serviceName ?? string.Empty;
return (TBuilder)this;
}

/// <summary>
/// Set the service version.
/// </summary>
/// <param name="serviceVersion">
/// The version of the service that will be added to resource attributes when a service name is provided.
/// </param>
/// <returns>A reference to this builder.</returns>
public TBuilder WithServiceVersion(string serviceVersion)
{
_serviceVersion = serviceVersion ?? string.Empty;
return (TBuilder)this;
}

/// <summary>
/// Set the environment name.
/// </summary>
/// <param name="environment">The environment name (for example, "prod" or "staging").</param>
/// <returns>A reference to this builder.</returns>
public TBuilder WithEnvironment(string environment)
{
_environment = environment ?? string.Empty;
return (TBuilder)this;
}

/// <summary>
/// Build an immutable <see cref="ObservabilityConfig"/> instance.
/// </summary>
/// <returns>The constructed <see cref="ObservabilityConfig"/>.</returns>
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);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<ItemGroup>
<!-- LaunchDarkly Dependencies -->
<PackageReference Include="LaunchDarkly.ServerSdk" Version="[8.10.0,9.0.0)" />
<PackageReference Include="LaunchDarkly.ServerSdk.Telemetry" Version="[1.2.0,2.0.0)" />
<PackageReference Include="LaunchDarkly.ServerSdk.Telemetry" Version="[1.3.0,2.0.0)" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ public struct ObservabilityConfig
/// The configured OTLP endpoint.
/// </summary>
public string OtlpEndpoint { get; }

/// <summary>
/// The configured back-end URL.
/// <para>
/// This is used for non-telemetry operations such as accessing the sampling configuration.
/// </para>
/// </summary>
public string BackendUrl { get; }

/// <summary>
/// The name of the service.
/// <para>
Expand All @@ -24,20 +25,23 @@ public struct ObservabilityConfig
/// </para>
/// </summary>
public string ServiceName { get; }

/// <summary>
/// The version of the service.
/// </summary>
public string ServiceVersion { get; }

/// <summary>
/// The environment for the service.
/// </summary>
public string Environment { get; }

/// <summary>
/// The LaunchDarkly SDK key.
/// </summary>
public string SdkKey { get; }

private ObservabilityConfig(
internal ObservabilityConfig(
string otlpEndpoint,
string backendUrl,
string serviceName,
Expand All @@ -56,111 +60,21 @@ private ObservabilityConfig(
/// <summary>
/// Create a new builder for <see cref="ObservabilityConfig"/>.
/// </summary>
/// <param name="sdkKey">The LaunchDarkly SDK key used for authentication and resource attributes.</param>
/// <returns>A new <see cref="Builder"/> instance for configuring observability.</returns>
internal static Builder CreateBuilder(string sdkKey) => new Builder(sdkKey);
/// <returns>A new <see cref="ObservabilityConfigBuilder"/> instance for configuring observability.</returns>
internal static ObservabilityConfigBuilder Builder() => new ObservabilityConfigBuilder();

/// <summary>
/// Fluent builder for <see cref="ObservabilityConfig"/>.
/// Builder for building an observability configuration.
/// </summary>
public sealed class Builder
public class ObservabilityConfigBuilder : BaseBuilder<ObservabilityConfigBuilder>
{
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.");
}

/// <summary>
/// Set the OTLP endpoint.
/// <para>
/// For most configurations, the OTLP endpoint will not need to be set.
/// </para>
/// <para>
/// Setting the endpoint to null will reset the builder value to the default.
/// </para>
/// </summary>
/// <param name="otlpEndpoint">The OTLP exporter endpoint URL.</param>
/// <returns>A reference to this builder.</returns>
public Builder WithOtlpEndpoint(string otlpEndpoint)
{
_otlpEndpoint = otlpEndpoint ?? DefaultOtlpEndpoint;
return this;
}

/// <summary>
/// Set the back-end URL for non-telemetry operations.
/// <para>
/// For most configurations, the backend url will not need to be set.
/// </para>
/// <para>
/// Setting the url to null will reset the builder value to the default.
/// </para>
/// </summary>
/// <param name="backendUrl">The back-end URL used for non-telemetry operations.</param>
/// <returns>A reference to this builder.</returns>
public Builder WithBackendUrl(string backendUrl)
{
_backendUrl = backendUrl ?? DefaultBackendUrl;
return this;
}

/// <summary>
/// Set the service name.
/// </summary>
/// <param name="serviceName">The logical service name used in telemetry resource attributes.</param>
/// <returns>A reference to this builder.</returns>
public Builder WithServiceName(string serviceName)
{
_serviceName = serviceName ?? string.Empty;
return this;
}

/// <summary>
/// Set the service version.
/// </summary>
/// <param name="serviceVersion">
/// The version of the service that will be added to resource attributes when a service name is provided.
/// </param>
/// <returns>A reference to this builder.</returns>
public Builder WithServiceVersion(string serviceVersion)
{
_serviceVersion = serviceVersion ?? string.Empty;
return this;
}

/// <summary>
/// Set the environment name.
/// </summary>
/// <param name="environment">The environment name (for example, "prod" or "staging").</param>
/// <returns>A reference to this builder.</returns>
public Builder WithEnvironment(string environment)
internal ObservabilityConfigBuilder()
{
_environment = environment ?? string.Empty;
return this;
}

/// <summary>
/// Build an immutable <see cref="ObservabilityConfig"/> instance.
/// </summary>
/// <returns>The constructed <see cref="ObservabilityConfig"/>.</returns>
public ObservabilityConfig Build()
internal ObservabilityConfig Build(string sdkKey)
{
return new ObservabilityConfig(
_otlpEndpoint,
_backendUrl,
_serviceName,
_environment,
_serviceVersion,
_sdkKey);
return BuildConfig(sdkKey);
}
}
}
Expand Down
Loading
Loading