Skip to content

feat(tracing): add OpenTelemetry metrics middleware for outbox observability (Vibe Kanban)#39

Merged
jtomaszewski merged 3 commits intomainfrom
vk/64fd-add-built-in-met
Dec 22, 2025
Merged

feat(tracing): add OpenTelemetry metrics middleware for outbox observability (Vibe Kanban)#39
jtomaszewski merged 3 commits intomainfrom
vk/64fd-add-built-in-met

Conversation

@jtomaszewski
Copy link
Copy Markdown

@jtomaszewski jtomaszewski commented Dec 22, 2025

Summary

Adds built-in OpenTelemetry metrics support to the tracing package, providing comprehensive observability for outbox event processing.

Changes

New MetricsOutboxMiddleware

A new middleware in @fullstackhouse/nestjs-outbox-tracing that exposes the following OTel metrics:

Counters:

  • outbox.events.emitted - Total events emitted (with outbox.event_name attribute)
  • outbox.events.processed - Total listener executions
  • outbox.events.succeeded - Successful executions
  • outbox.events.failed - Failed executions
  • outbox.events.dead_lettered - Events moved to DLQ after exceeding max retries

Histogram:

  • outbox.processing.duration - Processing duration in milliseconds

All metrics include relevant attributes (outbox.event_name, outbox.listener, outbox.retry_count) for filtering and grouping in dashboards.

Why

Previously, the package relied entirely on custom middleware for observability with no built-in metrics. This made it harder to monitor outbox health out-of-the-box. By adding OTel metrics to the tracing package (which already has OTel as a peer dependency), users get production-ready observability that integrates with any OTel-compatible backend (Prometheus, Grafana, Datadog, etc.).

Usage

import { MetricsOutboxMiddleware } from '@fullstackhouse/nestjs-outbox-tracing';

OutboxModule.registerAsync({
  middlewares: [MetricsOutboxMiddleware],
  // ...
});

With custom meter name:

{
  provide: METRICS_OUTBOX_OPTIONS,
  useValue: { meterName: 'my-app' },
}

This PR was written using Vibe Kanban

## Summary

Added built-in metrics/observability to the NestJS Outbox package. The implementation includes:

### New Files Created

1. **`packages/core/src/metrics/metrics-collector.interface.ts`** - Interface for metrics collection with:
   - `MetricsCollector` interface with `recordEmit()`, `recordProcessed()`, `getSnapshot()`, `reset()`
   - `OutboxMetricsSnapshot` type with aggregate counters and per-event/per-listener breakdowns
   - `EventMetrics` and `ListenerMetrics` types for detailed tracking

2. **`packages/core/src/metrics/default-metrics-collector.ts`** - Default in-memory implementation tracking:
   - Total events emitted/processed/succeeded/failed
   - Total processing time
   - Metrics broken down by event name and listener name

3. **`packages/core/src/metrics/metrics.middleware.ts`** - Middleware that hooks into the event lifecycle to record metrics on emit and after processing

4. **`packages/core/src/metrics/index.ts`** - Barrel export

5. **Test files** - Unit tests for both the collector and middleware

### Module Integration

Updated `OutboxModule` with two new options:
- `enableMetrics: boolean` (default: `false`) - Enable built-in metrics collection
- `metricsCollector?: MetricsCollector` - Optional custom collector implementation

When enabled, the `METRICS_COLLECTOR_TOKEN` is exported and can be injected to access metrics.

### Usage Example

```typescript
OutboxModule.registerAsync({
  enableMetrics: true,
  // ... other options
});

// Inject and use metrics
@Injectable()
class MetricsReporter {
  constructor(
    @Inject(METRICS_COLLECTOR_TOKEN)
    private metrics: MetricsCollector,
  ) {}

  getStats() {
    const snapshot = this.metrics.getSnapshot();
    return {
      total: snapshot.eventsProcessed,
      successRate: snapshot.eventsSucceeded / snapshot.eventsProcessed,
      avgDuration: snapshot.totalProcessingTimeMs / snapshot.eventsProcessed,
    };
  }
}
```
## Summary

Moved metrics to the tracing package with proper OpenTelemetry metrics instead of in-memory counters.

### New File: `packages/tracing/src/metrics.outbox-middleware.ts`

A new `MetricsOutboxMiddleware` that uses OTel metrics API:

**Counters:**
- `outbox.events.emitted` - Total events emitted (with `outbox.event_name` attribute)
- `outbox.events.processed` - Total listener executions
- `outbox.events.succeeded` - Successful executions
- `outbox.events.failed` - Failed executions

**Histogram:**
- `outbox.processing.duration` - Processing duration in milliseconds

All metrics include `outbox.event_name` and `outbox.listener` attributes for filtering/grouping.

### Usage

```typescript
import { MetricsOutboxMiddleware } from '@fullstackhouse/nestjs-outbox-tracing';

OutboxModule.registerAsync({
  middlewares: [MetricsOutboxMiddleware],
  // ...
});
```

With custom meter name:
```typescript
{
  provide: METRICS_OUTBOX_OPTIONS,
  useValue: { meterName: 'my-app' },
}
```

The metrics integrate with any OTel-compatible backend (Prometheus, Grafana, Datadog, etc.) via the standard OTel metrics SDK.
…tered` counter that tracks events moved to DLQ with `outbox.event_name` and `outbox.retry_count` attributes.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Dec 22, 2025

Open in StackBlitz

npm i https://pkg.pr.new/fullstackhouse/nestjs-outbox/@fullstackhouse/nestjs-outbox@39
npm i https://pkg.pr.new/fullstackhouse/nestjs-outbox/@fullstackhouse/nestjs-outbox-mikro-orm-driver@39
npm i https://pkg.pr.new/fullstackhouse/nestjs-outbox/@fullstackhouse/nestjs-outbox-tracing@39
npm i https://pkg.pr.new/fullstackhouse/nestjs-outbox/@fullstackhouse/nestjs-outbox-typeorm-driver@39

commit: 69cc910

@jtomaszewski jtomaszewski changed the title Add built-in metrics/observability (vibe-kanban) feat(tracing): add OpenTelemetry metrics middleware for outbox observability (Vibe Kanban) Dec 22, 2025
@jtomaszewski jtomaszewski merged commit b12a5a8 into main Dec 22, 2025
2 checks passed
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.

1 participant