Skip to content

Telemetry

Strategies report what they do as events: OnRetry, OnCircuitOpened, OnTimeout… Every event from every pipeline in the process goes to one stream. The OpenTelemetry, Prometheus, stats and Hystrix integrations are consumers of that stream; you can write your own in a few lines.

import {
function onTelemetry(listener: (event: TelemetryEvent) => void): () => void

Subscribe to every strategy event in the process. Works in every runtime (diagnostics_channel is Node-only). Returns an unsubscribe function.

onTelemetry
} from 'resilience-pipeline';
const
const unsubscribe: () => void
unsubscribe
=
function onTelemetry(listener: (event: TelemetryEvent) => void): () => void

Subscribe to every strategy event in the process. Works in every runtime (diagnostics_channel is Node-only). Returns an unsubscribe function.

onTelemetry
(({
event: string
event
,
source: TelemetrySource
source
,
context: ResilienceContext
context
,
args: unknown
args
}) => {
if (
event: string
event
=== 'OnCircuitOpened') {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.warn(message?: any, ...optionalParams: any[]): void (+1 overload)

The console.warn() function is an alias for

error

.

@since ― v0.1.100

warn
(`${
source: TelemetrySource
source
.
TelemetrySource.pipelineName: string | undefined
pipelineName
}/${
source: TelemetrySource
source
.
TelemetrySource.strategyName: string | undefined
strategyName
} opened`,
context: ResilienceContext
context
.
ResilienceContext.operationKey: string | undefined
operationKey
,
args: unknown
args
);
}
});

Each TelemetryEvent has:

Field Content
event Event name, Polly-style: OnRetry, OnTimeout, Chaos.OnFault…
source { pipelineName, strategyName, tags? }
context The execution’s ResilienceContext
args Event-specific payload, the same object the matching on* callback receives

onTelemetry works in every runtime. On runtimes with node:diagnostics_channel (Node, Bun, Deno), the same events are also published on the resilience-pipeline channel (TELEMETRY_CHANNEL):

import {
function subscribe(name: string | symbol, onMessage: ChannelListener): void

Register a message handler to subscribe to this channel. This message handler will be run synchronously whenever a message is published to the channel. Any errors thrown in the message handler will trigger an 'uncaughtException'.

import diagnostics_channel from 'node:diagnostics_channel';
diagnostics_channel.subscribe('my-channel', (message, name) => {
// Received data
});

@since ― v18.7.0, v16.17.0

@param ― name The channel name

@param ― onMessage The handler to receive channel messages

subscribe
} from 'node:diagnostics_channel';
import {
const TELEMETRY_CHANNEL: "resilience-pipeline"
TELEMETRY_CHANNEL
, type
(alias) interface TelemetryEvent
import TelemetryEvent

Published on the resilience-pipeline diagnostics channel and to onTelemetry listeners.

TelemetryEvent
} from 'resilience-pipeline';
function subscribe(name: string | symbol, onMessage: ChannelListener): void

Register a message handler to subscribe to this channel. This message handler will be run synchronously whenever a message is published to the channel. Any errors thrown in the message handler will trigger an 'uncaughtException'.

import diagnostics_channel from 'node:diagnostics_channel';
diagnostics_channel.subscribe('my-channel', (message, name) => {
// Received data
});

@since ― v18.7.0, v16.17.0

@param ― name The channel name

@param ― onMessage The handler to receive channel messages

subscribe
(
const TELEMETRY_CHANNEL: "resilience-pipeline"
TELEMETRY_CHANNEL
, (
message: unknown
message
) => {
const {
const event: string
event
,
const source: TelemetrySource
source
} =
message: unknown
message
as
(alias) interface TelemetryEvent
import TelemetryEvent

Published on the resilience-pipeline diagnostics channel and to onTelemetry listeners.

TelemetryEvent
;
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@since ― v0.1.100

log
(
const source: TelemetrySource
source
.
TelemetrySource.pipelineName: string | undefined
pipelineName
,
const source: TelemetrySource
source
.
TelemetrySource.strategyName: string | undefined
strategyName
,
const event: string
event
);
});

Subscribers on the channel see events from every copy of the package in the process, while onTelemetry only sees events from the copy it was imported from. In tests, await nextTelemetry((e) => e.event === 'OnCircuitOpened') resolves with the next matching event.

Four events fire on every execution, whatever strategies it has. Metrics adapters turn them into latency histograms and error rates.

