In-memory rate limiter for Sentry error reporting to prevent duplicate error spam and avoid draining your error log quota.
While Sentry has spike protection that limits errors to ~500 per hour, it doesn't make sense to receive hundreds of logs for a single error on the backend. This library ensures that each unique error is only reported a few times within a configurable time window, dramatically reducing quota usage while still capturing enough information to debug issues.
- 🚀 Zero dependencies (except peer dependency on
@sentry/node) - 📦 TypeScript support with full type definitions
- 🎯 Smart fingerprinting based on error type, message, and stack trace
- ⏱️ Configurable time windows (hourly, daily, or custom)
- 🧹 Automatic memory cleanup to prevent memory leaks
- 📊 Statistics API to monitor rate limiting behavior
npm install @synapsmedia/sentry-rate-limiteror
yarn add @synapsmedia/sentry-rate-limiterPeer dependency: Requires @sentry/node v7.x or v8.x
import * as Sentry from "@sentry/node";
import { SentryRateLimiter, createBeforeSend } from "@synapsmedia/sentry-rate-limiter";
// Create rate limiter instance
const rateLimiter = new SentryRateLimiter({
maxReportsPerWindow: 5, // Allow max 5 reports per window
windowHours: 1, // 1 hour window
});
// Initialize Sentry with rate limiting
Sentry.init({
dsn: "your-sentry-dsn",
beforeSend: createBeforeSend(rateLimiter),
// ... other Sentry options
});const rateLimiter = new SentryRateLimiter({
maxReportsPerWindow: 3, // Max 3 reports per window
windowHours: 24, // 24 hour window
cleanupIntervalMinutes: 30, // Run cleanup every 30 minutes
});If you need more control over the beforeSend hook:
import { SentryRateLimiter } from "@synapsmedia/sentry-rate-limiter";
const rateLimiter = new SentryRateLimiter();
Sentry.init({
dsn: "your-sentry-dsn",
beforeSend(event) {
// Your custom logic here
// Apply rate limiting
if (!rateLimiter.shouldReport(event)) {
return null; // Drop the event
}
// More custom logic
return event;
},
});// Get current rate limiting statistics
const stats = rateLimiter.getStats();
console.log(`Tracking ${stats.trackedErrors} unique errors`);
stats.errors.forEach(error => {
console.log(`Error ${error.fingerprint}:`);
console.log(` Count: ${error.count}`);
console.log(` First seen: ${error.firstSeen}`);
console.log(` Last seen: ${error.lastSeen}`);
});If you need to manually destroy the rate limiter (e.g., in tests):
rateLimiter.destroy();-
Fingerprinting: Each error gets a unique SHA256 fingerprint based on:
- Error type (e.g.,
TypeError,ReferenceError) - Error message
- Top 3 stack frames (file and line number)
- Error type (e.g.,
-
Rate Limiting:
- First occurrence of an error is always reported
- Subsequent identical errors are counted within the time window
- Once the limit is reached, additional occurrences are dropped
- After the window expires, the counter resets
-
Memory Management:
- Automatic cleanup runs periodically to remove stale entries
- Prevents memory leaks in long-running applications
| Option | Type | Default | Description |
|---|---|---|---|
maxReportsPerWindow |
number |
5 |
Maximum number of reports allowed per unique error within the time window |
windowHours |
number |
1 |
Time window in hours for rate limiting |
cleanupIntervalMinutes |
number |
15 |
How often to run cleanup of stale entries (in minutes) |
// Only allow 2 reports per error per day
const rateLimiter = new SentryRateLimiter({
maxReportsPerWindow: 2,
windowHours: 24,
});// Allow up to 10 reports per error per hour
const rateLimiter = new SentryRateLimiter({
maxReportsPerWindow: 10,
windowHours: 1,
});const rateLimiter = new SentryRateLimiter({
maxReportsPerWindow: process.env.NODE_ENV === 'production' ? 3 : 100,
windowHours: process.env.NODE_ENV === 'production' ? 24 : 1,
});This library is written in TypeScript and includes full type definitions. All types are exported:
import type {
SentryRateLimiter,
SentryRateLimiterOptions,
ErrorRateLimit
} from "@synapsmedia/sentry-rate-limiter";MIT
Contributions are welcome! Please feel free to submit a Pull Request.
For issues and questions, please open an issue on GitHub.