@dhrupad-sah/exception-catcher
v1.1.1
Published
Automatically catch and report exceptions to Mira Sentinel with rich context and Loki log integration
Maintainers
Readme
@dhrupad-sah/exception-catcher
Automatically catch and report exceptions to Mira Sentinel with rich context and Loki log integration for AI-powered debugging and automatic fix generation.
Features
- 🚨 Automatic Exception Catching - Monitors uncaught exceptions and unhandled rejections
- 📊 Rich Context - Collects system info, memory usage, and custom context
- 🔍 Loki Integration - Correlates exceptions with log data for enhanced debugging
- 🤖 AI-Powered Fixes - Integrates with Claude Code for automatic issue resolution
- 📋 GitHub Integration - Automatically creates issues and pull requests
- 🔄 Retry Logic - Robust error reporting with configurable retries
- 🎯 Flexible Filtering - Custom error filtering and context enrichment
Installation
npm install @dhrupad-sah/exception-catcherQuick Start
Fastify Integration (Recommended for APIs)
import Fastify from 'fastify'
import { fastifyMiraSentinel } from '@dhrupad-sah/exception-catcher'
const fastify = Fastify({ logger: true })
// Register the plugin - automatically handles all HTTP route exceptions
await fastify.register(fastifyMiraSentinel, {
sentinelUrl: process.env.MIRA_SENTINEL_URL!,
serviceName: 'api-service',
repo: 'company/api-service',
// Optional: Skip client errors
skipStatusCodes: [400, 401, 403, 404],
// Optional: Extract custom context
extractRequestContext: (request) => ({
userId: request.headers['x-user-id'],
traceId: request.headers['x-trace-id']
})
})
// Your routes - exceptions are automatically caught and reported
fastify.get('/users/:id', async (request, reply) => {
const user = await getUser(request.params.id) // Any error here is caught
return user
})
await fastify.listen({ port: 3000 })Basic Usage (Non-Fastify Apps)
import { MiraSentinelExceptionCatcher } from '@dhrupad-sah/exception-catcher'
const catcher = new MiraSentinelExceptionCatcher({
sentinelUrl: 'https://your-sentinel-instance.com',
serviceName: 'api-service',
repo: 'company/api-service'
})
// Initialize and start monitoring
catcher.initialize()
// That's it! All exceptions are now automatically caught and reportedEnvironment-Based Auto-Initialization
import { autoInitialize } from '@dhrupad-sah/exception-catcher'
// Set environment variables:
// MIRA_SENTINEL_URL=https://your-sentinel-instance.com
// MIRA_SERVICE_NAME=api-service
// MIRA_REPO=company/api-service
const catcher = autoInitialize()
// Automatically initializes if environment variables are setAdvanced Configuration
import { MiraSentinelExceptionCatcher } from '@dhrupad-sah/exception-catcher'
const catcher = new MiraSentinelExceptionCatcher({
sentinelUrl: 'https://your-sentinel-instance.com',
serviceName: 'api-service',
repo: 'company/api-service',
apiKey: 'your-api-key', // Optional authentication
// Custom error filtering
shouldCatchError: (error) => {
// Skip test errors
return !error.message.includes('test')
},
// Enrich context with custom data
enrichContext: (error, context) => {
return {
userId: getCurrentUserId(),
requestId: getCurrentRequestId(),
version: process.env.APP_VERSION
}
},
// Configure retries
retry: {
attempts: 5,
delay: 2000
}
})
catcher.initialize()Manual Exception Reporting
try {
// Some risky operation
await processPayment(paymentData)
} catch (error) {
// Manually report with additional context
await catcher.reportException(error, {
context: {
paymentId: paymentData.id,
userId: paymentData.userId,
amount: paymentData.amount
},
tags: ['payment', 'critical'],
severity: 'high'
})
throw error // Re-throw if needed
}Event Handling
catcher.on('exception-caught', (error, context) => {
console.log('Exception caught:', error.message)
})
catcher.on('exception-sent', (context, response) => {
console.log('Exception reported:', response.jobId)
})
catcher.on('exception-failed', (error, context, reason) => {
console.error('Failed to report exception:', reason)
})Integration with Express.js
import express from 'express'
import { MiraSentinelExceptionCatcher } from '@dhrupad-sah/exception-catcher'
const app = express()
const catcher = new MiraSentinelExceptionCatcher({
sentinelUrl: process.env.MIRA_SENTINEL_URL!,
serviceName: 'express-api',
repo: 'company/express-api',
// Enrich with Express context
enrichContext: (error, context) => {
const req = (error as any).req
return req ? {
method: req.method,
url: req.url,
headers: req.headers,
userAgent: req.get('User-Agent'),
ip: req.ip
} : {}
}
})
catcher.initialize()
// Your routes here
app.get('/api/users', async (req, res) => {
try {
const users = await getUsers()
res.json(users)
} catch (error) {
// Attach request context to error
;(error as any).req = req
// Report with additional context
await catcher.reportException(error as Error, {
context: {
endpoint: '/api/users',
method: req.method,
query: req.query
},
tags: ['api', 'users']
})
res.status(500).json({ error: 'Internal server error' })
}
})
app.listen(3000)Integration with Fastify
Option 1: Fastify Plugin (Recommended)
import Fastify from 'fastify'
import { fastifyMiraSentinel } from '@dhrupad-sah/exception-catcher'
const fastify = Fastify({ logger: true })
// Register the plugin - automatically handles all HTTP route exceptions
await fastify.register(fastifyMiraSentinel, {
sentinelUrl: process.env.MIRA_SENTINEL_URL!,
serviceName: 'fastify-api',
repo: 'company/fastify-api',
// Optional configurations
includeHeaders: true,
includeBody: false, // Be careful with sensitive data
skipStatusCodes: [400, 401, 403, 404], // Don't report client errors
// Custom request context extraction
extractRequestContext: (request) => ({
userId: request.headers['x-user-id'],
tenantId: request.headers['x-tenant-id'],
traceId: request.headers['x-trace-id']
})
})
// Your routes - exceptions are automatically caught and reported
fastify.get('/users/:id', async (request, reply) => {
const user = await getUser(request.params.id) // Any error here is caught
return user
})
// Manual exception reporting is also available
fastify.get('/manual-report', async (request, reply) => {
try {
await riskyOperation()
} catch (error) {
await fastify.miraSentinel.reportException(error, {
context: { operation: 'risky' }
})
throw error // Re-throw to send HTTP error response
}
})
await fastify.listen({ port: 3000 })Option 2: Manual Integration
import Fastify from 'fastify'
import { MiraSentinelExceptionCatcher } from '@dhrupad-sah/exception-catcher'
const fastify = Fastify({ logger: true })
const catcher = new MiraSentinelExceptionCatcher({
sentinelUrl: process.env.MIRA_SENTINEL_URL!,
serviceName: 'fastify-api',
repo: 'company/fastify-api'
})
catcher.initialize()
// Global error handler
fastify.setErrorHandler(async (error, request, reply) => {
await catcher.reportException(error, {
context: {
method: request.method,
url: request.url,
params: request.params,
query: request.query,
headers: request.headers
},
tags: ['fastify', 'api-error']
})
reply.status(500).send({ error: 'Internal Server Error' })
})
fastify.listen({ port: 3000 })Environment Variables
| Variable | Description | Required |
|----------|-------------|----------|
| MIRA_SENTINEL_URL | URL of your Mira Sentinel instance | Yes |
| MIRA_SERVICE_NAME | Name of your service | Yes |
| MIRA_REPO | GitHub repository (owner/repo) | Yes |
| MIRA_API_KEY | API key for authentication | No |
| MIRA_ENABLED | Enable/disable (default: true) | No |
Configuration Options
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| sentinelUrl | string | Mira Sentinel instance URL | Required |
| serviceName | string | Service name for Loki correlation | Required |
| repo | string | GitHub repository (owner/repo) | Required |
| apiKey | string | Optional API key | undefined |
| enabled | boolean | Enable/disable catching | true |
| timeout | number | HTTP timeout (ms) | 10000 |
| shouldCatchError | function | Custom error filter | () => true |
| enrichContext | function | Custom context enrichment | () => ({}) |
| retry.attempts | number | Retry attempts | 3 |
| retry.delay | number | Retry delay (ms) | 1000 |
How It Works
- Exception Occurs - Your service throws an exception
- Context Collection - Rich context is automatically collected:
- Error message and stack trace
- System information (Node.js version, memory, CPU)
- Timestamp for Loki correlation
- Custom context from your application
- Sent to Mira Sentinel - Exception data is sent to your Sentinel instance
- Loki Integration - Sentinel queries Loki logs around the exception time
- AI Analysis - Claude Code analyzes the exception + log context
- GitHub Integration - Issue and PR are automatically created
- Timeline Analysis - Full timeline of events leading to the exception
Best Practices
1. Service Naming
Use consistent service names that match your Loki log labels:
// Good - matches Loki service label
serviceName: 'api-gateway'
// Bad - doesn't match logs
serviceName: 'my-awesome-service'2. Error Filtering
Filter out noise to focus on actionable exceptions:
shouldCatchError: (error) => {
// Skip test environments
if (process.env.NODE_ENV === 'test') return false
// Skip known non-critical errors
if (error.message.includes('ECONNRESET')) return false
// Skip client errors (4xx)
if (error.name === 'ValidationError') return false
return true
}3. Context Enrichment
Add meaningful context for better debugging:
enrichContext: (error, context) => {
return {
// Business context
tenantId: getCurrentTenant(),
feature: getCurrentFeature(),
// Technical context
version: process.env.APP_VERSION,
deployment: process.env.DEPLOYMENT_ID,
// Performance context
responseTime: getResponseTime(),
queueSize: getQueueSize()
}
}4. Graceful Shutdown
Always clean up on process exit:
const catcher = new MiraSentinelExceptionCatcher(config)
catcher.initialize()
process.on('SIGTERM', () => {
catcher.shutdown()
process.exit(0)
})Testing
Test your integration:
const catcher = new MiraSentinelExceptionCatcher(config)
catcher.initialize()
// Test connection
const isConnected = await catcher.testConnection()
console.log('Connected:', isConnected)
// Test exception reporting
await catcher.reportException(new Error('Test error'), {
context: { test: true },
tags: ['test']
})License
MIT
Support
For support, please create an issue in the GitHub repository.
