Skip to content

CallEvent

CallEvent is a readonly struct passed to the Resilience.OnEvent listener. Raising an event is allocation-free, although the Result member boxes value-type results. The Result member is only populated if a listener is attached.

MemberDescription
KindThe type of event that occurred.
PolicyNameThe name of the policy (Resilience.Name). This allows a single listener to monitor multiple policies.
AttemptNumberThe 1-based index of the attempt. For Attempt events, it is the attempt that just finished. For Retrying and pre-attempt events, it is the attempt about to run.
VerdictThe classification of the most recent attempt. Defaults to Ok before any attempt has run.
DurationFor Attempt events, this is the duration of that attempt. For all other event kinds, this is the total time the call has been running.
DelayThe duration of the pause about to be served. This is the backoff delay for Retrying events or the rejection pause for Rejected events. This is null for all other event kinds.
ExceptionThe exception thrown by the most recent attempt, or null if none was thrown.
ResultThe value returned by the most recent attempt, as an object. This is null if the attempt threw an exception or returned nothing.
ReasonThe StopReason indicating why the call stopped. This is populated only for terminal event kinds.
ToString()Returns a formatted summary of the event, omitting absent segments.

CallEventKind

The CallEventKind enum defines the types of events emitted during a call.

KindTerminalDelay PopulatedReason Populated
AttemptNoNoNo
RetryingNoYes (Backoff)No
SucceededYesNoYes (Succeeded)
NotRetriedYesNoYes (Permanent)
ExhaustedYesNoYes (AttemptsExhausted)
RejectedYesYes (Rejection pause)Yes (DependencyUnavailable or BudgetExhausted)
DeadlineExceededYesNoYes (DeadlineExceeded)
OrphanedWorkNoNoNo
BreakerOpenedNoNoNo
BreakerClosedNoNoNo
BreakerHalfOpenedNoNoNo
NestedRetryNoNoNo

Event invariants and behavior

  • Terminal Events: Every call ends with exactly one terminal event. This invariant ensures that logical operations can be counted reliably.
  • Attempt Events: Exactly one Attempt event fires per attempt.
  • Retrying Events: Retrying events fire before the backoff delay is served, allowing listeners to report the expected idle time.
  • Orphaned Work: OrphanedWork fires when an attempt exceeds its time ceiling by more than one second. This event is raised retrospectively the moment the work finally returns.
  • Nested Retries: NestedRetry events are raised exclusively by the HTTP handler.
  • Breaker Transitions: Breaker state transitions are raised on the call that triggered the transition, outside of the breaker's internal lock.

Listener contract

The OnEvent listener is executed synchronously on the executor's thread.

  • Blocking: If a listener blocks, it blocks the entire call.
  • Exceptions: Any exception thrown by a listener is swallowed by the library to prevent telemetry from crashing the application.
  • Multiple Listeners: To use multiple listeners, chain them together: OnEvent = first + second.

Log event IDs

The ILogger records a registered policy writes. An event ID is a contract the moment an alert is built on it, so the numbers below are stable and gated by a test. See Logging in DI for how to filter them.

IDNameDefaultVerboseMessage
1000AttemptSucceededTraceInformation{Policy} attempt {Attempt} succeeded in {ElapsedMs} ms
1001AttemptFailedDebugInformation{Policy} attempt {Attempt} failed in {ElapsedMs} ms: {Verdict} {ErrorType}
1002AttemptLimitedDebugInformation{Policy} attempt {Attempt} was refused by a local limiter before it left the process
1003RetryingDebugInformation{Policy} waiting {DelayMs} ms before attempt {Attempt} after a {Verdict} outcome
1004CallSucceededTraceInformation{Policy} succeeded in {ElapsedMs} ms
1005CallSucceededAfterRetriesDebugInformation{Policy} succeeded on attempt {Attempt} after {ElapsedMs} ms
1006NotRetriedDebugInformation{Policy} stopped after attempt {Attempt}: the outcome was classified Permanent
1007NotRetriedFirstSightingWarningWarning{Policy} did not retry {ErrorType} on attempt {Attempt} because the classifier called it Permanent.
1008ExhaustedDebugInformation{Policy} used all {Attempt} attempts in {ElapsedMs} ms and failed with {ErrorType}
1009DeadlineExceededDebugInformation{Policy} ran out of deadline after {ElapsedMs} ms on attempt {Attempt}
1010RejectedDependencyUnavailableWarningWarning{Policy} refused a call because its circuit breaker is open. Rejections logged quietly since the previous warning: {Suppressed}.
1011RejectedBudgetExhaustedWarningWarning{Policy} refused a retry because the retry budget is exhausted.
1012RejectedRepeatDebugInformation{Policy} refused a call: {Reason}
1013BreakerOpenedWarningWarning{Policy} opened its circuit breaker on attempt {Attempt}.
1014BreakerHalfOpenedInformationInformation{Policy} is probing its dependency: the break duration elapsed and this call is the probe
1015BreakerClosedInformationInformation{Policy} closed its circuit breaker and is taking traffic again
1016OrphanedWorkWarningWarning{Policy} attempt {Attempt} kept running after its timeout, so that work is still going unobserved.
1017OrphanedWorkRepeatDebugInformation{Policy} attempt {Attempt} kept running after its timeout
1018NestedRetryWarningWarning{Policy} is retrying a request that is already inside a retrying client.
1019NestedRetryRepeatTraceInformation{Policy} is retrying inside another retrying client
1020PolicyResolvedDebugInformation{Policy} resolved: {Effective}
1021PolicyClassifierTraceDebug{Policy} classifier: {Rules}

Field names are shared with the metric tag vocabulary wherever both exist (Policy, Verdict, Reason), so a structured record and a metric describe the same call with the same words.

Events 1010, 1011, 1016 and 1018 are rate-limited per policy - see flood control. Events 1007, 1012, 1017 and 1019 are the quiet forms the suppressed occurrences take.

Released under the MIT License.