Skip to content

feat: OTel distributed tracing (spans) via TracingChannel - #2

Draft
logaretm wants to merge 4 commits into
masterfrom
feat/otel-tracing
Draft

logaretm wants to merge 4 commits into
masterfrom
feat/otel-tracing

Conversation

@logaretm

@logaretm logaretm commented Mar 26, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Adds built-in OpenTelemetry span creation alongside existing metrics, subscribing to the same TracingChannel events
  • Context propagation via bindStore on OTel's internal AsyncLocalStorage — enables automatic parent-child span relationships (e.g. MULTI → child command spans)
  • Follows OTel semantic conventions for database spans (db.operation.name, db.query.text, db.namespace, error.type, db.response.status_code, db.operation.batch.size)

Spans emitted

Channel Span Name Kind Config
node-redis:command {COMMAND} (e.g. SET) CLIENT always (filterable)
node-redis:batch MULTI / PIPELINE CLIENT always
node-redis:connect redis connect CLIENT enableConnectionSpans
node-redis:connection:wait redis connection:wait INTERNAL enableConnectionSpans

Config

OpenTelemetry.init({
  tracing: {
    enabled: true,
    tracerProvider?: TracerProvider,    // defaults to global
    includeCommands?: string[],
    excludeCommands?: string[],
    enableConnectionSpans?: boolean,   // default: false
  }
});

Context propagation

Uses the same bindStore workaround from otel-tracing-channel — accesses OTel's internal AsyncLocalStorage via context._getContextManager()._asyncLocalStorage and binds it to each TracingChannel's start sub-channel. Falls back to WeakMap-based span tracking when ALS is not accessible.

Copilot AI review requested due to automatic review settings March 26, 2026 19:42
@logaretm
logaretm marked this pull request as draft March 26, 2026 19:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds OpenTelemetry distributed tracing support to the client package by creating spans from existing TracingChannel events, including optional connection-related spans and command filtering.

Changes:

  • Introduces OTelTracing to subscribe to TracingChannel lifecycle events and emit OTel spans with Redis/db semantic attributes.
  • Extends observability configuration/types to include tracing settings and adds db.operation.batch.size attribute key.
  • Adds unit tests covering span creation, parent-child relationships, errors, filtering, and lifecycle.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/client/package.json Adds OTel tracing-related devDependencies used by new tracing tests.
packages/client/lib/opentelemetry/types.ts Adds TracingConfig and new batch-size attribute constant; wires tracing into ObservabilityConfig.
packages/client/lib/opentelemetry/tracing.ts Implements OTelTracing span creation and context propagation via bindStore.
packages/client/lib/opentelemetry/tracing.spec.ts Adds unit tests for span behavior (commands, batch, connection spans, errors, lifecycle).
packages/client/lib/opentelemetry/index.ts Initializes tracing when enabled and re-exports tracing config/entrypoints.
package-lock.json Updates lockfile for newly added devDependencies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 19 to +23
"@node-rs/xxhash": "1.7.6",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/context-async-hooks": "^2.6.1",
"@opentelemetry/sdk-metrics": "^2.2.0",
"@opentelemetry/sdk-trace-base": "^2.6.1",

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new devDependencies @opentelemetry/context-async-hooks and @opentelemetry/sdk-trace-base require Node ^18.19.0 || >=20.6.0 (per their package engines), but this package advertises engines.node: >= 18. This can produce engine warnings/errors for contributors/CI pinned to Node 18.0–18.18; consider either bumping the supported Node engine range (e.g. to >=18.19) or using OTel devDependency versions compatible with Node 18.0.

Copilot uses AI. Check for mistakes.
api: typeof import('@opentelemetry/api');
config?: TracingConfig;
}) {
if (OTelTracing.#initialized) return;

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OTelTracing.init ignores config.enabled and silently no-ops when called multiple times, which is inconsistent with OpenTelemetry.init / OTelMetrics.init (they throw on re-init) and makes the enabled flag in TracingConfig misleading. Consider either (a) honoring config.enabled === false inside OTelTracing.init and throwing on double-init for consistency, or (b) removing enabled from TracingConfig and keeping the current behavior intentionally.

Suggested change
if (OTelTracing.#initialized) return;
if (OTelTracing.#initialized) {
throw new Error('OTelTracing has already been initialized');
}
// If tracing is explicitly disabled via config, mark as initialized
// but do not create an instance or subscribe to any channels.
if (config?.enabled === false) {
OTelTracing.#initialized = true;
return;
}

Copilot uses AI. Check for mistakes.
@logaretm
logaretm force-pushed the feat/tracing-channel branch from 960fab5 to f870361 Compare March 27, 2026 14:24
@logaretm
logaretm changed the base branch from feat/tracing-channel to master April 2, 2026 16:42
logaretm and others added 2 commits April 2, 2026 12:44
Adds built-in OpenTelemetry distributed tracing (spans) alongside the
existing metrics implementation. Spans are created by subscribing to the
same TracingChannels that metrics use, keeping the core code unchanged.

Context propagation uses the bindStore hack on OTel's internal
AsyncLocalStorage so that parent-child span relationships work
automatically (e.g. MULTI > individual commands).

Span coverage:
- Command spans (SET, GET, etc.) with db.query.text, db.namespace
- Batch spans (MULTI/PIPELINE) with db.operation.batch.size
- Connection + connection wait spans (opt-in via config)
- Error attributes: error.type, db.response.status_code

All attributes follow OTel semantic conventions for database spans.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@logaretm
logaretm force-pushed the feat/otel-tracing branch from 01f56ea to 57a67c5 Compare April 2, 2026 16:44
logaretm and others added 2 commits April 2, 2026 12:59
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants