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
import { Module } from '@nestjs/common';
import { TelemetryModule } from '@breadstone/archipel-platform-telemetry';
@Module({
imports: [
TelemetryModule.register({
enabled: true,
}),
],
})
export class AppModule {}Module Configuration
ITelemetryOptions
| Property | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable/disable telemetry |
When disabled, the NoopTelemetryFacade is injected instead — all method calls are no-ops with zero overhead.
// 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:
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:
this._telemetry.recordCacheEvent({
cache: 'products',
outcome: 'hit', // 'hit' | 'miss'
phase: 'memory', // optional: 'memory' | 'redis' | 'db'
});Record Custom Metrics
this._telemetry.recordClientMetric('order_total_amount', 'histogram', order.total, { currency: 'EUR', region: 'DE' });Get Logger
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:
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:
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:
| Component | Implementation |
|---|---|
| Trace exporter | OTLP/HTTP |
| Metrics exporter | OTLP/HTTP |
| Express instrumentation | @opentelemetry/instrumentation-express |
| HTTP instrumentation | @opentelemetry/instrumentation-http |
| SDK shutdown hook | Graceful on OnModuleDestroy |
Real-World Example: Full Observability Stack
@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:
NoopTelemetryFacadeis injected instead ofTelemetryFacade- All operations execute normally without tracing overhead
runOperation()simply invokes the callbackrecordCacheEvent()andrecordClientMetric()are no-ops- No errors thrown — the application runs normally
Exports Summary
| Export | Type | Description |
|---|---|---|
TelemetryModule | NestJS Module | register() configuration |
TelemetryFacade | Service | Main telemetry API |
NoopTelemetryFacade | Service | Disabled-mode fallback |
TelemetryCacheMetricsRecorder | Service | Bridge for cache metrics |
TelemetryRuleEngine | Service | Conditional recording |
TelemetryLoggerService | Service | Logger integration |
OperationTelemetryInterceptor | Interceptor | Auto-trace HTTP requests |
TELEMETRY_FACADE | Token | Injection token for facade |
TELEMETRY_ENABLED | Token | Boolean enabled flag |
TELEMETRY_OPTIONS | Token | Configuration options |
TELEMETRY_SHUTDOWN | Token | Shutdown hook |