You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TelemetryManager.Dispose shuts down its TracerProvider instances but never disposes them, so the underlying ActivityListener registered on Aspire.Cli.Reported leaks for the rest of the process lifetime.
Repro
// src/Aspire.Cli/Telemetry/TelemetryManager.cs:202publicvoidDispose(){if(!_shuttingDown){_azureMonitorProvider?.Shutdown(0);_profilingProvider?.Shutdown(0);_debugDiagnosticProvider?.Shutdown(0);// Dispose isn't used here because it always flushes telemetry and waits for completion.}}
TracerProvider.Shutdown(timeoutMs) flushes pending spans and stops accepting new ones; it does not unregister the ActivityListener that the provider builder added to Activity.AddActivityListener. Only TracerProvider.Dispose() does that.
Expected vs. actual
Expected: After TelemetryManager.Dispose() returns, the ActivityListener it registered should be unregistered. After Shutdown(0) has run there is nothing to flush, so a subsequent Dispose() on each provider should be fast.
Actual: The listener stays alive process-wide. Every disposed TelemetryManager adds one more stale listener to Aspire.Cli.Reported.
Impact
In production: non-issue. One TelemetryManager per CLI process; the process exits soon after.
In the test process: this is the underlying cause of the microsoft.sample_rate race tracked by [Failing test]: Aspire.Cli.Tests.CliSmokeTests.MainReturnsExpectedExitCode\(args: \[\], expectedExitCode: 1\) #17450. Across many test classes, each disposed TelemetryManager leaves its listener behind. Subsequent parallel Reported activities trigger multiple stale samplers concurrently, both calling ActivityCreationOptions.SamplingTags.Add("microsoft.sample_rate", ...) on the same activity → InvalidOperationException: The collection already contains item with same key 'microsoft.sample_rate'.
PR #17451 (commit e126a1a) worked around this by opting the whole Aspire.Cli.Tests assembly out of Azure Monitor, but the underlying lifecycle bug remains: any future test (or production caller) that opts back in and disposes a TelemetryManager still leaks the listener. This was rediscovered in #17461 when new tests in TelemetryConfigurationTests bypassed the workaround by directly constructing TelemetryManager and resurrected the race (fixed in that PR by routing the new tests through BuildHostAsync).
Summary
TelemetryManager.Disposeshuts down itsTracerProviderinstances but never disposes them, so the underlyingActivityListenerregistered onAspire.Cli.Reportedleaks for the rest of the process lifetime.Repro
TracerProvider.Shutdown(timeoutMs)flushes pending spans and stops accepting new ones; it does not unregister theActivityListenerthat the provider builder added toActivity.AddActivityListener. OnlyTracerProvider.Dispose()does that.Expected vs. actual
Expected: After
TelemetryManager.Dispose()returns, theActivityListenerit registered should be unregistered. AfterShutdown(0)has run there is nothing to flush, so a subsequentDispose()on each provider should be fast.Actual: The listener stays alive process-wide. Every disposed
TelemetryManageradds one more stale listener toAspire.Cli.Reported.Impact
TelemetryManagerper CLI process; the process exits soon after.microsoft.sample_raterace tracked by [Failing test]: Aspire.Cli.Tests.CliSmokeTests.MainReturnsExpectedExitCode\(args: \[\], expectedExitCode: 1\) #17450. Across many test classes, each disposedTelemetryManagerleaves its listener behind. Subsequent parallelReportedactivities trigger multiple stale samplers concurrently, both callingActivityCreationOptions.SamplingTags.Add("microsoft.sample_rate", ...)on the same activity →InvalidOperationException: The collection already contains item with same key 'microsoft.sample_rate'.PR #17451 (commit e126a1a) worked around this by opting the whole
Aspire.Cli.Testsassembly out of Azure Monitor, but the underlying lifecycle bug remains: any future test (or production caller) that opts back in and disposes aTelemetryManagerstill leaks the listener. This was rediscovered in #17461 when new tests inTelemetryConfigurationTestsbypassed the workaround by directly constructingTelemetryManagerand resurrected the race (fixed in that PR by routing the new tests throughBuildHostAsync).Related
sample_raterace this leak surfacesaspire --infoto enumerate the running CLI's install + discovered installs + hives #17461 — surfaced this when new tests bypassed the workaround