Skip to content

Commit 49f7e2d

Browse files
authored
Introduce ResilienceEventSeverity (#1361)
1 parent 190f096 commit 49f7e2d

31 files changed

+292
-162
lines changed

bench/Polly.Core.Benchmarks/TelemetryBenchmark.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ protected override ValueTask<Outcome<TResult>> ExecuteCoreAsync<TResult, TState>
7878
ResilienceContext context,
7979
TState state)
8080
{
81-
_telemetry.Report("DummyEvent", context, "dummy-args");
81+
_telemetry.Report(new ResilienceEvent(ResilienceEventSeverity.Warning, "DummyEvent"), context, "dummy-args");
8282
return callback(context, state);
8383
}
8484
}

src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public ValueTask CloseCircuitAsync(ResilienceContext context)
128128
if (_circuitState == CircuitState.Open && PermitHalfOpenCircuitTest_NeedsLock())
129129
{
130130
_circuitState = CircuitState.HalfOpen;
131-
_telemetry.Report(CircuitBreakerConstants.OnHalfOpenEvent, context, new OnCircuitHalfOpenedArguments());
131+
_telemetry.Report(new(ResilienceEventSeverity.Warning, CircuitBreakerConstants.OnHalfOpenEvent), context, new OnCircuitHalfOpenedArguments());
132132
isHalfOpen = true;
133133
}
134134

@@ -269,7 +269,7 @@ private void CloseCircuit_NeedsLock(Outcome<T> outcome, bool manual, ResilienceC
269269
if (priorState != CircuitState.Closed)
270270
{
271271
var args = new OutcomeArguments<T, OnCircuitClosedArguments>(context, outcome, new OnCircuitClosedArguments(manual));
272-
_telemetry.Report(CircuitBreakerConstants.OnCircuitClosed, args);
272+
_telemetry.Report(new(ResilienceEventSeverity.Information, CircuitBreakerConstants.OnCircuitClosed), args);
273273

274274
if (_onClosed is not null)
275275
{
@@ -320,7 +320,7 @@ private void OpenCircuitFor_NeedsLock(Outcome<T> outcome, TimeSpan breakDuration
320320
_circuitState = CircuitState.Open;
321321

322322
var args = new OutcomeArguments<T, OnCircuitOpenedArguments>(context, outcome, new OnCircuitOpenedArguments(breakDuration, manual));
323-
_telemetry.Report(CircuitBreakerConstants.OnCircuitOpened, args);
323+
_telemetry.Report(new(ResilienceEventSeverity.Error, CircuitBreakerConstants.OnCircuitOpened), args);
324324

325325
if (_onOpened is not null)
326326
{

src/Polly.Core/Fallback/FallbackResilienceStrategy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ protected override async ValueTask<Outcome<T>> ExecuteCallbackAsync<TState>(Func
2929

3030
var onFallbackArgs = new OutcomeArguments<T, OnFallbackArguments>(context, outcome, new OnFallbackArguments());
3131

32-
_telemetry.Report(FallbackConstants.OnFallback, onFallbackArgs);
32+
_telemetry.Report(new(ResilienceEventSeverity.Warning, FallbackConstants.OnFallback), onFallbackArgs);
3333

3434
if (_onFallback is not null)
3535
{

src/Polly.Core/Hedging/HedgingResilienceStrategy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ private async ValueTask HandleOnHedgingAsync(ResilienceContext context, Outcome<
126126
outcome,
127127
args);
128128

129-
_telemetry.Report(HedgingConstants.OnHedgingEventName, onHedgingArgs);
129+
_telemetry.Report(new(ResilienceEventSeverity.Warning, HedgingConstants.OnHedgingEventName), onHedgingArgs);
130130

131131
if (OnHedging is not null)
132132
{

src/Polly.Core/ResilienceContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private ResilienceContext()
6464
/// <remarks>
6565
/// If the number of resilience events is greater than zero it's an indication that the execution was unhealthy.
6666
/// </remarks>
67-
public IReadOnlyCollection<ResilienceEvent> ResilienceEvents => _resilienceEvents;
67+
public IReadOnlyList<ResilienceEvent> ResilienceEvents => _resilienceEvents;
6868

6969
/// <summary>
7070
/// Gets a <see cref="ResilienceContext"/> instance from the pool.

src/Polly.Core/Retry/RetryResilienceStrategy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ protected override async ValueTask<Outcome<T>> ExecuteCallbackAsync<TState>(Func
7474
}
7575

7676
var onRetryArgs = new OutcomeArguments<T, OnRetryArguments>(context, outcome, new OnRetryArguments(attempt, delay, executionTime));
77-
_telemetry.Report(RetryConstants.OnRetryEvent, onRetryArgs);
77+
_telemetry.Report(new(ResilienceEventSeverity.Warning, RetryConstants.OnRetryEvent), onRetryArgs);
7878

7979
if (OnRetry is not null)
8080
{

src/Polly.Core/Telemetry/ResilienceEvent.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ namespace Polly.Telemetry;
33
/// <summary>
44
/// Represents a resilience event that has been reported.
55
/// </summary>
6+
/// <param name="Severity">The severity of the event.</param>
67
/// <param name="EventName">The event name.</param>
78
/// <remarks>
89
/// Always use the constructor when creating this struct, otherwise we do not guarantee binary compatibility.
910
/// </remarks>
10-
public readonly record struct ResilienceEvent(string EventName)
11+
public readonly record struct ResilienceEvent(ResilienceEventSeverity Severity, string EventName)
1112
{
1213
/// <summary>
1314
/// Returns an <see cref="EventName"/>.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
namespace Polly.Telemetry;
2+
3+
/// <summary>
4+
/// The severity of reported resilience event.
5+
/// </summary>
6+
public enum ResilienceEventSeverity
7+
{
8+
/// <summary>
9+
/// The resilience event is not recorded.
10+
/// </summary>
11+
None = 0,
12+
13+
/// <summary>
14+
/// The resilience event is used for debugging purposes only.
15+
/// </summary>
16+
Debug,
17+
18+
/// <summary>
19+
/// The resilience event is informational.
20+
/// </summary>
21+
Information,
22+
23+
/// <summary>
24+
/// The resilience event should be treated as a warning.
25+
/// </summary>
26+
Warning,
27+
28+
/// <summary>
29+
/// The resilience event should be treated as an error.
30+
/// </summary>
31+
Error,
32+
33+
/// <summary>
34+
/// The resilience event should be treated as a critical error.
35+
/// </summary>
36+
Critical,
37+
}

src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,24 @@ internal ResilienceStrategyTelemetry(ResilienceTelemetrySource source, Diagnosti
2727
/// Reports an event that occurred in a resilience strategy.
2828
/// </summary>
2929
/// <typeparam name="TArgs">The arguments associated with this event.</typeparam>
30-
/// <param name="eventName">The event name.</param>
30+
/// <param name="resilienceEvent">The reported resilience event.</param>
3131
/// <param name="context">The resilience context associated with this event.</param>
3232
/// <param name="args">The event arguments.</param>
33-
/// <exception cref="ArgumentNullException">Thrown when <paramref name="eventName"/> is <see langword="null"/>.</exception>
34-
public void Report<TArgs>(string eventName, ResilienceContext context, TArgs args)
33+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="context"/> is <see langword="null"/>.</exception>
34+
public void Report<TArgs>(ResilienceEvent resilienceEvent, ResilienceContext context, TArgs args)
3535
{
36-
Guard.NotNull(eventName);
3736
Guard.NotNull(context);
3837

39-
AddResilienceEvent(eventName, context, args);
38+
context.AddResilienceEvent(resilienceEvent);
4039

41-
if (DiagnosticSource is null || !DiagnosticSource.IsEnabled(eventName))
40+
if (DiagnosticSource is null || !DiagnosticSource.IsEnabled(resilienceEvent.EventName) || resilienceEvent.Severity == ResilienceEventSeverity.None)
4241
{
4342
return;
4443
}
4544

46-
var telemetryArgs = TelemetryEventArguments.Get(TelemetrySource, eventName, context, null, args!);
45+
var telemetryArgs = TelemetryEventArguments.Get(TelemetrySource, resilienceEvent, context, null, args!);
4746

48-
DiagnosticSource.Write(eventName, telemetryArgs);
47+
DiagnosticSource.Write(resilienceEvent.EventName, telemetryArgs);
4948

5049
TelemetryEventArguments.Return(telemetryArgs);
5150
}
@@ -55,37 +54,22 @@ public void Report<TArgs>(string eventName, ResilienceContext context, TArgs arg
5554
/// </summary>
5655
/// <typeparam name="TArgs">The arguments associated with this event.</typeparam>
5756
/// <typeparam name="TResult">The type of the result.</typeparam>
58-
/// <param name="eventName">The event name.</param>
57+
/// <param name="resilienceEvent">The reported resilience event.</param>
5958
/// <param name="args">The event arguments.</param>
60-
/// <exception cref="ArgumentNullException">Thrown when <paramref name="eventName"/> is <see langword="null"/>.</exception>
61-
public void Report<TArgs, TResult>(string eventName, OutcomeArguments<TResult, TArgs> args)
59+
public void Report<TArgs, TResult>(ResilienceEvent resilienceEvent, OutcomeArguments<TResult, TArgs> args)
6260
{
63-
Guard.NotNull(eventName);
61+
args.Context.AddResilienceEvent(resilienceEvent);
6462

65-
AddResilienceEvent(eventName, args.Context, args.Arguments);
66-
67-
if (DiagnosticSource is null || !DiagnosticSource.IsEnabled(eventName))
63+
if (DiagnosticSource is null || !DiagnosticSource.IsEnabled(resilienceEvent.EventName) || resilienceEvent.Severity == ResilienceEventSeverity.None)
6864
{
6965
return;
7066
}
7167

72-
var telemetryArgs = TelemetryEventArguments.Get(TelemetrySource, eventName, args.Context, args.Outcome.AsOutcome(), args.Arguments!);
68+
var telemetryArgs = TelemetryEventArguments.Get(TelemetrySource, resilienceEvent, args.Context, args.Outcome.AsOutcome(), args.Arguments!);
7369

74-
DiagnosticSource.Write(eventName, telemetryArgs);
70+
DiagnosticSource.Write(resilienceEvent.EventName, telemetryArgs);
7571

7672
TelemetryEventArguments.Return(telemetryArgs);
7773
}
78-
79-
private static void AddResilienceEvent<TArgs>(string eventName, ResilienceContext context, TArgs args)
80-
{
81-
// ExecutionAttemptArguments is not reported as resilience event because that information is already contained
82-
// in OnHedgingArguments and OnRetryArguments
83-
if (args is ExecutionAttemptArguments attempt)
84-
{
85-
return;
86-
}
87-
88-
context.AddResilienceEvent(new ResilienceEvent(eventName));
89-
}
9074
}
9175

src/Polly.Core/Telemetry/TelemetryEventArguments.Pool.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@ public sealed partial record class TelemetryEventArguments
55
private static readonly ObjectPool<TelemetryEventArguments> Pool = new(() => new TelemetryEventArguments(), args =>
66
{
77
args.Source = null!;
8-
args.EventName = null!;
8+
args.Event = default;
99
args.Context = null!;
1010
args.Outcome = default;
1111
args.Arguments = null!;
1212
});
1313

1414
internal static TelemetryEventArguments Get(
1515
ResilienceTelemetrySource source,
16-
string eventName,
16+
ResilienceEvent resilienceEvent,
1717
ResilienceContext context,
1818
Outcome<object>? outcome,
1919
object arguments)
2020
{
2121
var args = Pool.Get();
2222

2323
args.Source = source;
24-
args.EventName = eventName;
24+
args.Event = resilienceEvent;
2525
args.Context = context;
2626
args.Outcome = outcome;
2727
args.Arguments = arguments;

0 commit comments

Comments
 (0)