Skip to content
Open
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
2d569f7
wip: add OpenTelemetry metrics instrumentation
PavelPashov Feb 20, 2026
364ed40
add noop metrics
PavelPashov Nov 6, 2025
2e3cb2a
fix: check if metrics are initilized in sendCommand
PavelPashov Nov 7, 2025
cd31dfa
fix(otel): optimize metrics tracking by eliminating promise chaining …
PavelPashov Nov 10, 2025
0d55b20
refactor(otel): organize metrics into specialized metric groups
PavelPashov Nov 12, 2025
67d99ca
fix(otel): revert metrics to be tracked in the sendCommand method
PavelPashov Nov 17, 2025
ce74dc1
perf(metrics): optimize command metrics with factory pattern and inli…
PavelPashov Nov 18, 2025
0eedb01
feat: Add new OTEL_ATTRIBUTES constants
PavelPashov Jan 16, 2026
a14d159
feat: Add client-side-caching metric group with 4 new metric names
PavelPashov Jan 16, 2026
46ebd09
feat: Create error categorization stub function
PavelPashov Jan 16, 2026
cfe88dc
feat: Add pool name formatting utility function
PavelPashov Jan 16, 2026
bccbdf7
feat: Refactor recordConnectionCreateTime to use closure pattern
PavelPashov Jan 16, 2026
91305c4
feat: Rename redis.client.errors.handled to redis.client.errors
PavelPashov Jan 16, 2026
28c1a1b
feat: Update IOTelResiliencyMetrics interface with internal flag and …
PavelPashov Jan 16, 2026
27a090a
feat: Add redis.client.connection.notification attribute to maintenan…
PavelPashov Jan 16, 2026
63d2bcf
feat: Add db.response.status_code attribute extraction for Redis errors
PavelPashov Jan 16, 2026
3a0d9de
feat: Wire redis.client.connection.closed metric with close reason at…
PavelPashov Jan 17, 2026
97e1539
feat: Add db.client.connection.wait_time method with closure pattern
PavelPashov Jan 17, 2026
d5bce49
feat: Add db.client.connection.use_time method with closure pattern
PavelPashov Jan 17, 2026
eda93d2
feat: Integrate wait_time and use_time metrics into connection pool
PavelPashov Jan 17, 2026
bfa15be
feat: Add CSC metric instruments to registerInstruments
PavelPashov Jan 17, 2026
c2bc36e
feat: Create IOTelClientSideCacheMetrics interface and implementation…
PavelPashov Jan 17, 2026
55aba1d
feat: Wire redis.client.csc.requests metric to cache hit/miss detection
PavelPashov Jan 17, 2026
f0a47cb
feat: Wire redis.client.csc.items metric to track cache size changes
PavelPashov Jan 17, 2026
9a08be7
feat: Wire redis.client.csc.evictions metric with eviction reason
PavelPashov Jan 17, 2026
41f0188
feat: Wire redis.client.csc.network_saved metric to estimate bytes sa…
PavelPashov Jan 17, 2026
d22a0fb
refactor: Add count parameter to recordCacheEviction to batch evictio…
PavelPashov Jan 17, 2026
996d4ee
fix: typo
PavelPashov Jan 17, 2026
c0feedb
feat: Implement redis.client.pubsub.messages metric for pub/sub publi…
PavelPashov Jan 17, 2026
406a9c6
feat: Implement redis.client.stream.produce.messages metric for strea…
PavelPashov Jan 17, 2026
d167cc9
refactor: move pub/sub and stream metrics recording to command wrapper
PavelPashov Jan 18, 2026
d377b92
refactor: align metric groups with instrumentation spec
PavelPashov Jan 19, 2026
ce23121
feat: add error classification helper and enrich metrics attributes
PavelPashov Jan 21, 2026
89aaf1b
refactor: replace command wrapper with onSuccess hook for metrics
PavelPashov Jan 24, 2026
912e27d
feat(client): add client identity tracking for OpenTelemetry metrics
PavelPashov Feb 25, 2026
ad2fd8d
feat(otel): convert metrics to observable gauges with client registry
PavelPashov Feb 16, 2026
e843d0d
feat(otel): refine metrics coverage and remove connection use_time in…
PavelPashov Feb 25, 2026
6d14d9c
refactor(otel): resolve metric attributes via clientId registry lookup
PavelPashov Feb 20, 2026
4305d30
refactor(otel): propagate clientId through runtime metrics paths
PavelPashov Feb 20, 2026
19c95b8
test(otel): expand metrics coverage and add test utilities
PavelPashov Feb 20, 2026
34cd991
test(otel): fix flaky test
PavelPashov Feb 20, 2026
ee5f2b0
feat(otel): align metric attributes/config and expand observability c…
PavelPashov Feb 25, 2026
d0f42d3
test(otel): add maintenance metrics e2e scenario with standalone FI c…
PavelPashov Feb 25, 2026
3e19cad
refactor(otel): use instrumentation scope name and stop injecting res…
PavelPashov Feb 25, 2026
baac384
fix(opentelemetry): rename stream bucket config
PavelPashov Feb 26, 2026
08c7413
docs(opentelemetry): add metrics docs/examples
PavelPashov Feb 26, 2026
f8a51c9
fix(otel): scope redirection error dedupe to cluster retry path
PavelPashov Mar 4, 2026
181d086
fix(opentelemetry): normalize db.namespace and server.port to strings…
PavelPashov Mar 4, 2026
bc14732
fix(otel): disable recordNetworkBytesSaved for CSC
PavelPashov Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,33 @@ See the [Programmability overview](https://github.com/redis/node-redis/blob/mast

Check out the [Clustering Guide](https://github.com/redis/node-redis/blob/master/docs/clustering.md) when using Node Redis to connect to a Redis Cluster.

### OpenTelemetry

#### OpenTelemetry Metrics Instrumentation

```typescript
import { createClient, OpenTelemetry } from "redis";

OpenTelemetry.init({
metrics: {
enabled: true
}
});

const client = createClient()

await client.connect();
// ... use the client as usual
```

**Important:** Initializing `OpenTelemetry` only enables node-redis metrics instrumentation and requires both `@opentelemetry/api` and an OpenTelemetry SDK configured in your application.

**Important:** Initialize `OpenTelemetry` before creating Redis clients.
For SDK/provider/exporter setup, verification, and advanced configuration, see:

- [OpenTelemetry Metrics docs](./docs/otel-metrics.md)
- [OpenTelemetry Metrics example](./examples/otel-metrics.js)

### Events

The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes:
Expand Down
173 changes: 173 additions & 0 deletions docs/otel-metrics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# OpenTelemetry Metrics

## Get started

### Step 1. Install node-redis dependencies

```bash
npm install redis @opentelemetry/api
```

`@opentelemetry/api` is required at runtime for `OpenTelemetry.init(...)`.

### Step 2. Install OpenTelemetry SDK packages

```bash
npm install @opentelemetry/sdk-metrics
```

Alternative (Node SDK):

```bash
npm install @opentelemetry/sdk-node @opentelemetry/sdk-metrics
```

If you export to OTLP or another backend, install the matching OpenTelemetry exporter package.

For more information, see the [OpenTelemetry Metrics documentation](https://opentelemetry.io/docs/instrumentation/js/exporters/#metrics).

### Step 3. Register OpenTelemetry

Option A: Use `@opentelemetry/sdk-metrics` directly

```typescript
import { metrics } from "@opentelemetry/api";
import {
ConsoleMetricExporter,
MeterProvider,
PeriodicExportingMetricReader,
} from "@opentelemetry/sdk-metrics";

const meterProvider = new MeterProvider({
readers: [
new PeriodicExportingMetricReader({
exporter: new ConsoleMetricExporter(),
exportIntervalMillis: 1000,
}),
],
});

metrics.setGlobalMeterProvider(meterProvider);
```

Option B: Use `@opentelemetry/sdk-node`

```typescript
import { NodeSDK } from "@opentelemetry/sdk-node";
import {
ConsoleMetricExporter,
PeriodicExportingMetricReader,
} from "@opentelemetry/sdk-metrics";

const sdk = new NodeSDK({
metricReader: new PeriodicExportingMetricReader({
exporter: new ConsoleMetricExporter(),
exportIntervalMillis: 1000,
}),
});

await sdk.start();
```

### Step 4. Initialize node-redis instrumentation before creating clients

```typescript
import { createClient, OpenTelemetry } from "redis";

OpenTelemetry.init({
metrics: {
enabled: true,
},
});

const client = createClient();
await client.connect();
```

## Examples

### Minimal Example

```typescript
import { OpenTelemetry } from "redis";

OpenTelemetry.init({
metrics: {
enabled: true,
},
});
```

### Full Example

```typescript
import { OpenTelemetry } from "redis";

OpenTelemetry.init({
metrics: {
enabled: true,
meterProvider: customMeterProvider,
enabledMetricGroups: ["command", "pubsub", "streaming", "resiliency"],
includeCommands: ["GET", "HSET", "XREADGROUP", "PUBLISH"],
excludeCommands: ["SET"],
hidePubSubChannelNames: true,
hideStreamNames: false,
bucketsOperationDuration: [0.001, 0.01, 0.1, 1],
bucketsStreamProcessingDuration: [0.01, 0.1, 1, 5],
},
});
```

## Configuration

### ObservabilityConfig

| Property | Default | Description |
| -------- | ------- | ----------- |
| metrics | | OpenTelemetry metrics configuration for node-redis. |

### MetricConfig

| Property | Default | Description |
| -------- | ------- | ----------- |
| enabled | **false** | Enables metric collection. |
| meterProvider | | Uses this provider instead of the global provider from @opentelemetry/api. |
| includeCommands | **[]** | Case-insensitive allow-list for command metrics. |
| excludeCommands | **[]** | Case-insensitive deny-list for command metrics. If both include and exclude match, exclude wins. |
| enabledMetricGroups | **['connection-basic', 'resiliency']** | Metric groups to enable. Supported groups: command, connection-basic, connection-advanced, resiliency, pubsub, streaming, client-side-caching. |
| hidePubSubChannelNames | **false** | If true, omits redis.client.pubsub.channel to reduce cardinality. |
| hideStreamNames | **false** | If true, omits redis.client.stream.name to reduce cardinality. |
| bucketsOperationDuration | **[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]** | Histogram bucket boundaries for db.client.operation.duration (seconds). |
| bucketsConnectionCreateTime | **[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]** | Histogram bucket boundaries for db.client.connection.create_time (seconds). |
| bucketsConnectionWaitTime | **[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]** | Histogram bucket boundaries for db.client.connection.wait_time (seconds). |
| bucketsStreamProcessingDuration | **[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]** | Histogram bucket boundaries for redis.client.stream.lag (seconds). |

## Metric groups and metrics

| Metric Group | Metric Name |
| ------------ | ----------- |
| command | db.client.operation.duration |
| connection-basic | db.client.connection.count |
| connection-basic | db.client.connection.create_time |
| connection-basic | redis.client.connection.relaxed_timeout |
| connection-basic | redis.client.connection.handoff |
| connection-advanced | db.client.connection.pending_requests |
| connection-advanced | db.client.connection.wait_time |
| connection-advanced | redis.client.connection.closed |
| resiliency | redis.client.errors |
| resiliency | redis.client.maintenance.notifications |
| pubsub | redis.client.pubsub.messages |
| streaming | redis.client.stream.lag |
| client-side-caching | redis.client.csc.requests |
| client-side-caching | redis.client.csc.items |
| client-side-caching | redis.client.csc.evictions |
| client-side-caching | redis.client.csc.network_saved |

## Notes

- `OpenTelemetry` is a singleton and a second `init` call throws.
- If `@opentelemetry/api` is not installed, `init` throws.

## Runnable example

See ../examples/otel-metrics.js.
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ This folder contains example scripts showing how to use Node Redis in different
| `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog). |
| `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys. |
| `managing-json.js` | Store, retrieve and manipulate JSON data atomically with [RedisJSON](https://redisjson.io/). |
| `otel-metrics.js` | Enable OpenTelemetry metrics for node-redis, generate command and resiliency signals, and export them via OpenTelemetry SDK metrics. |
| `pubsub-publisher.js` | Adds multiple messages on 2 different channels messages to Redis. |
| `pubsub-subscriber.js` | Reads messages from channels using `PSUBSCRIBE` command. |
| `search-hashes.js` | Uses [RediSearch](https://redisearch.io) to index and search data in hashes. |
Expand Down
58 changes: 58 additions & 0 deletions examples/otel-metrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// OpenTelemetry metrics example for node-redis.
// Demonstrates enablement, command activity, and metric export verification.

import { createClient, OpenTelemetry } from 'redis';
import { metrics } from '@opentelemetry/api';
import {
ConsoleMetricExporter,
MeterProvider,
PeriodicExportingMetricReader
} from '@opentelemetry/sdk-metrics';

// Export metrics to console for easy local verification.
const reader = new PeriodicExportingMetricReader({
exporter: new ConsoleMetricExporter(),
});

const meterProvider = new MeterProvider({
readers: [reader]
});

metrics.setGlobalMeterProvider(meterProvider);

// Enable OpenTelemetry before creating clients
OpenTelemetry.init({
metrics: {
enabled: true,
enabledMetricGroups: ['command', 'connection-basic', 'resiliency'],
}
});

const client = createClient();

client.on('error', (err) => {
console.error('Redis client error:', err);
});

try {
await client.connect();

// Normal command traffic.
await client.ping();
await client.set('otel:example:key', '1');
await client.get('otel:example:key');

// Generate a handled error to demonstrate resiliency metrics.
await client.hSet('otel:example:hash', 'field', 'value');
try {
await client.incr('otel:example:hash');
} catch (err) {
console.log('Expected command error:', err);
}

// Force export so output is visible immediately.
await meterProvider.forceFlush();
} finally {
client.destroy();
await meterProvider.shutdown();
}
Loading
Loading