@xcelsior/monitoring
v2.2.0
Published
Sentry-native error tracking and observability helpers (jobs, slow-query, metrics, spans).
Keywords
Readme
@xcelsior/monitoring
Sentry-native error tracking and observability helpers for Xcelsior applications. Ships helpers for job lifecycle and slow-query detection. (Uptime monitoring and webhook logging live elsewhere — Betterstack and Strapi respectively.)
Set
SENTRY_DSNto enable.Monitor.fromEnv()builds a Monitor fromSENTRY_DSN,SENTRY_TRACES_SAMPLE_RATE,SENTRY_RELEASE,POWERTOOLS_SERVICE_NAME,APP_ENV.
API surface
The Monitor class wraps @sentry/node with a small, opinionated surface:
| Method | Sentry call |
|---|---|
| captureException(err, ctx?) | Sentry.captureException |
| captureMessage(msg, ctx?) | Sentry.captureMessage |
| addBreadcrumb({ category, message, data }) | Sentry.addBreadcrumb |
| startSpan(name, fn, { op, attributes }) | Sentry.startSpan |
| metricsCount(name, value, { unit, attributes }) | Sentry.metrics.count |
| metricsGauge(name, value, …) | Sentry.metrics.gauge |
| metricsDistribution(name, value, …) | Sentry.metrics.distribution |
| wrapHandler(lambda) | @sentry/aws-serverless.wrapHandler |
| flush(timeoutMs?) | Sentry.flush |
Sentry's metrics API is gated behind
_experiments.enableMetrics: true, which is set automatically.
Helpers
| Helper | Emits |
|---|---|
| runJob({ name, monitor, attributes }, fn) | startSpan + metric: job.duration (distribution, ms) + metric: job.run (count, with status). On throw, captureException with jobName tag. |
| createKnexSlowQueryListener({ monitor, thresholdMs }) | Per query: metric: db.query.duration. Above threshold: metric: db.slow_query + breadcrumb. |
No sinks — Sentry's dashboards are the storage and aggregation layer.
Installation
pnpm add @xcelsior/monitoringFeatures
Provider Support
Choose between Sentry and Rollbar for error tracking and monitoring:
import { Monitor } from '@xcelsior/monitoring';
// Using Sentry
const sentryMonitor = new Monitor({
provider: 'sentry',
service: 'user-service',
environment: 'production',
sentry: {
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 0.1,
},
});
// Using Rollbar
const rollbarMonitor = new Monitor({
provider: 'rollbar',
service: 'user-service',
environment: 'production',
rollbar: {
accessToken: process.env.ROLLBAR_ACCESS_TOKEN,
},
});Error Tracking
Capture and track application errors:
// Capture error
monitor.captureException(new Error('Something went wrong'), {
user: { id: '123' },
tags: { component: 'auth' },
});
// Capture message
monitor.captureMessage('Important event occurred', {
level: 'info',
extra: { details: 'some details' },
});Performance Monitoring
Track application performance:
// Track function execution time
const result = await monitor.trackPerformance(
'database-query',
async () => {
return await db.query();
}
);
// Manual performance tracking
const span = monitor.startSpan('api-request');
try {
await apiCall();
} finally {
span.finish();
}Health Checks
Monitor service health:
const health = monitor.healthCheck({
database: async () => {
await db.ping();
return { status: 'healthy' };
},
redis: async () => {
await redis.ping();
return { status: 'healthy' };
},
});
// Get health status
const status = await health.check();Configuration
Monitor Options
interface MonitoringConfig {
provider: 'sentry' | 'rollbar';
service: string;
environment: string;
release?: string;
sampleRate?: number;
ignoreErrors?: (string | RegExp)[];
enabled?: boolean;
// Sentry-specific configuration
sentry?: {
dsn: string;
tracesSampleRate?: number;
profilesSampleRate?: number;
};
// Rollbar-specific configuration
rollbar?: {
accessToken: string;
captureUncaught?: boolean;
captureUnhandledRejections?: boolean;
};
}Environment Variables
For Sentry:
SENTRY_DSN=your_sentry_dsn_hereFor Rollbar:
ROLLBAR_ACCESS_TOKEN=your_rollbar_access_token_hereIntegration Options
// Express integration
app.use(monitor.expressHandler());
// Lambda integration
export const handler = monitor.wrapLambda(async (event) => {
// Your handler logic
});
// Custom integration
monitor.addIntegration({
name: 'custom',
setup: (client) => {
// Setup logic
},
});Advanced Usage
Custom Context
Add custom context to all events:
monitor.setContext('user', {
id: '123',
email: '[email protected]',
});
monitor.setTag('region', 'us-east-1');
monitor.setExtra('deploymentInfo', {
version: '1.0.0',
timestamp: Date.now(),
});Breadcrumbs
Track application flow:
monitor.addBreadcrumb({
category: 'auth',
message: 'User login attempt',
level: 'info',
data: { userId: '123' },
});Transaction Monitoring
Track complex operations:
const transaction = monitor.startTransaction('order-process');
try {
const span1 = transaction.startSpan('validate-cart');
await validateCart();
span1.finish();
const span2 = transaction.startSpan('process-payment');
await processPayment();
span2.finish();
transaction.finish();
} catch (error) {
transaction.finish(error);
throw error;
}Best Practices
Error Handling
try {
await riskyOperation();
} catch (error) {
monitor.captureError(error, {
tags: { operation: 'riskyOperation' },
extra: { input: input },
user: { id: userId },
});
throw error;
}Performance Tracking
const middleware = async (req, res, next) => {
const transaction = monitor.startTransaction(req.path);
res.on('finish', () => {
transaction.finish({
status: res.statusCode,
duration: Date.now() - req.startTime,
});
});
next();
};Custom Metrics
monitor.trackMetric('queue_size', 10);
monitor.trackMetric('response_time', 150, {
tags: { endpoint: '/api/users' },
});Migration from Legacy API
The package maintains backward compatibility with the legacy captureException function:
// Legacy API (still supported)
import { captureException } from '@xcelsior/monitoring';
captureException({
err: new Error('Something went wrong'),
logLevel: 'error',
tags: { component: 'auth' },
});However, we recommend migrating to the new Monitor class for better functionality and provider flexibility.
License
MIT
