Skip to content

platform-telemetry

OpenTelemetry integration providing distributed tracing, metrics collection, and a graceful no-op fallback when telemetry is disabled. Wraps the OTEL SDK with a clean facade pattern.

Package: @breadstone/archipel-platform-telemetry

Quick Start

typescript
import { Module } from '@nestjs/common';
import { TelemetryModule } from '@breadstone/archipel-platform-telemetry';

@Module({
  imports: [
    TelemetryModule.register({
      enabled: true,
    }),
  ],
})
export class AppModule {}

Module Configuration

ITelemetryOptions

PropertyTypeDefaultDescription
enabledbooleanfalseEnable/disable telemetry

When disabled, the NoopTelemetryFacade is injected instead — all method calls are no-ops with zero overhead.

typescript
// Disabled telemetry — safe, no-overhead
TelemetryModule.register({ enabled: false });

// Enabled telemetry — full OTEL SDK
TelemetryModule.register({ enabled: true });

TelemetryFacade

Main API for recording operations, cache events, and custom metrics.

Wrap Operations

Automatically trace async operations with duration, success/failure status, and error capture:

typescript
import { TelemetryFacade, TELEMETRY_FACADE } from '@breadstone/archipel-platform-telemetry';

@Injectable()
export class OrderService {
  constructor(@Inject(TELEMETRY_FACADE) private readonly _telemetry: TelemetryFacade) {}

  public async processOrder(orderId: string): Promise<IOrder> {
    return this._telemetry.runOperation('processOrder', async () => {
      const order = await this._orderRepo.findById(orderId);
      await this._paymentService.charge(order);
      await this._inventoryService.reserve(order.items);
      return order;
    });
    // Automatically records:
    // - operation.name = 'processOrder'
    // - duration_ms
    // - status = 'success' | 'error'
    // - error details (if thrown)
  }
}

Record Cache Events

Track cache hits and misses:

typescript
this._telemetry.recordCacheEvent({
  cache: 'products',
  outcome: 'hit', // 'hit' | 'miss'
  phase: 'memory', // optional: 'memory' | 'redis' | 'db'
});

Record Custom Metrics

typescript
this._telemetry.recordClientMetric('order_total_amount', 'histogram', order.total, { currency: 'EUR', region: 'DE' });

Get Logger

typescript
const logger = this._telemetry.getLogger();
logger.log('Order processed', { orderId, total: order.total });

TelemetryCacheMetricsRecorder

Bridges the ICacheMetricsRecorder interface (from platform-core) with the telemetry facade:

typescript
import { TelemetryCacheMetricsRecorder } from '@breadstone/archipel-platform-telemetry';
import { MemoryLayeredCache } from '@breadstone/archipel-platform-core';

// In a module provider
{
    provide: 'PRODUCT_CACHE',
    useFactory: (recorder: TelemetryCacheMetricsRecorder) => {
        return new MemoryLayeredCache<string, IProduct>(
            async (key) => fetchProduct(key),
            {
                ttlMs: 60_000,
                metricsRecorder: recorder,
                cacheName: 'products',
            }
        );
    },
    inject: [TelemetryCacheMetricsRecorder],
}

TelemetryRuleEngine

Conditionally enable/disable telemetry recording per operation:

typescript
import { TelemetryRuleEngine } from '@breadstone/archipel-platform-telemetry';

@Injectable()
export class AppTelemetryRules extends TelemetryRuleEngine {
  public shouldRecord(operationName: string): boolean {
    // Skip noisy health checks
    if (operationName === 'healthCheck') return false;
    return true;
  }
}

OpenTelemetry Internals

The module initializes these OTEL components:

ComponentImplementation
Trace exporterOTLP/HTTP
Metrics exporterOTLP/HTTP
Express instrumentation@opentelemetry/instrumentation-express
HTTP instrumentation@opentelemetry/instrumentation-http
SDK shutdown hookGraceful on OnModuleDestroy

Real-World Example: Full Observability Stack

typescript
@Module({
  imports: [
    TelemetryModule.register({ enabled: process.env.OTEL_ENABLED === 'true' }),
    LoggerModule, // Sentry integration
    DatabaseModule.forRoot(),
  ],
})
export class AppModule {}

// In a service
@Injectable()
export class ProductService {
  constructor(
    @Inject(TELEMETRY_FACADE) private readonly _telemetry: TelemetryFacade,
    private readonly _analytics: AnalyticsService,
    private readonly _productRepo: ProductRepository,
  ) {}

  public async searchProducts(query: string): Promise<IProduct[]> {
    return this._telemetry.runOperation('searchProducts', async () => {
      const results = await this._productRepo.execute(searchProducts(query));

      this._telemetry.recordClientMetric('search_results_count', 'histogram', results.length, {
        query_length: query.length.toString(),
      });

      return results;
    });
  }
}

Graceful Degradation

When enabled: false or when the OTEL SDK fails to initialize:

  • NoopTelemetryFacade is injected instead of TelemetryFacade
  • All operations execute normally without tracing overhead
  • runOperation() simply invokes the callback
  • recordCacheEvent() and recordClientMetric() are no-ops
  • No errors thrown — the application runs normally

Exports Summary

ExportTypeDescription
TelemetryModuleNestJS Moduleregister() configuration
TelemetryFacadeServiceMain telemetry API
NoopTelemetryFacadeServiceDisabled-mode fallback
TelemetryCacheMetricsRecorderServiceBridge for cache metrics
TelemetryRuleEngineServiceConditional recording
TelemetryLoggerServiceServiceLogger integration
OperationTelemetryInterceptorInterceptorAuto-trace HTTP requests
TELEMETRY_FACADETokenInjection token for facade
TELEMETRY_ENABLEDTokenBoolean enabled flag
TELEMETRY_OPTIONSTokenConfiguration options
TELEMETRY_SHUTDOWNTokenShutdown hook

Released under the MIT License.