Skip to content

Commit 5f9462d

Browse files
authored
Respect override for PipelineExecuting and PipelineExecuted (#2752)
Resolves #2750.
1 parent 0707c4d commit 5f9462d

File tree

4 files changed

+123
-51
lines changed

4 files changed

+123
-51
lines changed

docs/advanced/telemetry.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,19 @@ services.AddResiliencePipeline("my-strategy", (builder, context) =>
283283
});
284284
```
285285
<!-- endSnippet -->
286+
287+
There are telemetry events which are specific to strategies. Like the above overrides are specific to Retry (and partially to Hedging).
288+
289+
> [!TIP]
290+
> If you want to define *X* severity for the `ExecutionAttempt` event of Retry and *Y* severity for the `ExecutionAttempt` event of Hedging
291+
> then use the `args.Source.StrategyName` information as well inside the switch expression.
292+
293+
There are also pipeline specific telemetry events
294+
295+
- `PipelineExecuting`: by default reported at `Debug` level
296+
- `PipelineExecuted`: by default reported at `Information` level
297+
298+
In general the suggestion is to use `SeverityProvider` to override strategy specific telemetry events' severity.
299+
300+
> [!TIP]
301+
> If you want to suppress the specific event completely then use `ResilienceEventSeverity.None`.

src/Polly.Extensions/Telemetry/Log.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ public static partial void ResilienceEvent(
3535
Exception? exception);
3636

3737
[LoggerMessage(
38-
1,
39-
LogLevel.Debug,
40-
"Resilience pipeline executing. " +
38+
EventId = 1,
39+
Message = "Resilience pipeline executing. " +
4140
SourceWithoutStrategy + Separator +
4241
OperationKey,
4342
EventName = "StrategyExecuting")]
4443
public static partial void PipelineExecuting(
4544
this ILogger logger,
45+
LogLevel logLevel,
4646
string pipelineName,
4747
string pipelineInstance,
4848
string? operationKey);
@@ -74,8 +74,7 @@ public static partial void PipelineExecuted(
7474
"Handled: '{Handled}', " +
7575
"Attempt: '{Attempt}', " +
7676
ExecutionTime,
77-
EventName = "ExecutionAttempt",
78-
SkipEnabledCheck = true)]
77+
EventName = "ExecutionAttempt")]
7978
public static partial void ExecutionAttempt(
8079
this ILogger logger,
8180
LogLevel level,

src/Polly.Extensions/Telemetry/TelemetryListenerImpl.cs

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,9 @@ public override void Write<TResult, TArgs>(in TelemetryEventArguments<TResult, T
5454
}
5555
}
5656

57-
var severity = args.Event.Severity;
58-
59-
if (_severityProvider is { } provider)
60-
{
61-
severity = provider(new SeverityProviderArguments(args.Source, args.Event, args.Context));
62-
}
57+
var severity = _severityProvider is { } provider
58+
? provider(new SeverityProviderArguments(args.Source, args.Event, args.Context))
59+
: args.Event.Severity;
6360

6461
LogEvent(in args, severity);
6562
MeterEvent(in args, severity);
@@ -166,53 +163,56 @@ private void LogEvent<TResult, TArgs>(in TelemetryEventArguments<TResult, TArgs>
166163
{
167164
var result = GetResult(args.Context, args.Outcome);
168165
var level = severity.AsLogLevel();
166+
var pipelineName = args.Source.PipelineName.GetValueOrPlaceholder();
167+
var pipelineInstanceName = args.Source.PipelineInstanceName.GetValueOrPlaceholder();
168+
var strategyName = args.Source.StrategyName.GetValueOrPlaceholder();
169+
var operationKey = args.Context.OperationKey;
170+
var exception = args.Outcome?.Exception;
169171

170172
if (GetArgs<TArgs, PipelineExecutingArguments>(args.Arguments, out _))
171173
{
172174
_logger.PipelineExecuting(
173-
args.Source.PipelineName.GetValueOrPlaceholder(),
174-
args.Source.PipelineInstanceName.GetValueOrPlaceholder(),
175-
args.Context.OperationKey);
175+
level,
176+
pipelineName,
177+
pipelineInstanceName,
178+
operationKey);
176179
}
177180
else if (GetArgs<TArgs, PipelineExecutedArguments>(args.Arguments, out var pipelineExecuted))
178181
{
179182
_logger.PipelineExecuted(
180-
LogLevel.Debug,
181-
args.Source.PipelineName.GetValueOrPlaceholder(),
182-
args.Source.PipelineInstanceName.GetValueOrPlaceholder(),
183-
args.Context.OperationKey,
184-
GetResult(args.Context, args.Outcome),
183+
level,
184+
pipelineName,
185+
pipelineInstanceName,
186+
operationKey,
187+
result,
185188
pipelineExecuted.Duration.TotalMilliseconds,
186-
args.Outcome?.Exception);
189+
exception);
187190
}
188191
else if (GetArgs<TArgs, ExecutionAttemptArguments>(args.Arguments, out var executionAttempt))
189192
{
190-
if (_logger.IsEnabled(level))
191-
{
192-
_logger.ExecutionAttempt(
193-
level,
194-
args.Source.PipelineName.GetValueOrPlaceholder(),
195-
args.Source.PipelineInstanceName.GetValueOrPlaceholder(),
196-
args.Source.StrategyName.GetValueOrPlaceholder(),
197-
args.Context.OperationKey,
198-
result,
199-
executionAttempt.Handled,
200-
executionAttempt.AttemptNumber,
201-
executionAttempt.Duration.TotalMilliseconds,
202-
args.Outcome?.Exception);
203-
}
193+
_logger.ExecutionAttempt(
194+
level,
195+
pipelineName,
196+
pipelineInstanceName,
197+
strategyName,
198+
operationKey,
199+
result,
200+
executionAttempt.Handled,
201+
executionAttempt.AttemptNumber,
202+
executionAttempt.Duration.TotalMilliseconds,
203+
exception);
204204
}
205205
else
206206
{
207207
_logger.ResilienceEvent(
208208
level,
209209
args.Event.EventName,
210-
args.Source.PipelineName.GetValueOrPlaceholder(),
211-
args.Source.PipelineInstanceName.GetValueOrPlaceholder(),
212-
args.Source.StrategyName.GetValueOrPlaceholder(),
213-
args.Context.OperationKey,
210+
pipelineName,
211+
pipelineInstanceName,
212+
strategyName,
213+
operationKey,
214214
result,
215-
args.Outcome?.Exception);
215+
exception);
216216
}
217217
}
218218

test/Polly.Extensions.Tests/Telemetry/TelemetryListenerImplTests.cs

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,8 @@ public void WriteEvent_MeteringWithEnrichers_Ok(int count)
291291

292292
const int DefaultDimensions = 6;
293293

294-
var telemetry = Create(new[]
295-
{
294+
var telemetry = Create(
295+
[
296296
new CallbackEnricher(context =>
297297
{
298298
for (int i = 0; i < count; i++)
@@ -305,7 +305,7 @@ public void WriteEvent_MeteringWithEnrichers_Ok(int count)
305305
{
306306
context.Tags.Add(new KeyValuePair<string, object?>("other", "other-value"));
307307
})
308-
});
308+
]);
309309

310310
ReportEvent(telemetry, Outcome.FromResult<object>(true));
311311

@@ -357,16 +357,17 @@ public void PipelineExecution_Logged(bool exception)
357357
var outcome = exception ? Outcome.FromException<object>(new InvalidOperationException("dummy message")) : Outcome.FromResult((object)10);
358358
var result = exception ? "dummy message" : "10";
359359

360-
ReportEvent(telemetry, outcome: outcome, arg: default(PipelineExecutingArguments), context: context);
361-
ReportEvent(telemetry, outcome: outcome, arg: new PipelineExecutedArguments(TimeSpan.FromSeconds(10)), context: context);
360+
ReportEvent(telemetry, outcome: outcome, arg: default(PipelineExecutingArguments), context: context, severity: ResilienceEventSeverity.Debug);
361+
ReportEvent(telemetry, outcome: outcome, arg: new PipelineExecutedArguments(TimeSpan.FromSeconds(10)), context: context, severity: ResilienceEventSeverity.Information);
362362

363363
var messages = _logger.GetRecords(new EventId(1, "StrategyExecuting")).ToList();
364364
messages.Count.ShouldBe(1);
365365
messages[0].Message.ShouldBe("Resilience pipeline executing. Source: 'my-pipeline/my-instance', Operation Key: 'op-key'");
366+
messages[0].LogLevel.ShouldBe(LogLevel.Debug);
366367
messages = [.. _logger.GetRecords(new EventId(2, "StrategyExecuted"))];
367368
messages.Count.ShouldBe(1);
368369
messages[0].Message.ShouldMatch($"Resilience pipeline executed. Source: 'my-pipeline/my-instance', Operation Key: 'op-key', Result: '{result}', Execution Time: 10000ms");
369-
messages[0].LogLevel.ShouldBe(LogLevel.Debug);
370+
messages[0].LogLevel.ShouldBe(LogLevel.Information);
370371
}
371372

372373
[Fact]
@@ -404,8 +405,8 @@ public void PipelineExecution_Metered(bool exception)
404405
var outcome = exception ? Outcome.FromException<object>(new InvalidOperationException("dummy message")) : Outcome.FromResult((object)10);
405406
var result = exception ? "dummy message" : "10";
406407

407-
var telemetry = Create(new[]
408-
{
408+
var telemetry = Create(
409+
[
409410
new CallbackEnricher(context =>
410411
{
411412
if (exception)
@@ -415,7 +416,7 @@ public void PipelineExecution_Metered(bool exception)
415416

416417
context.Tags.Add(new("custom-tag", "custom-tag-value"));
417418
})
418-
});
419+
]);
419420

420421
ReportEvent(telemetry, outcome: outcome, arg: new PipelineExecutedArguments(TimeSpan.FromSeconds(10)), context: context);
421422

@@ -489,6 +490,59 @@ public void SeverityProvider_EnsureRespected()
489490
called.ShouldBeTrue();
490491
}
491492

493+
[Fact]
494+
public void SeverityProvider_EnsureRespected_For_PipelineEvents()
495+
{
496+
var context = ResilienceContextPool.Shared.Get("op-key", TestCancellation.Token).WithResultType<int>();
497+
var outcome = Outcome.FromException<object>(new InvalidOperationException("dummy message"));
498+
499+
var severity = ResilienceEventSeverity.Critical;
500+
var expectedLogLevel = LogLevel.Critical;
501+
502+
const string PipelineExecuting = nameof(PipelineExecuting);
503+
const string PipelineExecuted = nameof(PipelineExecuted);
504+
505+
var telemetry = Create(configure: options =>
506+
{
507+
options.SeverityProvider = args =>
508+
{
509+
return args.Event.EventName switch
510+
{
511+
PipelineExecuting => severity,
512+
PipelineExecuted => severity,
513+
_ => ResilienceEventSeverity.None
514+
};
515+
};
516+
});
517+
518+
ReportEvent(telemetry, outcome: outcome, arg: default(PipelineExecutingArguments), context: context, eventName: PipelineExecuting);
519+
ReportEvent(telemetry, outcome: outcome, arg: default(PipelineExecutedArguments), context: context, eventName: PipelineExecuted);
520+
521+
_logger.GetRecords(new EventId(1, "StrategyExecuting")).Single().LogLevel.ShouldBe(expectedLogLevel);
522+
_logger.GetRecords(new EventId(2, "StrategyExecuted")).Single().LogLevel.ShouldBe(expectedLogLevel);
523+
}
524+
525+
[InlineData((ResilienceEventSeverity)(-1))]
526+
[InlineData((ResilienceEventSeverity)6)]
527+
[Theory]
528+
public void SeverityProvider_EnsureLogLevelChecked(ResilienceEventSeverity severity)
529+
{
530+
var context = ResilienceContextPool.Shared.Get("op-key", TestCancellation.Token).WithResultType<int>();
531+
var outcome = Outcome.FromException<object>(new InvalidOperationException("dummy message"));
532+
var telemetry = Create(configure: options => options.SeverityProvider = args => severity);
533+
534+
ReportEvent(telemetry, outcome: outcome, arg: default(PipelineExecutingArguments), context: context, eventName: "PipelineExecuting");
535+
ReportEvent(telemetry, outcome: outcome, arg: default(ExecutionAttemptArguments), context: context, eventName: "ExecutionAttempt");
536+
ReportEvent(telemetry, outcome: outcome, arg: default(Retry.OnRetryArguments<object>), context: context, eventName: "OnRetry");
537+
ReportEvent(telemetry, outcome: outcome, arg: default(PipelineExecutedArguments), context: context, eventName: "PipelineExecuted");
538+
539+
var expectedLogLevel = LogLevel.None;
540+
_logger.GetRecords(new EventId(0, "ResilienceEvent")).Single().LogLevel.ShouldBe(expectedLogLevel);
541+
_logger.GetRecords(new EventId(1, "StrategyExecuting")).Single().LogLevel.ShouldBe(expectedLogLevel);
542+
_logger.GetRecords(new EventId(2, "StrategyExecuted")).Single().LogLevel.ShouldBe(expectedLogLevel);
543+
_logger.GetRecords(new EventId(3, "ExecutionAttempt")).Single().LogLevel.ShouldBe(expectedLogLevel);
544+
}
545+
492546
private List<Dictionary<string, object?>> GetEvents(string eventName) => [.. _events.Where(e => e.Name == eventName).Select(v => v.Tags)];
493547

494548
private TelemetryListenerImpl Create(IEnumerable<MeteringEnricher>? enrichers = null, Action<TelemetryOptions>? configure = null)
@@ -522,19 +576,22 @@ private static void ReportEvent(
522576
string? instanceName = "my-instance",
523577
ResilienceContext? context = null,
524578
TestArguments? arg = null,
525-
ResilienceEventSeverity severity = ResilienceEventSeverity.Warning) => ReportEvent<TestArguments>(telemetry, outcome, instanceName, context, arg!, severity);
579+
ResilienceEventSeverity severity = ResilienceEventSeverity.Warning,
580+
string eventName = "my-event")
581+
=> ReportEvent<TestArguments>(telemetry, outcome, instanceName, context, arg!, severity, eventName);
526582

527583
private static void ReportEvent<TArgs>(
528584
TelemetryListenerImpl telemetry,
529585
Outcome<object>? outcome,
530586
string? instanceName = "my-instance",
531587
ResilienceContext? context = null,
532588
TArgs arg = default!,
533-
ResilienceEventSeverity severity = ResilienceEventSeverity.Warning) =>
589+
ResilienceEventSeverity severity = ResilienceEventSeverity.Warning,
590+
string eventName = "my-event") =>
534591
telemetry.Write(
535592
new TelemetryEventArguments<object, TArgs>(
536593
new ResilienceTelemetrySource("my-pipeline", instanceName, "my-strategy"),
537-
new ResilienceEvent(severity, "my-event"),
594+
new ResilienceEvent(severity, eventName),
538595
context ?? ResilienceContextPool.Shared.Get("op-key"),
539596
arg!,
540597
outcome));

0 commit comments

Comments
 (0)