Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3603f51
add support for OTEL_SDK_DISABLED environment variable
hannahhaering Oct 3, 2025
947fcb1
added changelog entry
hannahhaering Oct 3, 2025
ba7d7ae
fix environment variable parsing
hannahhaering Oct 3, 2025
6244098
Merge branch 'main' into support-sdk-disabled-envVar
hannahhaering Oct 3, 2025
ae28768
retry workflow
hannahhaering Oct 4, 2025
583b34b
Merge branch 'support-sdk-disabled-envVar' of https://github.com/hann…
hannahhaering Oct 4, 2025
ca695bf
Merge branch 'main' into support-sdk-disabled-envVar
hannahhaering Oct 6, 2025
bbf01d4
parameterize tests for OTEL_SDK_DISABLED
hannahhaering Oct 6, 2025
d35a343
Update src/OpenTelemetry/CHANGELOG.md
hannahhaering Oct 6, 2025
c51cd9e
parameterize tests for OTEL_SDK_DISABLED
hannahhaering Oct 6, 2025
0aa2dc3
Merge branch 'main' into support-sdk-disabled-envVar
hannahhaering Oct 6, 2025
ecacbf2
Add helper and refactor tests for sdk disabled env var
hannahhaering Oct 7, 2025
86b050b
Merge branch 'main' into support-sdk-disabled-envVar
hannahhaering Oct 7, 2025
d35c752
Update test/OpenTelemetry.Tests/Logs/LoggerProviderBuilderBaseTests.cs
hannahhaering Oct 7, 2025
dee73a9
Merge branch 'main' into support-sdk-disabled-envVar
hannahhaering Oct 8, 2025
538ae6c
sdk disabled env var support for hosting
hannahhaering Oct 8, 2025
abc9d43
Merge branch 'support-sdk-disabled-envVar' of https://github.com/hann…
hannahhaering Oct 8, 2025
aa6027c
Merge branch 'main' into support-sdk-disabled-envVar
hannahhaering Oct 8, 2025
14114da
dispose noop providers
hannahhaering Nov 2, 2025
a911411
Merge branch 'main' into support-sdk-disabled-envVar
hannahhaering Nov 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Notes](../../RELEASENOTES.md).

## Unreleased

* Added support for the `OTEL_SDK_DISABLED` environment variable in TracerProvider,
MeterProvider, and LoggerProvider. When `OTEL_SDK_DISABLED=true`,
the SDK returns no-op implementations for all telemetry signals.
([#6568](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6568))

## 1.14.0-rc.1

Released 2025-Oct-21
Expand Down
32 changes: 31 additions & 1 deletion src/OpenTelemetry/Logs/Builder/LoggerProviderBuilderBase.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenTelemetry.Internal;
using static OpenTelemetry.OpenTelemetrySdk;

namespace OpenTelemetry.Logs;

Expand Down Expand Up @@ -39,7 +41,17 @@ internal LoggerProviderBuilderBase(IServiceCollection services)

services
.AddOpenTelemetryLoggerProviderBuilderServices()
.TryAddSingleton<LoggerProvider>(sp => new LoggerProviderSdk(sp, ownsServiceProvider: false));
.TryAddSingleton<LoggerProvider>(sp =>
{
if (IsOtelSdkDisabled(sp.GetRequiredService<IConfiguration>()))
{
var noopLoggerProvider = new NoopLoggerProvider();
noopLoggerProvider.Dispose();
return noopLoggerProvider;
}

return new LoggerProviderSdk(sp, ownsServiceProvider: false);
});

this.innerBuilder = new LoggerProviderServiceCollectionBuilder(services);

Expand Down Expand Up @@ -91,7 +103,25 @@ internal LoggerProvider Build()
bool validateScopes = false;
#endif
var serviceProvider = services.BuildServiceProvider(validateScopes);
var configuration = serviceProvider.GetRequiredService<IConfiguration>();

if (IsOtelSdkDisabled(configuration))
{
serviceProvider.Dispose();
return new NoopLoggerProvider();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 comments:

  1. Would probably be good to add a log here. Don't want support tickets raised for missing telemetry when users have shot themselves in the foot 😄

  2. This won't work for hosting scenarios. See line 80. This code is only invoked for manual/detached bootstrap. Something else will need to be done for hosting style. Ideally we would have a single solution for both but I haven't looked at where that might go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the hint. Now it should work for both scenarios. Could you please check again?

}

return new LoggerProviderSdk(serviceProvider, ownsServiceProvider: true);
}

private static bool IsOtelSdkDisabled(IConfiguration configuration)
{
bool isDisabled = configuration.TryGetBoolValue(OpenTelemetrySdkEventSource.Log, SdkConfigDefinitions.SdkDisableEnvVarName, out bool result) && result;
if (isDisabled)
{
OpenTelemetrySdkEventSource.Log.LoggerProviderSdkEvent($"Disabled because {SdkConfigDefinitions.SdkDisableEnvVarName} is true.");
}

return isDisabled;
}
}
32 changes: 31 additions & 1 deletion src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderBase.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenTelemetry.Internal;
using static OpenTelemetry.OpenTelemetrySdk;

namespace OpenTelemetry.Metrics;

Expand Down Expand Up @@ -39,7 +41,17 @@ internal MeterProviderBuilderBase(IServiceCollection services)

services
.AddOpenTelemetryMeterProviderBuilderServices()
.TryAddSingleton<MeterProvider>(sp => new MeterProviderSdk(sp, ownsServiceProvider: false));
.TryAddSingleton<MeterProvider>(sp =>
{
if (IsOtelSdkDisabled(sp.GetRequiredService<IConfiguration>()))
{
var noopMeterProvider = new NoopMeterProvider();
noopMeterProvider.Dispose();
return noopMeterProvider;
}

return new MeterProviderSdk(sp, ownsServiceProvider: false);
});

this.innerBuilder = new MeterProviderServiceCollectionBuilder(services);

Expand Down Expand Up @@ -108,7 +120,25 @@ protected MeterProvider Build()
bool validateScopes = false;
#endif
var serviceProvider = services.BuildServiceProvider(validateScopes);
var configuration = serviceProvider.GetRequiredService<IConfiguration>();

if (IsOtelSdkDisabled(configuration))
{
serviceProvider.Dispose();
return new NoopMeterProvider();
}

return new MeterProviderSdk(serviceProvider, ownsServiceProvider: true);
}

private static bool IsOtelSdkDisabled(IConfiguration configuration)
{
bool isDisabled = configuration.TryGetBoolValue(OpenTelemetrySdkEventSource.Log, SdkConfigDefinitions.SdkDisableEnvVarName, out bool result) && result;
if (isDisabled)
{
OpenTelemetrySdkEventSource.Log.MeterProviderSdkEvent($"Disabled because {SdkConfigDefinitions.SdkDisableEnvVarName} is true.");
}

return isDisabled;
}
}
9 changes: 9 additions & 0 deletions src/OpenTelemetry/SdkConfigDefinitions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

namespace OpenTelemetry;

internal static class SdkConfigDefinitions
{
public const string SdkDisableEnvVarName = "OTEL_SDK_DISABLED";
}
32 changes: 31 additions & 1 deletion src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenTelemetry.Internal;
using static OpenTelemetry.OpenTelemetrySdk;

namespace OpenTelemetry.Trace;

Expand Down Expand Up @@ -39,7 +41,17 @@ internal TracerProviderBuilderBase(IServiceCollection services)

