Crash Reporting & Telemetry Buffering0%

Crash Reporting & Telemetry Buffering

Beginner12 min read•Updated: Jul 11, 2026
Study Materials

Crash Reporting & Telemetry Buffering in Modern JavaScript

Transmitting analytics events and crash telemetry to remote monitoring servers on every single user interaction creates high network overhead and drains mobile batteries. Conversely, sending telemetry when the user closes a tab often fails because standard fetch() calls are abruptly aborted. Telemetry Buffering and navigator.sendBeacon() solve both problems.


1. The Page Unload Problem

Historically, developers attempted to send telemetry during window.onbeforeunload or window.onunload:

JavaScript
// ANTI-PATTERN: Fails on modern browsers!
window.addEventListener('unload', () => {
// fetch() is CANCELLED immediately when the tab closes!
fetch('/analytics', { method: 'POST', body: JSON.stringify(metrics) });
});

Because the tab is being terminated, the browser terminates all active HTTP connections, and the telemetry is lost.


2. The Solution: navigator.sendBeacon()

navigator.sendBeacon(url, data) sends small amounts of data asynchronously over HTTP POST. The browser guarantees that the data is transmitted to the server, even after the page has finished closing!

JavaScript
window.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const payload = JSON.stringify(bufferedEvents);
const blob = new Blob([payload], { type: 'application/json' });
 
// Browser schedules background transmission that survives tab closing!
const success = navigator.sendBeacon('/api/telemetry', blob);
if (success) {
bufferedEvents = []; // Cleared successfully
}
}
});
Use the visibilitychange event rather than unload or beforeunload. Mobile browsers frequently suspend background tabs without firing unload!

3. The fetch() with keepalive: true Alternative

Modern Fetch API supports keepalive: true, which keeps the HTTP request alive after the page is discarded:

JavaScript
function sendFinalAnalytics(data) {
fetch('/api/telemetry', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
keepalive: true // Keeps connection alive even if tab closes!
});
}

4. Implementing a Production Telemetry Buffer

To avoid spamming analytics endpoints with hundreds of tiny HTTP calls, buffer events in memory and flush them in batches based on buffer size or a periodic timer:

JavaScript
class TelemetryBuffer {
constructor(endpoint, maxBufferSize = 20, flushIntervalMs = 5000) {
this.endpoint = endpoint;
this.maxBufferSize = maxBufferSize;
this.flushIntervalMs = flushIntervalMs;
this.buffer = [];
 
// Periodic flush timer
this.timer = setInterval(() => this.flush(), this.flushIntervalMs);
 
// Flush on page hidden / unload
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
this.flush();
}
});
}
 
track(eventName, properties = {}) {
this.buffer.push({
event: eventName,
properties,
timestamp: Date.now()
});
 
// Flush immediately if buffer reaches capacity threshold
if (this.buffer.length >= this.maxBufferSize) {
this.flush();
}
}
 
flush() {
if (this.buffer.length === 0) return;
 
const payload = JSON.stringify(this.buffer);
this.buffer = []; // Clear buffer immediately to prevent duplicate sends
 
// Attempt sendBeacon first, fallback to fetch with keepalive
if (navigator.sendBeacon) {
const blob = new Blob([payload], { type: 'application/json' });
navigator.sendBeacon(this.endpoint, blob);
} else {
fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: payload,
keepalive: true
}).catch(err => console.warn('Telemetry flush failed:', err));
}
}
}
 
// Global instance
export const analytics = new TelemetryBuffer('/api/v1/telemetry');

Interactive Quiz

Practice Quiz

Question 1
Why do standard fetch() requests often fail when initiated inside a window 'unload' event?
Question 2
What is the primary guarantee of navigator.sendBeacon(url, data)?
Question 3
Which page lifecycle event is the recommended modern replacement for 'unload' and 'beforeunload' when saving state and flushing telemetry?
Question 4
What fetch() option keeps an HTTP request alive after a page unloads, serving as an alternative to sendBeacon?
Question 5
What is the primary operational benefit of Telemetry Buffering?
Next Lesson

Proxy & Reflect API Deep Dive

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext