IntegrationAdvanced Usage
Advanced Usage
beforeSend Hook Patterns
Basic Pattern Matching
const noiseGateFilter = async (event: Sentry.Event): Promise<Sentry.Event | null> => {
const patterns = await getPatterns();
const message = event.exception?.values?.[0]?.value || "";
for (const p of patterns) {
if (p.type === "regex") {
if (new RegExp(p.pattern).test(message)) return null;
} else {
if (message.includes(p.pattern)) return null;
}
}
return event;
};Match Against Stack Traces
const noiseGateFilter = async (event: Sentry.Event): Promise<Sentry.Event | null> => {
const patterns = await getPatterns();
// Check error message
const message = event.exception?.values?.[0]?.value || "";
// Check stack frames
const frames = event.exception?.values?.[0]?.stacktrace?.frames || [];
const stackText = frames.map(f => `${f.filename}:${f.function}`).join("\n");
const textToCheck = `${message}\n${stackText}`;
for (const p of patterns) {
const matches = p.type === "regex"
? new RegExp(p.pattern).test(textToCheck)
: textToCheck.includes(p.pattern);
if (matches) return null;
}
return event;
};Combine with Existing beforeSend
const existingBeforeSend = (event: Sentry.Event) => {
// Your existing logic
event.tags = { ...event.tags, app_version: "1.0.0" };
return event;
};
Sentry.init({
dsn: "...",
beforeSend: async (event) => {
// Run NoiseGate filter first
const filtered = await noiseGateFilter(event);
if (!filtered) return null;
// Then run existing logic
return existingBeforeSend(filtered);
},
});Caching Strategies
In-Memory Cache (Recommended)
class PatternCache {
private patterns: Pattern[] | null = null;
private expiry = 0;
private pending: Promise<Pattern[]> | null = null;
async get(): Promise<Pattern[]> {
// Return cached if valid
if (this.patterns && Date.now() < this.expiry) {
return this.patterns;
}
// Deduplicate concurrent requests
if (this.pending) {
return this.pending;
}
this.pending = this.fetch();
const patterns = await this.pending;
this.pending = null;
return patterns;
}
private async fetch(): Promise<Pattern[]> {
const response = await fetch("https://api.noisegate.dev/v1/filters", {
headers: { "Authorization": `Bearer ${API_KEY}` }
});
const data = await response.json();
this.patterns = data.patterns;
this.expiry = Date.now() + 5 * 60 * 1000; // 5 minutes
return this.patterns;
}
}
const cache = new PatternCache();localStorage Cache (Browser)
const CACHE_KEY = "noisegate_patterns";
const CACHE_TTL = 5 * 60 * 1000;
async function getPatterns(): Promise<Pattern[]> {
// Check localStorage
const cached = localStorage.getItem(CACHE_KEY);
if (cached) {
const { patterns, expiry } = JSON.parse(cached);
if (Date.now() < expiry) {
return patterns;
}
}
// Fetch fresh
const response = await fetch("https://api.noisegate.dev/v1/filters", {
headers: { "Authorization": `Bearer ${API_KEY}` }
});
const data = await response.json();
// Store in localStorage
localStorage.setItem(CACHE_KEY, JSON.stringify({
patterns: data.patterns,
expiry: Date.now() + CACHE_TTL
}));
return data.patterns;
}Background Refresh
class PatternCache {
private patterns: Pattern[] = [];
private refreshInterval: number;
constructor() {
this.refresh(); // Initial fetch
this.refreshInterval = setInterval(() => this.refresh(), 5 * 60 * 1000);
}
private async refresh() {
try {
const response = await fetch("https://api.noisegate.dev/v1/filters", {
headers: { "Authorization": `Bearer ${API_KEY}` }
});
const data = await response.json();
this.patterns = data.patterns;
} catch {
// Keep existing patterns on error
}
}
get(): Pattern[] {
return this.patterns; // Always synchronous
}
destroy() {
clearInterval(this.refreshInterval);
}
}Fallback Handling
Graceful Degradation
const noiseGateFilter = async (event: Sentry.Event): Promise<Sentry.Event | null> => {
try {
const patterns = await getPatterns();
// ... filtering logic
return shouldFilter ? null : event;
} catch (error) {
// Always send event if NoiseGate fails
console.warn("NoiseGate unavailable, sending unfiltered event");
return event;
}
};Fallback Patterns
const FALLBACK_PATTERNS: Pattern[] = [
{ pattern: "ResizeObserver loop", type: "string" },
{ pattern: "Script error", type: "string" },
{ pattern: "ChunkLoadError", type: "string" },
];
async function getPatterns(): Promise<Pattern[]> {
try {
const response = await fetch("https://api.noisegate.dev/v1/filters", {
headers: { "Authorization": `Bearer ${API_KEY}` },
signal: AbortSignal.timeout(3000), // 3s timeout
});
if (!response.ok) throw new Error("API error");
const data = await response.json();
return data.patterns;
} catch {
return FALLBACK_PATTERNS;
}
}Timeout with Cached Fallback
async function getPatterns(): Promise<Pattern[]> {
const cached = getCachedPatterns(); // From localStorage or memory
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 2000);
const response = await fetch("https://api.noisegate.dev/v1/filters", {
headers: { "Authorization": `Bearer ${API_KEY}` },
signal: controller.signal,
});
clearTimeout(timeout);
const data = await response.json();
setCachedPatterns(data.patterns);
return data.patterns;
} catch {
// Return stale cache if available, empty array otherwise
return cached || [];
}
}Performance Tips
1. Prefetch on App Load
// Fetch patterns before any errors occur
if (typeof window !== "undefined") {
getPatterns(); // Fire and forget
}2. Use Synchronous Fallback
Sentry.init({
dsn: "...",
beforeSend: (event) => {
// Synchronous check against cached patterns
const patterns = cache.get(); // Returns immediately
if (patterns.length === 0) return event; // No patterns yet
const message = event.exception?.values?.[0]?.value || "";
const shouldFilter = patterns.some(p =>
p.type === "regex"
? new RegExp(p.pattern).test(message)
: message.includes(p.pattern)
);
return shouldFilter ? null : event;
},
});3. Batch Pattern Compilation
class CompiledPatterns {
private regexPatterns: RegExp[] = [];
private stringPatterns: string[] = [];
load(patterns: Pattern[]) {
this.regexPatterns = patterns
.filter(p => p.type === "regex")
.map(p => new RegExp(p.pattern));
this.stringPatterns = patterns
.filter(p => p.type === "string")
.map(p => p.pattern);
}
matches(text: string): boolean {
return (
this.stringPatterns.some(p => text.includes(p)) ||
this.regexPatterns.some(p => p.test(text))
);
}
}Type Definitions
interface Pattern {
id: string;
pattern: string;
type: "string" | "regex";
category: string;
confidence: number;
source: string;
}
interface FiltersResponse {
version: string;
patterns: Pattern[];
}