@delta-base/observability
Version:
Observability framework for delta-base applications
1,313 lines (1,044 loc) • 36.5 kB
Markdown
# Observability Framework
A comprehensive, configurable observability framework built on OpenTelemetry with semantic wide events, automatic middleware, intelligent error categorization, and utility functions for common patterns.
## Installation
```bash
npm install @delta-base/observability
```
## Quick Start
```typescript
import { observabilityMiddleware } from '@delta-base/observability';
import { BUILD_INFO } from './build-info'; // Generated by collect-build-info script
const app = new OpenAPIHono();
// Add observability middleware - automatically handles tracing, HTTP tracking,
// intelligent error detection, and build info initialization
app.use('*', observabilityMiddleware({
buildInfo: BUILD_INFO // Automatically initializes build info
}));
// Your routes now have automatic observability with smart error categorization
app.post('/users', async (c) => {
const tracker = c.get('wideEvent');
// Set operation and track user data being created
tracker
.setOperation('user.create', { domain: 'user-management', layer: 'api' })
.trackUser({
email: 'john@example.com',
actorType: 'public'
});
// Your business logic here
const result = await createUser(userData);
// Track the created entity
tracker.trackEntity({
type: 'user',
id: result.id
});
return c.json(result, 201);
});
```
> 💡 **Tip**: Add `"prebuild": "collect-build-info --package-json-dir . --output-dir src"` to your `package.json` scripts to automatically generate build info before each build.
## Configuration
The framework now supports comprehensive configuration to customize behavior for different domains and use cases.
### Simple Configuration
```typescript
import { configureObservability, observabilityMiddleware } from '@delta-base/observability';
import { BUILD_INFO } from './build-info';
// Option 1: Configure globally
configureObservability({
serviceName: 'my-service',
autoTrackHttp: true,
autoTrackErrorResponses: true,
debug: false
});
// Option 2: Configure via middleware (recommended for build info)
app.use('*', observabilityMiddleware({
serviceName: 'my-service',
autoTrackHttp: true,
autoTrackErrorResponses: true,
debug: false,
buildInfo: BUILD_INFO // Automatically initializes build info
}));
```
### Advanced Configuration
```typescript
import { configureObservabilityAdvanced } from '@delta-base/observability';
// Configure with comprehensive options
configureObservabilityAdvanced({
tracing: {
enabled: true,
serviceName: 'my-service',
serviceVersion: '1.0.0',
environment: 'production'
},
http: {
trackRequests: true,
trackResponses: true,
trackBodies: false,
excludeHeaders: ['authorization', 'cookie']
},
errors: {
autoTrackErrorResponses: true,
autoCategorizeErrors: true,
defaultUnexpected: true
},
performance: {
trackTiming: true,
categorizePerformance: true,
performanceThresholds: {
fast: 100,
slow: 1000
}
},
contextSchema: {
user: ['id', 'email', 'role', 'organization_id'],
operation: ['type', 'domain', 'layer', 'tenant_id'],
// ... more categories
}
});
```
### Build Information Integration
The framework includes comprehensive build information tracking with multiple initialization methods:
#### Method 1: Automatic via Middleware (Recommended)
The simplest approach is to pass build info directly to the middleware:
```typescript
import { observabilityMiddleware } from '@delta-base/observability';
import { BUILD_INFO } from './build-info'; // Generated by collect-build-info script
const app = new OpenAPIHono();
// Automatically initializes build info when middleware is set up
app.use('*', observabilityMiddleware({
buildInfo: BUILD_INFO
}));
// Your routes now have build info automatically available
app.post('/users', async (c) => {
const tracker = c.get('wideEvent');
// Build info is already initialized - no manual setup needed
});
```
#### Method 2: Manual Initialization
If you need more control or want to initialize build info elsewhere:
```typescript
import { initializeBuildInfo } from '@delta-base/observability';
// Initialize build info manually in your application
initializeBuildInfo({
'service.name': 'my-service',
'service.version': '1.0.0',
'service.environment': 'production',
'git.commit_hash': 'abc123',
'git.branch': 'main',
'build.timestamp': new Date().toISOString()
});
// Then use middleware without buildInfo parameter
app.use('*', observabilityMiddleware());
```
#### Method 3: Build Info Collection Script
Generate build info automatically using the CLI tool:
```bash
# Add to your package.json scripts
"prebuild": "collect-build-info --package-json-dir . --output-dir src"
```
This creates a `src/build-info.ts` file with comprehensive build information:
```typescript
// Auto-generated build info - do not edit manually
export const BUILD_INFO = {
"service.name": "@my-org/my-service",
"service.version": "1.0.0",
"service.environment": "production",
"git.commit_hash": "abc123def456",
"git.branch": "main",
"git.commit_message": "feat: add user management",
"git.commit_author": "developer",
"build.timestamp": "2024-01-01T12:00:00.000Z",
"deployment.user": "ci-cd",
"deployment.trigger": "push"
} as const;
```
Then use it in your middleware:
```typescript
import { BUILD_INFO } from './build-info';
app.use('*', observabilityMiddleware({ buildInfo: BUILD_INFO }));
```
#### Build Info Benefits
With build info properly initialized, your observability data includes:
- **Service identification**: `service.name`, `service.version`, `service.environment`
- **Git context**: `git.commit_hash`, `git.branch`, `git.commit_author`
- **Build context**: `build.timestamp`, `build.id`, `deployment.user`
- **Deployment context**: `deployment.trigger`, `deployment.ci_build_url`
This appears in all traces and logs:
```json
{
"service.name": "@my-org/my-service",
"service.version": "1.0.0",
"git.commit_hash_short": "abc123d",
"git.branch": "main",
"operation": "user.create",
"request_id": "uuid-here",
// ... other tracking data
}
```
### Context Schema Customization
The framework uses a configurable context schema to determine which fields are tracked and extracted from spans. You can customize this for your domain:
```typescript
import { addContextFields } from '@delta-base/observability';
// Add custom user fields
addContextFields('user', ['role', 'organization_id', 'permissions']);
// Add custom operation fields
addContextFields('operation', ['tenant_id', 'workspace_id']);
// Add custom infrastructure fields
addContextFields('infrastructure', ['queue.name', 'cache.hit_rate']);
```
### Custom Error Patterns
Extend the automatic error detection with domain-specific patterns:
```typescript
import { addErrorPattern } from '@delta-base/observability';
// Add custom error pattern for your API
addErrorPattern({
name: 'CustomBusinessError',
matcher: (body) =>
body?.error?.type === 'BUSINESS_RULE_VIOLATION' &&
body?.error?.code,
tracker: (wideEvent, body) => {
wideEvent.trackError({
type: 'BusinessRuleViolation',
message: body.error.message,
businessRule: {
rule: body.error.code,
violation: body.error.details
},
unexpected: false // Expected - business rule violation
});
}
});
```
## Core Concepts
### Wide Events
Wide events are context-rich, structured logs that capture comprehensive information about operations. Instead of multiple log lines, you emit a single event per service hop with all relevant context.
### Intelligent Error Categorization
The framework automatically categorizes errors into **expected** and **unexpected** types:
#### Expected Errors (`unexpected: false`)
- **Validation errors** - User submitted invalid data
- **Business rule violations** - Duplicate email, insufficient permissions
- **Authentication/authorization failures** - Wrong password, missing permissions
- **Rate limiting** - User exceeded API limits
- **Resource not found** - User requested non-existent data
#### Unexpected Errors (`unexpected: true`)
- **System failures** - Database down, network issues
- **Programming errors** - Null pointer exceptions, type errors
- **Infrastructure issues** - Service unavailable, timeouts
- **Unhandled edge cases** - Code paths that shouldn't be reached
This categorization enables:
- **Smart alerting** - Only alert on unexpected errors
- **SLA tracking** - Only count unexpected errors against service level agreements
- **User experience monitoring** - Track expected errors separately for UX insights
- **Debugging prioritization** - Unexpected errors need immediate attention
### Semantic Tracking Methods
The framework provides semantic methods for tracking different types of data:
- `setOperation()` - Set operation type and context (domain, layer, etc.)
- `trackUser()` - User and actor information
- `trackEntity()` - Primary resource being operated on
- `trackInfrastructure()` - Technical infrastructure details
- `trackError()` - Error information with automatic categorization
- `recordEvent()` - Business events during the operation
## Configuration API Reference
### ObservabilityConfig
The comprehensive configuration interface for the observability framework.
```typescript
interface ObservabilityConfig {
contextSchema: ContextSchemaConfig; // Defines which fields are tracked
tracing: TracingConfig; // OpenTelemetry tracing configuration
http: HttpTrackingConfig; // HTTP tracking configuration
errors: ErrorTrackingConfig; // Error detection and categorization
performance: PerformanceConfig; // Performance tracking and thresholds
debug: boolean; // Debug logging
}
```
### ContextSchemaConfig
Defines which fields are tracked for each category of context data.
```typescript
interface ContextSchemaConfig {
user: string[]; // User-related fields (id, email, role, etc.)
entity: string[]; // Entity-related fields (type, id, status, etc.)
operation: string[]; // Operation-related fields (type, domain, layer, etc.)
infrastructure: string[]; // Infrastructure fields (database, event_store, etc.)
error: string[]; // Error-related fields (type, message, unexpected, etc.)
http: string[]; // HTTP-related fields (method, path, status_code, etc.)
performance: string[]; // Performance fields (duration_ms, category, etc.)
service: string[]; // Service-related fields (name, version, environment, etc.)
}
```
### ErrorResponsePattern
Defines a pattern for detecting and categorizing error responses.
```typescript
interface ErrorResponsePattern {
name: string; // Human-readable pattern name
matcher: (body: any) => boolean; // Test if response matches pattern
tracker: (wideEvent: WideEventsWrapper, body: any) => void; // Track the error
}
```
### Configuration Functions
#### observabilityMiddleware(options?: ObservabilityOptions)
Create observability middleware with configuration options.
```typescript
interface ObservabilityOptions {
/** Custom service name (defaults to environment variable SERVICE_NAME) */
serviceName?: string;
/** Whether to automatically track HTTP request/response (defaults to true) */
autoTrackHttp?: boolean;
/** Whether to automatically detect and track error responses (defaults to true) */
autoTrackErrorResponses?: boolean;
/** Whether to enable debug logging (defaults to false) */
debug?: boolean;
/** Build info to initialize (optional) */
buildInfo?: Record<string, string | number | boolean>;
}
// Simple usage
app.use('*', observabilityMiddleware());
// With configuration
app.use('*', observabilityMiddleware({
serviceName: 'my-service',
autoTrackHttp: true,
autoTrackErrorResponses: true,
debug: false,
buildInfo: BUILD_INFO
}));
```
#### configureObservability(options: ObservabilityOptions)
Simple configuration for basic use cases.
```typescript
configureObservability({
serviceName: 'my-service',
autoTrackHttp: true,
autoTrackErrorResponses: true,
debug: false
});
```
#### configureObservabilityAdvanced(config: Partial<ObservabilityConfig>)
Advanced configuration with full control over all options.
```typescript
configureObservabilityAdvanced({
tracing: {
serviceName: 'my-service',
environment: 'production'
},
contextSchema: {
user: ['id', 'email', 'role'],
operation: ['type', 'domain', 'tenant_id']
}
});
```
#### addContextFields(category: keyof ContextSchemaConfig, fields: string[])
Add custom fields to a context category.
```typescript
addContextFields('user', ['organization_id', 'permissions']);
addContextFields('operation', ['workspace_id', 'feature_flag']);
```
#### addErrorPattern(pattern: ErrorResponsePattern)
Add a custom error detection pattern.
```typescript
addErrorPattern({
name: 'RateLimitError',
matcher: (body) => body?.error?.code === 'RATE_LIMIT_EXCEEDED',
tracker: (wideEvent, body) => {
wideEvent.trackError({
type: 'RateLimitError',
message: body.error.message,
unexpected: false // Expected - rate limiting
});
}
});
```
## API Reference
### WideEventsWrapper
The main class for tracking operation context.
```typescript
import { WideEventsWrapper } from '@delta-base/observability';
const tracker = new WideEventsWrapper('Create User', 'user.create');
await tracker.execute(async (t) => {
// Set operation and track different types of context
t.setOperation('user.create', { domain: 'user-management', layer: 'api' })
.trackUser({ id: '123', email: 'john@example.com' })
.trackEntity({ type: 'user', id: '123' });
// Your business logic here
});
```
#### trackUser(userData: UserData)
Track user and actor information.
```typescript
tracker.trackUser({
id: '123',
email: 'john@example.com',
firstName: 'John',
lastName: 'Doe',
actorType: 'authenticated_user'
});
```
#### trackEntity(entityData: EntityData)
Track the primary entity/resource being operated on.
```typescript
tracker.trackEntity({
type: 'user',
id: 'user-123',
status: 'active'
});
```
#### setOperation(operationType: string, context?: OperationData)
Set the operation type and context for this operation.
```typescript
// Simple operation
tracker.setOperation('user.create');
// With context
tracker.setOperation('user.create', {
domain: 'user-management',
layer: 'api'
});
// With custom properties
tracker.setOperation('user.change-password', {
domain: 'user-management',
layer: 'business-logic',
trigger: 'user-initiated'
});
```
#### trackInfrastructure(infrastructureData: InfrastructureData)
Track infrastructure and technical details.
```typescript
tracker.trackInfrastructure({
database: {
operation: 'INSERT',
table: 'users',
duration: 45
},
eventStore: {
streamId: 'user-123',
expectedVersion: 0,
nextVersion: 1
},
externalService: {
name: 'notification-service',
operation: 'send-welcome-email',
duration: 120
}
});
```
#### trackError(errorData: ErrorData)
Track error information with automatic categorization.
```typescript
// Expected error - validation failure
tracker.trackError({
type: 'ValidationError',
message: 'Email is required',
validation: {
issueCount: 2,
fields: ['email', 'firstName']
},
unexpected: false // Expected - user submitted invalid data
});
// Expected error - business rule violation
tracker.trackError({
type: 'BusinessRuleViolation',
message: 'User already exists',
businessRule: {
rule: 'unique_email',
violation: 'already_exists'
},
unexpected: false // Expected - business constraint violation
});
// Unexpected error - system failure
tracker.trackError({
type: 'DatabaseConnectionError',
message: 'Connection pool exhausted',
unexpected: true // Unexpected - infrastructure issue
});
```
#### recordEvent(eventName: string, eventData?: object)
Record business events during the operation.
```typescript
tracker.recordEvent('user.created', {
userId: '123',
source: 'api'
});
tracker.recordEvent('validation.completed', {
validation_type: 'input'
});
```
### Middleware
#### observabilityMiddleware(options?)
Automatic observability middleware that:
- Initializes OpenTelemetry tracing
- Creates a WideEventsWrapper for the request
- Automatically tracks HTTP request/response details
- Intelligently detects and categorizes error responses using pattern matching
- Makes the tracker available via `c.get('wideEvent')`
- Provides simple fallback operation type derivation
```typescript
import { observabilityMiddleware } from '@delta-base/observability';
// Basic usage
app.use('*', observabilityMiddleware());
// With custom options
app.use('*', observabilityMiddleware({
serviceName: 'my-custom-service',
autoTrackHttp: true,
autoTrackErrorResponses: true
}));
```
The middleware includes intelligent error response detection using pattern matching:
```typescript
// Automatic detection of OpenAPIHono validation errors
{
name: 'OpenAPIHono_ZodValidation',
matcher: (body) =>
body?.success === false &&
body?.error?.name === 'ZodError' &&
Array.isArray(body?.error?.issues),
tracker: (wideEvent, body) => {
wideEvent.trackError({
type: 'ValidationError',
message: 'Request validation failed',
validation: {
issueCount: body.error.issues.length,
fields: body.error.issues.map(issue => issue.path.join('.'))
},
unexpected: false // Expected - user submitted invalid data
});
}
}
```
#### createObservabilityErrorHandler(options?)
Error handler that integrates with observability tracking for exceptions that bypass normal response flow.
```typescript
import { observabilityMiddleware, createObservabilityErrorHandler } from '@delta-base/observability';
const app = new OpenAPIHono();
app.use('*', observabilityMiddleware());
app.onError(createObservabilityErrorHandler());
```
#### extractUserContext(request: Request)
Extract user context from HTTP request.
```typescript
const userContext = extractUserContext(c.req.raw);
if (userContext) {
tracker.trackUser(userContext);
}
```
### Utility Functions
#### withDomainTracking
Track domain operations with automatic span management.
```typescript
import { withDomainTracking } from '@delta-base/observability';
const result = await withDomainTracking(tracker, 'user', 'create', async () => {
// Domain logic here
return await createUser(data);
});
```
#### withEventStoreTracking
Track event store operations with automatic span management.
```typescript
import { withEventStoreTracking } from '@delta-base/observability';
const result = await withEventStoreTracking(tracker, 'user-123', 'append', async () => {
return await eventStore.append(streamId, events);
});
```
#### withValidationTracking
Track validation operations with automatic error handling and categorization.
```typescript
import { withValidationTracking } from '@delta-base/observability';
const validatedData = await withValidationTracking(tracker, 'input', async () => {
return await validateUserInput(data);
});
```
#### withExternalServiceTracking
Track external service calls with automatic timing.
```typescript
import { withExternalServiceTracking } from '@delta-base/observability';
const result = await withExternalServiceTracking(tracker, 'user-service', 'create', async () => {
return await userService.createUser(data);
});
```
#### withDatabaseTracking
Track database operations with automatic timing.
```typescript
import { withDatabaseTracking } from '@delta-base/observability';
const result = await withDatabaseTracking(tracker, 'INSERT', 'users', async () => {
return await db.insert(userData);
});
```
#### trackUserFromSources
Automatically extract and track user context from various sources.
```typescript
import { trackUserFromSources } from '@delta-base/observability';
trackUserFromSources(tracker, {
request: c.req.raw, // HTTP request
body: requestBody, // Request body
headers: customHeaders, // Custom headers
});
```
## Build Information System
The framework includes a comprehensive build information system that automatically collects and tracks build metadata:
### Automatic Build Info Collection
```bash
# Use the CLI tool to collect build info
npx collect-build-info --package-json-dir . --output-dir src --service-name my-service
# Or programmatically
import { collectBuildInfo } from '@delta-base/observability';
const buildInfo = collectBuildInfo({
packageJsonDir: process.cwd(),
outputDir: 'src',
defaultServiceName: 'my-service'
});
```
### Build Info in Package.json
Add the build info collection to your package.json:
```json
{
"scripts": {
"prebuild": "collect-build-info --package-json-dir . --output-dir src --service-name my-service",
"build": "npm run prebuild && your-build-command"
}
}
```
### Build Info Integration
The framework automatically includes build information in all wide events:
```typescript
import { initializeBuildInfo } from '@delta-base/observability';
// Initialize with your build info
initializeBuildInfo({
'service.name': 'my-service',
'service.version': '1.0.0',
'git.commit_hash': 'abc123',
'git.branch': 'main',
'build.timestamp': new Date().toISOString()
});
```
## Advanced Usage
### Error Categorization in Practice
The framework automatically categorizes errors, but you can also manually track errors with explicit categorization:
```typescript
const tracker = c.get('wideEvent');
try {
// Your operation
await performOperation();
} catch (error) {
if (error instanceof ValidationError) {
// Expected error - user input issue
tracker.trackError({
type: 'ValidationError',
message: error.message,
validation: {
issueCount: 1,
fields: ['email']
},
unexpected: false // Expected - user submitted invalid data
});
} else if (error instanceof DatabaseConnectionError) {
// Unexpected error - system issue
tracker.trackError({
type: 'DatabaseConnectionError',
message: error.message,
unexpected: true // Unexpected - infrastructure issue
});
}
throw error;
}
```
### Alerting Based on Error Categories
```typescript
// Example alerting logic
if (error.unexpected === true) {
await sendAlert({
severity: 'high',
message: `Unexpected error in ${operation}`,
error: error
});
} else {
// Expected errors might be tracked for user experience metrics
await trackUserExperienceMetric({
type: 'validation_error',
operation: operation,
error: error
});
}
```
### SLA and Error Budget Tracking
```typescript
// Example SLA tracking
const errorBudget = {
recordRequest: (success: boolean, unexpected: boolean) => {
if (!success && unexpected) {
// Only count unexpected errors against SLA
errorBudget.recordFailure();
} else {
errorBudget.recordSuccess();
}
}
};
// In your monitoring system
if (event.success === false && event.error?.unexpected === true) {
slaTracker.recordViolation();
}
```
### Custom Error Response Patterns
You can extend the error response pattern matching by adding custom patterns:
```typescript
// Example of adding custom error patterns
const customErrorPatterns = [
{
name: 'CustomBusinessError',
matcher: (body) =>
body?.error?.type === 'BUSINESS_RULE_VIOLATION' &&
body?.error?.code,
tracker: (wideEvent, body) => {
wideEvent.trackError({
type: 'BusinessRuleViolation',
message: body.error.message,
businessRule: {
rule: body.error.code,
violation: body.error.details
},
unexpected: false // Expected - business rule violation
});
}
}
];
```
### Creating Child Spans
```typescript
const validationSpan = tracker.createOperationSpan('validation', {
'operation.layer': 'validation'
});
try {
// Validation logic
validationSpan.setStatus({ code: SpanStatusCode.OK });
} catch (error) {
validationSpan.recordException(error);
} finally {
validationSpan.end();
}
```
### Comprehensive Route Example
```typescript
app.post('/users', async (c) => {
const tracker = c.get('wideEvent'); // From middleware
const body = c.req.valid('json');
try {
// Set operation and track user data being created
tracker
.setOperation('user.create', {
domain: 'user-management',
layer: 'api'
})
.trackUser({
email: body.email,
firstName: body.firstName,
lastName: body.lastName,
actorType: 'public'
});
// Use utility for domain tracking
const result = await withDomainTracking(tracker, 'user', 'create', async () => {
return await createUserHandler(eventStore, actor, body, tracker);
});
// Track created entity and infrastructure
tracker
.trackEntity({
type: 'user',
id: result.id,
status: 'created'
})
.trackInfrastructure({
eventStore: {
streamId: result.id,
expectedVersion: 0,
nextVersion: result.nextExpectedStreamVersion,
createdNewStream: true
}
});
return c.json({
success: true,
message: 'User created successfully',
user: result.user,
nextExpectedStreamVersion: result.nextExpectedStreamVersion
}, 201);
} catch (error) {
// Error tracking is automatic via middleware pattern matching
// But you can add specific context if needed
if (error instanceof IllegalStateError && error.message.includes('already exists')) {
tracker.trackError({
type: 'BusinessRuleViolation',
message: error.message,
businessRule: {
rule: 'unique_email',
violation: 'already_exists'
},
unexpected: false // Expected - business constraint violation
});
}
throw error;
}
});
```
## Output Format
The framework generates structured wide events with the following format:
```json
{
"request_id": "8bfdf7ecdd485694",
"timestamp": "2024-09-08T06:14:05.680Z",
"operation": "user.create",
"success": true,
"http.method": "POST",
"http.path": "/users",
"http.status_code": 201,
"user.email": "john@example.com",
"user.first_name": "John",
"user.last_name": "Doe",
"actor.type": "public",
"entity.type": "user",
"entity.id": "user-123",
"operation.domain": "user-management",
"operation.layer": "api",
"event_store.stream_id": "user-123",
"event_store.expected_version": 0,
"event_store.next_version": 1,
"event_store.created_new_stream": true,
"timing.domain_ms": 45,
"timing.event_store_ms": 67,
"timing.total_ms": 124,
"performance_category": "fast",
"error_category": null,
"spans": [
{
"name": "validation",
"duration_ms": 12,
"status": "ok"
},
{
"name": "domain",
"duration_ms": 45,
"status": "ok"
},
{
"name": "event_store",
"duration_ms": 67,
"status": "ok"
}
],
"service.name": "my-service",
"service.version": "1.0.0",
"git.commit_hash": "abc123",
"git.branch": "main",
"build.timestamp": "2024-09-08T06:14:05.680Z"
}
```
### Error Output Format
When errors occur, the framework adds comprehensive error context:
```json
{
"request_id": "8bfdf7ecdd485694",
"timestamp": "2024-09-08T06:14:05.680Z",
"operation": "user.create",
"success": false,
"http.method": "POST",
"http.path": "/users",
"http.status_code": 400,
"error.type": "ValidationError",
"error.message": "Request validation failed",
"error.unexpected": false,
"error.validation.issue_count": 2,
"error.validation.fields": ["email", "firstName"],
"user.email": "invalid-email",
"user.first_name": "John",
"actor.type": "public",
"operation.domain": "user-management",
"operation.layer": "api",
"timing.total_ms": 12,
"performance_category": "fast",
"error_category": "expected",
"service.name": "my-service",
"service.version": "1.0.0",
"git.commit_hash": "abc123",
"git.branch": "main",
"build.timestamp": "2024-09-08T06:14:05.680Z"
}
```
## Environment Variables
- `SERVICE_NAME` - Name of your service (used in spans and events)
- `SERVICE_VERSION` - Version of your service
- `ENVIRONMENT_NAME` - Environment (development, staging, production)
## Error Response Pattern Matching
The framework uses an extensible pattern matching system to intelligently detect and categorize different types of error responses:
```typescript
interface ErrorResponsePattern {
name: string; // Human-readable name
matcher: (body: any) => boolean; // Pattern matching function
tracker: (wideEvent: WideEventsWrapper, body: any) => void; // Error tracking function
}
```
Built-in patterns include:
- **OpenAPIHono ZodValidation**: Detects Zod validation errors from OpenAPIHono
- **Business Validation**: Detects business rule violations
- **Generic HTTP Errors**: Fallback for unmatched error responses
## Best Practices
### Framework Usage
1. **Use Middleware**: Always use `observabilityMiddleware()` for automatic setup and intelligent error detection
2. **Configure Early**: Configure observability at application startup before registering middleware
3. **Leverage Automatic Categorization**: Trust the framework's error categorization - it properly distinguishes expected vs unexpected errors
4. **Semantic Methods**: Use the semantic tracking methods (`trackUser`, `trackEntity`, etc.) instead of generic setters
5. **Utility Functions**: Use utility functions like `withDomainTracking` for common patterns
### Configuration
6. **Context Schema**: Customize the context schema for your domain - add fields that are important for your use case
7. **Error Patterns**: Add custom error patterns for domain-specific error types
8. **Environment Specific**: Use different configurations for different environments (development, staging, production)
9. **Performance Thresholds**: Adjust performance thresholds based on your service's characteristics
### Error Handling
10. **Error Context**: The middleware automatically adds error context, but you can add specific business context when needed
11. **Business Events**: Record significant business events with `recordEvent()`
12. **Infrastructure Details**: Track infrastructure operations for performance analysis
13. **Alerting Strategy**: Only alert on unexpected errors (`unexpected: true`)
14. **SLA Tracking**: Only count unexpected errors against service level agreements
15. **User Experience**: Track expected errors separately for UX insights
### Development Workflow
16. **Debug Mode**: Enable debug mode in development to see detailed logging
17. **Test Error Patterns**: Test your custom error patterns with different error scenarios
18. **Validate Context**: Regularly review the context fields being tracked to ensure they're useful
19. **Monitor Performance**: Use the performance categorization to identify slow operations
## Extending the Framework
### Creating Custom Tracking Utilities
You can create domain-specific utilities that wrap the framework's functionality:
```typescript
// Custom utility for tracking database operations
export async function withDatabaseQuery<T>(
tracker: WideEventsWrapper | undefined,
query: string,
table: string,
fn: () => Promise<T>
): Promise<T> {
if (!tracker) return await fn();
return await withDatabaseTracking(tracker, 'SELECT', table, async () => {
tracker.recordEvent('database.query.started', { query, table });
const result = await fn();
tracker.recordEvent('database.query.completed', { query, table });
return result;
});
}
// Custom utility for tracking external API calls
export async function withExternalAPI<T>(
tracker: WideEventsWrapper | undefined,
apiName: string,
endpoint: string,
fn: () => Promise<T>
): Promise<T> {
if (!tracker) return await fn();
return await withExternalServiceTracking(tracker, apiName, endpoint, async () => {
tracker.recordEvent('external_api.call.started', { api: apiName, endpoint });
const result = await fn();
tracker.recordEvent('external_api.call.completed', { api: apiName, endpoint });
return result;
});
}
```
### Domain-Specific Configuration Presets
Create configuration presets for different types of services:
```typescript
// User management service configuration
export const userServiceConfig = {
contextSchema: {
user: ['id', 'email', 'role', 'organization_id'],
operation: ['type', 'domain', 'layer', 'user_id'],
infrastructure: ['database.operation', 'cache.hit_rate'],
error: ['type', 'message', 'unexpected', 'validation_error']
},
performance: {
performanceThresholds: {
fast: 100,
slow: 1000
}
}
};
// API gateway service configuration
export const apiGatewayServiceConfig = {
contextSchema: {
user: ['id', 'email', 'role', 'organization_id'],
operation: ['type', 'domain', 'layer', 'route', 'method'],
infrastructure: ['upstream.service', 'upstream.duration_ms'],
error: ['type', 'message', 'unexpected', 'auth_failure', 'rate_limit']
},
performance: {
performanceThresholds: {
fast: 100,
slow: 2000 // API calls can be slower
}
}
};
// Apply preset configuration
configureObservabilityAdvanced(userServiceConfig);
```
## Troubleshooting
### Common Issues
#### Context Fields Not Appearing
```typescript
// Problem: Custom context fields not showing in wide events
// Solution: Ensure fields are added to the context schema
addContextFields('user', ['custom_field']);
// Or check if the field name matches what you're tracking
tracker.trackUser({ customField: 'value' }); // This becomes 'user.custom_field'
```
#### Error Patterns Not Matching
```typescript
// Problem: Custom error patterns not being detected
// Solution: Add debug logging to test your matcher
addErrorPattern({
name: 'CustomError',
matcher: (body) => {
console.log('Testing error pattern:', body); // Debug logging
return body?.error?.type === 'CUSTOM_ERROR';
},
tracker: (wideEvent, body) => {
// ... tracker logic
}
});
```
#### Performance Issues
```typescript
// Problem: Observability causing performance degradation
// Solution: Disable expensive features in production
configureObservabilityAdvanced({
http: {
trackBodies: false, // Disable body tracking
maxBodySize: 1024 // Limit body size
},
performance: {
autoCreateSpans: false // Disable automatic spans
}
});
```
### Debug Mode
Enable debug mode to see detailed information about what the framework is doing:
```typescript
configureObservability({
debug: true
});
// You'll see console output like:
// [Observability] Detected OpenAPIHono_ZodValidation error response, tracking...
// [Observability] Extracted business context: user.id, user.email, operation.type
// [Observability] Wide event emitted: {...}
```
## Summary of Features
This enhanced observability framework provides:
### ✅ **Configurable Context Schema**
- No more hard-coded context fields
- Customize what fields are tracked for your domain
- Add fields incrementally with `addContextFields()`
### ✅ **Pluggable Error Patterns**
- Extend automatic error detection
- Domain-specific error categorization
- Proper expected vs unexpected error handling
### ✅ **Comprehensive Configuration**
- Simple and advanced configuration options
- Environment-specific settings
- Performance tuning options
### ✅ **Build Information Integration**
- Automatic build info collection from git and package.json
- CLI tool for build pipeline integration
- Comprehensive metadata tracking
### ✅ **Developer Experience**
- Clear TypeScript interfaces
- Extensive documentation and examples
- Debug mode for troubleshooting
### ✅ **Production Ready**
- Comprehensive error handling
- Performance optimizations
- Environment-specific configurations
This framework provides a comprehensive, configurable observability solution that can be adapted to any domain while maintaining high-quality error categorization and debugging capabilities.