diff --git a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs index 9076965334d..57d0afca782 100644 --- a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs +++ b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs @@ -95,7 +95,9 @@ public ValueTask IsolateCircuitAsync(ResilienceContext context) lock (_lock) { - SetLastHandledOutcome_NeedsLock(Outcome.FromException(new IsolatedCircuitException())); + var exception = new IsolatedCircuitException(); + _telemetry.UpdateTelemetrySource(exception); + SetLastHandledOutcome_NeedsLock(Outcome.FromException(exception)); OpenCircuitFor_NeedsLock(Outcome.FromResult(default), TimeSpan.MaxValue, manual: true, context, out task); _circuitState = CircuitState.Isolated; } @@ -123,7 +125,7 @@ public ValueTask CloseCircuitAsync(ResilienceContext context) { EnsureNotDisposed(); - Exception? exception = null; + BrokenCircuitException? exception = null; bool isHalfOpen = false; Task? task = null; @@ -157,6 +159,7 @@ public ValueTask CloseCircuitAsync(ResilienceContext context) if (exception is not null) { + _telemetry.UpdateTelemetrySource(exception); return Outcome.FromException(exception); } @@ -308,11 +311,13 @@ private void SetLastHandledOutcome_NeedsLock(Outcome outcome) private BrokenCircuitException CreateBrokenCircuitException() { TimeSpan retryAfter = _blockedUntil - _timeProvider.GetUtcNow(); - return _breakingException switch + BrokenCircuitException exception = _breakingException switch { - Exception exception => new BrokenCircuitException(BrokenCircuitException.DefaultMessage, retryAfter, exception), + Exception ex => new BrokenCircuitException(BrokenCircuitException.DefaultMessage, retryAfter, ex), _ => new BrokenCircuitException(BrokenCircuitException.DefaultMessage, retryAfter) }; + _telemetry.UpdateTelemetrySource(exception); + return exception; } private void OpenCircuit_NeedsLock(Outcome outcome, bool manual, ResilienceContext context, out Task? scheduledTask) diff --git a/src/Polly.Core/ExecutionRejectedException.cs b/src/Polly.Core/ExecutionRejectedException.cs index b5c8e4e0684..3718d30c457 100644 --- a/src/Polly.Core/ExecutionRejectedException.cs +++ b/src/Polly.Core/ExecutionRejectedException.cs @@ -2,6 +2,8 @@ using System.Runtime.Serialization; #endif +using Polly.Telemetry; + namespace Polly; /// @@ -49,4 +51,10 @@ protected ExecutionRejectedException(SerializationInfo info, StreamingContext co } #endif #pragma warning restore RS0016 // Add public types and members to the declared API + + /// + /// Gets the source of the strategy which has thrown the exception, if known. + /// + public virtual ResilienceTelemetrySource? TelemetrySource { get; internal set; } + } diff --git a/src/Polly.Core/PublicAPI.Unshipped.txt b/src/Polly.Core/PublicAPI.Unshipped.txt index 5688e21d398..5101967e54e 100644 --- a/src/Polly.Core/PublicAPI.Unshipped.txt +++ b/src/Polly.Core/PublicAPI.Unshipped.txt @@ -3,4 +3,5 @@ Polly.CircuitBreaker.BrokenCircuitException.BrokenCircuitException(string! messa Polly.CircuitBreaker.BrokenCircuitException.BrokenCircuitException(string! message, System.TimeSpan retryAfter, System.Exception! inner) -> void Polly.CircuitBreaker.BrokenCircuitException.BrokenCircuitException(System.TimeSpan retryAfter) -> void Polly.CircuitBreaker.BrokenCircuitException.RetryAfter.get -> System.TimeSpan? -Polly.Telemetry.ResilienceStrategyTelemetry.AsTelemetrySourceString() -> string! +virtual Polly.ExecutionRejectedException.TelemetrySource.get -> Polly.Telemetry.ResilienceTelemetrySource? +Polly.Telemetry.ResilienceStrategyTelemetry.UpdateTelemetrySource(Polly.ExecutionRejectedException! exception) -> void diff --git a/src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs b/src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs index d2a711027ca..1ec34820450 100644 --- a/src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs +++ b/src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs @@ -1,3 +1,5 @@ +using System.ComponentModel; + namespace Polly.Telemetry; /// @@ -22,15 +24,15 @@ internal ResilienceStrategyTelemetry(ResilienceTelemetrySource source, Telemetry internal ResilienceTelemetrySource TelemetrySource { get; } /// - /// Returns a string representation of the source of the telemetry. + /// Updates the source of the telemetry on the provided exception. /// - /// The string representation of the source of the telemetry. - public string AsTelemetrySourceString() + /// The to-be-updated exception. + [EditorBrowsable(EditorBrowsableState.Never)] + public void UpdateTelemetrySource(ExecutionRejectedException exception) { - var pipelineName = TelemetrySource?.PipelineName ?? "(null)"; - var pipelineInstanceName = TelemetrySource?.PipelineInstanceName ?? "(null)"; - var strategyName = TelemetrySource?.StrategyName ?? "(null)"; - return $"{pipelineName}/{pipelineInstanceName}/{strategyName}"; + Guard.NotNull(exception); + + exception.TelemetrySource = TelemetrySource; } /// diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs index 5b17fe2d720..e9e304b36b3 100644 --- a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs +++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs @@ -74,6 +74,7 @@ protected internal override async ValueTask> ExecuteCore(timeoutException.TrySetStackTrace()); } diff --git a/src/Polly.RateLimiting/PublicAPI.Unshipped.txt b/src/Polly.RateLimiting/PublicAPI.Unshipped.txt index a4827472041..ab058de62d4 100644 --- a/src/Polly.RateLimiting/PublicAPI.Unshipped.txt +++ b/src/Polly.RateLimiting/PublicAPI.Unshipped.txt @@ -1,6 +1 @@ #nullable enable -Polly.RateLimiting.RateLimiterRejectedException.RateLimiterRejectedException(string! message, string! telemetrySource) -> void -Polly.RateLimiting.RateLimiterRejectedException.RateLimiterRejectedException(string! message, string! telemetrySource, System.Exception! inner) -> void -Polly.RateLimiting.RateLimiterRejectedException.RateLimiterRejectedException(string! message, string! telemetrySource, System.TimeSpan retryAfter) -> void -Polly.RateLimiting.RateLimiterRejectedException.RateLimiterRejectedException(string! message, string! telemetrySource, System.TimeSpan retryAfter, System.Exception! inner) -> void -Polly.RateLimiting.RateLimiterRejectedException.TelemetrySource.get -> string? diff --git a/src/Polly.RateLimiting/RateLimiterRejectedException.cs b/src/Polly.RateLimiting/RateLimiterRejectedException.cs index c89bdd005f4..d9416332c26 100644 --- a/src/Polly.RateLimiting/RateLimiterRejectedException.cs +++ b/src/Polly.RateLimiting/RateLimiterRejectedException.cs @@ -38,15 +38,6 @@ public RateLimiterRejectedException(string message) { } - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - /// A string representing the source of the telemetry. - public RateLimiterRejectedException(string message, string telemetrySource) - : base(message) - => TelemetrySource = telemetrySource; - /// /// Initializes a new instance of the class. /// @@ -56,16 +47,6 @@ public RateLimiterRejectedException(string message, TimeSpan retryAfter) : base(message) => RetryAfter = retryAfter; - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - /// A string representing the source of the telemetry. - /// The retry after value. - public RateLimiterRejectedException(string message, string telemetrySource, TimeSpan retryAfter) - : base(message) - => (TelemetrySource, RetryAfter) = (telemetrySource, retryAfter); - /// /// Initializes a new instance of the class. /// @@ -76,16 +57,6 @@ public RateLimiterRejectedException(string message, Exception inner) { } - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - /// A string representing the source of the telemetry. - /// The inner exception. - public RateLimiterRejectedException(string message, string telemetrySource, Exception inner) - : base(message, inner) - => TelemetrySource = telemetrySource; - /// /// Initializes a new instance of the class. /// @@ -96,17 +67,6 @@ public RateLimiterRejectedException(string message, TimeSpan retryAfter, Excepti : base(message, inner) => RetryAfter = retryAfter; - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - /// A string representing the source of the telemetry. - /// The retry after value. - /// The inner exception. - public RateLimiterRejectedException(string message, string telemetrySource, TimeSpan retryAfter, Exception inner) - : base(message, inner) - => (TelemetrySource, RetryAfter) = (telemetrySource, retryAfter); - /// /// Gets the amount of time to wait before retrying again. /// @@ -116,11 +76,6 @@ public RateLimiterRejectedException(string message, string telemetrySource, Time /// public TimeSpan? RetryAfter { get; } - /// - /// Gets the source of the strategy which has thrown the exception, if known. - /// - public string? TelemetrySource { get; } - #pragma warning disable RS0016 // Add public types and members to the declared API #if !NETCOREAPP /// @@ -136,12 +91,6 @@ private RateLimiterRejectedException(SerializationInfo info, StreamingContext co { RetryAfter = TimeSpan.FromSeconds(retryAfter); } - - var telemetrySource = info.GetString(nameof(TelemetrySource)); - if (telemetrySource is not null) - { - TelemetrySource = telemetrySource; - } } /// @@ -149,7 +98,6 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont { Guard.NotNull(info); - info.AddValue(nameof(TelemetrySource), TelemetrySource ?? "(null)/(null)/(null)"); info.AddValue(nameof(RetryAfter), RetryAfter.HasValue ? RetryAfter.Value.TotalSeconds : -1.0); base.GetObjectData(info, context); diff --git a/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs b/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs index f27159a478f..861350558eb 100644 --- a/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs +++ b/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs @@ -65,12 +65,11 @@ protected override async ValueTask> ExecuteCore(exception.TrySetStackTrace()); } diff --git a/test/Polly.Core.Tests/Telemetry/ResilienceStrategyTelemetryTests.cs b/test/Polly.Core.Tests/Telemetry/ResilienceStrategyTelemetryTests.cs index 247ff203ab6..90b58338be1 100644 --- a/test/Polly.Core.Tests/Telemetry/ResilienceStrategyTelemetryTests.cs +++ b/test/Polly.Core.Tests/Telemetry/ResilienceStrategyTelemetryTests.cs @@ -25,27 +25,6 @@ public void Enabled_Ok() new ResilienceStrategyTelemetry(_source, null).Enabled.Should().BeFalse(); } - [Fact] - public void AsTelemetrySourceString_Ok() - { - var source = new ResilienceTelemetrySource("builder", "instance", "strategy_name"); - new ResilienceStrategyTelemetry(source, null).AsTelemetrySourceString().Should().Be("builder/instance/strategy_name"); - } - - [Fact] - public void AsTelemetrySourceString_Null_Ok() - { - ResilienceTelemetrySource? source = null; - new ResilienceStrategyTelemetry(source!, null).AsTelemetrySourceString().Should().Be("(null)/(null)/(null)"); - } - - [Fact] - public void AsTelemetrySourceString_Nulls_Ok() - { - var source = new ResilienceTelemetrySource(null, null, null); - new ResilienceStrategyTelemetry(source, null).AsTelemetrySourceString().Should().Be("(null)/(null)/(null)"); - } - [Fact] public void Report_NoOutcome_OK() { diff --git a/test/Polly.RateLimiting.Tests/RateLimiterRejectedExceptionTests.cs b/test/Polly.RateLimiting.Tests/RateLimiterRejectedExceptionTests.cs index 02af7f8a27e..4239db541ca 100644 --- a/test/Polly.RateLimiting.Tests/RateLimiterRejectedExceptionTests.cs +++ b/test/Polly.RateLimiting.Tests/RateLimiterRejectedExceptionTests.cs @@ -4,7 +4,6 @@ namespace Polly.Core.Tests.Timeout; public class RateLimiterRejectedExceptionTests { - private readonly string _telemetrySource = "MyPipeline/MyPipelineInstance/MyRateLimiterStrategy"; private readonly string _message = "dummy"; private readonly TimeSpan _retryAfter = TimeSpan.FromSeconds(4); @@ -38,16 +37,6 @@ public void Ctor_Message_Ok() exception.TelemetrySource.Should().BeNull(); } - [Fact] - public void Ctor_Message_TelemetrySource_Ok() - { - var exception = new RateLimiterRejectedException(_message, _telemetrySource); - exception.InnerException.Should().BeNull(); - exception.Message.Should().Be(_message); - exception.RetryAfter.Should().BeNull(); - exception.TelemetrySource.Should().Be(_telemetrySource); - } - [Fact] public void Ctor_Message_RetryAfter_Ok() { @@ -58,16 +47,6 @@ public void Ctor_Message_RetryAfter_Ok() exception.TelemetrySource.Should().BeNull(); } - [Fact] - public void Ctor_Message_TelemetrySource_RetryAfter_Ok() - { - var exception = new RateLimiterRejectedException(_message, _telemetrySource, _retryAfter); - exception.InnerException.Should().BeNull(); - exception.Message.Should().Be(_message); - exception.RetryAfter.Should().Be(_retryAfter); - exception.TelemetrySource.Should().Be(_telemetrySource); - } - [Fact] public void Ctor_Message_InnerException_Ok() { @@ -78,16 +57,6 @@ public void Ctor_Message_InnerException_Ok() exception.TelemetrySource.Should().BeNull(); } - [Fact] - public void Ctor_Message_TelemetrySource_InnerException_Ok() - { - var exception = new RateLimiterRejectedException(_message, _telemetrySource, new InvalidOperationException()); - exception.InnerException.Should().BeOfType(); - exception.Message.Should().Be(_message); - exception.RetryAfter.Should().BeNull(); - exception.TelemetrySource.Should().Be(_telemetrySource); - } - [Fact] public void Ctor_Message_RetryAfter_InnerException_Ok() { @@ -98,16 +67,6 @@ public void Ctor_Message_RetryAfter_InnerException_Ok() exception.TelemetrySource.Should().BeNull(); } - [Fact] - public void Ctor_Message_TelemetrySource_RetryAfter_InnerException_Ok() - { - var exception = new RateLimiterRejectedException(_message, _telemetrySource, _retryAfter, new InvalidOperationException()); - exception.InnerException.Should().BeOfType(); - exception.Message.Should().Be(_message); - exception.RetryAfter.Should().Be(_retryAfter); - exception.TelemetrySource.Should().Be(_telemetrySource); - } - #if !NETCOREAPP [Fact] public void BinaryDeserialization_Ok() @@ -118,9 +77,6 @@ public void BinaryDeserialization_Ok() result = SerializeAndDeserializeException(new RateLimiterRejectedException()); result.RetryAfter.Should().BeNull(); - - result = SerializeAndDeserializeException(new RateLimiterRejectedException("message", _telemetrySource)); - result.TelemetrySource.Should().Be(_telemetrySource); } public static T SerializeAndDeserializeException(T exception)