services
.AddOpenTelemetryTracerProviderBuilderServices()
.TryAddSingleton<TracerProvider>(sp => new TracerProviderSdk(sp, ownsServiceProvider: false));
.TryAddSingleton<TracerProvider>(sp =>
{
if (IsOtelSdkDisabled(sp.GetRequiredService<IConfiguration>()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there may be a problem here, as it appears that service provider is not being disposed of.

Copy link
Contributor Author

@hannahhaering hannahhaering Nov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the TracerProvider instead of the service provider? The IServiceCollection here belongs to the app using OpenTelemetry, so we shouldn't dispose it. But I think I should call Dispose on the NoopTracerProvider to avoid that any Trace can be registered. I am just making this change, please lmk if it is correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rajkumar-rangaraj I’ve taken another look, but I think my comment still applies. In this hosting scenario, the ServiceProvider is owned by the application (ASP.NET Core host) and not by OpenTelemetry. Only in the manual bootstrap case does OpenTelemetry create and own the ServiceProvider. I’m still not sure I understand, could you please clarify what you think still needs to be disposed?

{
var noopTracerProvider = new NoopTracerProvider();
noopTracerProvider.Dispose();
return noopTracerProvider;
}

return new TracerProviderSdk(sp, ownsServiceProvider: false);
});

this.innerBuilder = new TracerProviderServiceCollectionBuilder(services);

Expand Down Expand Up @@ -146,7 +158,25 @@ protected TracerProvider Build()
bool validateScopes = false;
#endif
var serviceProvider = services.BuildServiceProvider(validateScopes);
var configuration = serviceProvider.GetRequiredService<IConfiguration>();

if (IsOtelSdkDisabled(configuration))
{
serviceProvider.Dispose();
return new NoopTracerProvider();
}

return new TracerProviderSdk(serviceProvider, ownsServiceProvider: true);
}

private static bool IsOtelSdkDisabled(IConfiguration configuration)
{
bool isDisabled = configuration.TryGetBoolValue(OpenTelemetrySdkEventSource.Log, SdkConfigDefinitions.SdkDisableEnvVarName, out bool result) && result;
if (isDisabled)
{
OpenTelemetrySdkEventSource.Log.TracerProviderSdkEvent($"Disabled because {SdkConfigDefinitions.SdkDisableEnvVarName} is true.");
}

return isDisabled;
}
}
17 changes: 17 additions & 0 deletions test/OpenTelemetry.Tests/EnvironmentVariableScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

internal sealed class EnvironmentVariableScope : IDisposable
{
private readonly string name;
private readonly string? previous;

public EnvironmentVariableScope(string name, string? value)
{
this.name = name;
this.previous = Environment.GetEnvironmentVariable(name);
Environment.SetEnvironmentVariable(name, value);
}

public void Dispose() => Environment.SetEnvironmentVariable(this.name, this.previous);
}
26 changes: 26 additions & 0 deletions test/OpenTelemetry.Tests/Logs/LoggerProviderBuilderBaseTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using Xunit;
using static OpenTelemetry.OpenTelemetrySdk;

namespace OpenTelemetry.Logs.Tests;

public sealed class LoggerProviderBuilderBaseTests
{
[Theory]
[InlineData("true", typeof(NoopLoggerProvider))]
[InlineData("false", typeof(LoggerProviderSdk))]
[InlineData(null, typeof(LoggerProviderSdk))]
public void LoggerProviderIsExpectedType(string? value, Type expected)
{
using (new EnvironmentVariableScope("OTEL_SDK_DISABLED", value))
{
var builder = new LoggerProviderBuilderBase();

using var provider = builder.Build();

Assert.IsType(expected, provider);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using Xunit;
using static OpenTelemetry.OpenTelemetrySdk;

namespace OpenTelemetry.Metrics.Tests;

public sealed class MeterProviderBuilderBaseTests
{
[Theory]
[InlineData("true", typeof(NoopMeterProvider))]
[InlineData("false", typeof(MeterProviderSdk))]
[InlineData(null, typeof(MeterProviderSdk))]
public void LoggerProviderIsExpectedType(string? value, Type expected)
{
using (new EnvironmentVariableScope("OTEL_SDK_DISABLED", value))
{
var builder = new MeterProviderBuilderBase();

using var provider = builder.Build();

Assert.IsType(expected, provider);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,28 @@
// SPDX-License-Identifier: Apache-2.0

using Xunit;
using static OpenTelemetry.OpenTelemetrySdk;

namespace OpenTelemetry.Trace.Tests;

public class TracerProviderBuilderBaseTests
public sealed class TracerProviderBuilderBaseTests
{
[Theory]
[InlineData("true", typeof(NoopTracerProvider))]
[InlineData("false", typeof(TracerProviderSdk))]
[InlineData(null, typeof(TracerProviderSdk))]
public void TracerProviderIsExpectedType(string? value, Type expected)
{
using (new EnvironmentVariableScope("OTEL_SDK_DISABLED", value))
{
var builder = new TestTracerProviderBuilder();

using var provider = builder.Build();

Assert.IsType(expected, provider);
}
}

[Fact]
public void AddInstrumentationInvokesFactoryTest()
{
Expand Down
Loading