Event When args
OnPipelineExecuting A call starts, before any strategy { context }
OnExecutionAttempt Each call of your callback (first attempt, retries, hedges) { outcome, context, attemptNumber, duration }
OnStrategyExecuted Each strategy finishes, source.strategyName set { outcome, duration, context }
OnPipelineExecuted The call ends { outcome, duration }

With no listener and no channel subscriber, nothing is published and per-strategy timing isn’t measured. An onTelemetry listener that throws is ignored: it never fails or retries the call it observes.

Strategy Events
Retry OnRetry, OnRetryExhausted, OnRetryBudgetExhausted
Timeout OnTimeout
Circuit breaker OnCircuitOpened, OnCircuitClosed, OnCircuitHalfOpened, OnCircuitRejected, OnHalfOpenProbeTimedOut
Breaker extensions OnHealthCheckFailed, OnCircuitStateSynced, OnCircuitStoreError
Fallback OnFallback
Hedging OnHedging
Limiters OnRateLimiterRejected, OnRateLimiterInsuranceUsed, OnLimitChanged (adaptive)
Cache OnCacheHit, OnCacheMiss, OnCoalesced, OnStaleServed, OnCacheStoreFailed
Lock OnLockRejected, OnLockLost
Chaos Chaos.OnFault, Chaos.OnOutcome, Chaos.OnLatency, Chaos.OnBehavior, Chaos.OnGlobal

Payloads are listed in the events reference.

onRetry, onOpened and the other on* options belong to one strategy instance: use them for behaviour (persist breaker state, refresh a token). Events are global and read-only: use them for logs, metrics and traces.

instrument(): wrap executions and attempts

Section titled “instrument(): wrap executions and attempts”

Events report what happened. instrument() wraps the work itself, which tracing needs: a span must be active while your callback runs so your own spans nest under it.

const
const remove: () => void
remove
=
function instrument(instrumentation: Instrumentation): () => void

Register instrumentation for every pipeline in the process. Returns a function that removes it.

instrument
({
Instrumentation.execution?<R>(info: ExecutionInfo, run: () => Promise<R>): Promise<R>

Around a whole pipeline execution, retries included.

execution
: async (
info: ExecutionInfo
info
,
run: () => Promise<R>
run
) => {
const
const started: number
started
=
var performance: Performance

MDN Reference

performance is a global reference for import { performance } from 'node:perf_hooks'

performance
.
Performance.now(): DOMHighResTimeStamp

The performance.now() method returns a high resolution timestamp in milliseconds.

MDN Reference

now
();
try {
return await
run: () => Promise<R>
run
();
} finally {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@since ― v0.1.100

log
(
info: ExecutionInfo
info
.
ExecutionInfo.context: ResilienceContext
context
.
ResilienceContext.operationName?: string | undefined

Operation name for telemetry (spans, metrics); defaults to the pipeline name.

operationName
??
info: ExecutionInfo
info
.
ExecutionInfo.source: TelemetrySource
source
.
TelemetrySource.pipelineName: string | undefined
pipelineName
,
var performance: Performance

MDN Reference

performance is a global reference for import { performance } from 'node:perf_hooks'

performance
.
Performance.now(): DOMHighResTimeStamp

The performance.now() method returns a high resolution timestamp in milliseconds.

MDN Reference

now
() -
const started: number
started
);
}
},
Instrumentation.attempt?<R>(info: ExecutionInfo, run: () => Promise<R>): Promise<R>

Around each call of the user callback (every retry / hedged attempt); info.context.attemptNumber tells which.

attempt
: (
info: ExecutionInfo
info
,
run: () => Promise<R>
run
) =>
run: () => Promise<R>
run
(), // around each call of your callback; info.context.attemptNumber
});
  • Each hook must call run() exactly once and return its result.
  • The first registered instrumentation is the outermost. A pipeline’s own instrumentation option runs innermost.
  • instrumentation: false on a builder opts that pipeline out of every hook, global ones included.
Setting Where Effect
tags: { team: 'billing' } Builder Added to source.tags of every event of the pipeline
name: 'GET /orders' execute() options context.operationName, used as the span name
attributes: { tenant: 'acme' } execute() options Extra span and OpenTelemetry metric attributes for that call
telemetry: false Builder The pipeline emits no events at all

Custom strategies emit their own events with report(source, 'OnMyEvent', context, args).