OpenTelemetry Implementation Guide
This guide covers the practical implementation of observability in Clean Stack using OpenTelemetry.
Quick Setup
import { initTelemetry } from '@clean-stack/backend-telemetry';
await initTelemetry('user-service', {
  tracing: true,
  metrics: true,
  logging: true
});
Configuration Options
Basic Configuration
interface TelemetryConfig {
  serviceName: string;
  environment?: 'development' | 'staging' | 'production';
  version?: string;
  tracing?: boolean | TracingConfig;
  metrics?: boolean | MetricsConfig;
  logging?: boolean | LoggingConfig;
}
Advanced Options
interface AdvancedConfig extends TelemetryConfig {
  exporters?: {
    tracing?: TracingExporter[];
    metrics?: MetricsExporter[];
    logging?: LoggingExporter[];
  };
  samplers?: {
    tracing?: TracingSamplerConfig;
  };
  propagators?: PropagatorConfig[];
}
Tracing
Request Tracing
import { trace } from '@clean-stack/backend-telemetry';
// Create a span
const span = trace.getTracer('my-service').startSpan('operation-name');
try {
  // Add attributes
  span.setAttribute('user.id', userId);
  
  // Add events
  span.addEvent('cache.miss');
  
  // Your business logic
  await doSomething();
} catch (error) {
  // Record errors
  span.recordException(error);
  throw error;
} finally {
  span.end();
}
Automatic Instrumentation
Clean Stack automatically instruments:
- HTTP/HTTPS requests
 - gRPC calls
 - Database operations
 - Cache operations
 - Queue operations
 
Metrics
Custom Metrics
import { metrics } from '@clean-stack/backend-telemetry';
// Create counter
const requestCounter = metrics.getMetric('requests_total', {
  description: 'Total number of requests',
  unit: '1',
  valueType: ValueType.INT64
});
// Increment counter
requestCounter.add(1, {
  endpoint: '/users',
  method: 'GET'
});
Default Metrics
Automatically collected metrics include:
- CPU usage
 - Memory usage
 - Event loop lag
 - HTTP request duration
 - gRPC call latency
 - Cache hit/miss ratio
 
Structured Logging
Basic Logging
import { logger } from '@clean-stack/backend-telemetry';
logger.info('User action completed', {
  userId: '123',
  action: 'login',
  duration: 150
});
Context Enrichment
import { enrichContext } from '@clean-stack/backend-telemetry';
// Add context to all logs in this scope
enrichContext({
  requestId: '456',
  tenant: 'acme-corp'
}, async () => {
  logger.info('Processing request');
  await processRequest();
  logger.info('Request completed');
});
Best Practices
- 
Span Management
// DO: Use the withSpan helper
await withSpan('operation-name', async (span) => {
// Your code here
});
// DON'T: Forget to end spans
const span = tracer.startSpan('operation');
// ... code without ending span - 
Error Handling
// DO: Record exceptions with context
try {
await riskyOperation();
} catch (error) {
span.recordException(error, {
attributes: {
'error.type': error.name,
'error.message': error.message
}
});
throw error;
} - 
Performance Impact
// DO: Use sampling for high-throughput operations
const config: TracingSamplerConfig = {
type: 'probabilistic',
ratio: 0.1 // Sample 10% of traces
}; 
Troubleshooting
Common Issues
- 
Missing Traces
- Check exporter configuration
 - Verify sampling settings
 - Ensure spans are being ended
 
 - 
High Cardinality
- Limit number of unique tag values
 - Use enumerated values where possible
 - Configure appropriate sampling
 
 - 
Performance Impact
- Adjust sampling rates
 - Optimize attribute collection
 - Monitor telemetry overhead