How to Build a Comprehensive Front-End Performance Monitoring System
This article explains how to create a front‑end monitoring client that captures global JavaScript errors, tracks resource loading and page performance metrics, intercepts AJAX and fetch requests, gathers PV/UV statistics, and reports data using various strategies such as image beacons, POST, and IndexedDB storage.
Introduction
Page performance optimization is essential for front‑end development, especially as pages become larger and interactions more complex. A continuous front‑end monitoring system helps quickly locate performance bottlenecks and guide developers to optimize.
Error Capture
The most basic part of a monitoring system is global error capture. By registering a global window.onerror handler you can obtain the error message, source file, line, column and the Error object.
window.onerror = function(message, source, lineno, colno, error) {
// handle error
};Static resource loading errors (JS, CSS, images, video, audio) can be caught with:
window.addEventListener('error', event => {
// errorTarget = event.target
}, true);Promise rejections are captured via unhandledrejection:
window.addEventListener('unhandledrejection', event => {
// event.reason contains the rejection reason
});Note that unhandledrejection has poor compatibility on many platforms (see compatibility chart).
Resource Monitoring
Beyond error counting, the loading speed of resources must be monitored. The Performance API’s performance.getEntries() returns detailed HTTP request information, including initiatorType and duration. Visualizing these durations helps identify slow resources.
Page Performance Monitoring
Performance timing data can be obtained from performance.timing. Key metrics include DNS lookup time, TCP connection time, TTFB, DOM parsing time, request time, and page load time.
DNS lookup: domainLookupEnd - domainLookupStart TCP connection: connectEnd - connectStart TTFB: responseStart - navigationStart DOM parsing: domComplete - responseEnd Request time: responseEnd - requestStart Page load:
loadEventEnd - navigationStartAJAX Request Interception
To monitor AJAX errors, intercept the native XMLHttpRequest methods. The example below shows a simple proxy that records request URL, method, start time and later calculates load time.
window._ahrealxhr = window._ahrealxhr || XMLHttpRequest;
XMLHttpRequest = function () {
this.xhr = new window._ahrealxhr();
// proxy properties and methods
for (var attr in this.xhr) {
var type = typeof this.xhr[attr];
if (type === 'function') {
this[attr] = hookfun(attr);
} else {
Object.defineProperty(this, attr, {
get: getFactory(attr),
set: setFactory(attr)
});
}
}
};In open you can filter URLs and store the start time:
open([method, url], xhr) {
if (options.filterURL.some(filter => url.includes(filter))) return;
currentAjax.push({ url, method, startTime: new Date().getTime(), xhr });
}The onreadystatechange and onload events are used to calculate the total request duration and report non‑2xx responses as errors.
onreadystatechange(xhr) {
if (xhr.readyState !== 4) return;
// calculate endTime, loadTime, and report if status >= 400
}Fetch requests are intercepted by wrapping window.fetch and using then / catch to report non‑ok responses.
let _oldFetch = window.fetch;
window.fetch = function () {
return _oldFetch.apply(this, arguments).then(res => {
if (!res.ok) {
// report error
}
return res;
}).catch(error => {
// report network error
throw error;
});
};Framework Exception Interception
Modern frameworks provide global error hooks. For Vue use Vue.config.errorHandler, for React use componentDidCatch in an Error Boundary. These handlers can forward errors to the same reporting pipeline.
PV/UV Statistics
Page view (PV) and unique visitor (UV) metrics are collected together with performance data. Session storage can store a per‑session UID, while local storage with an expiration date tracks daily UVs.
function markSessionUserId() {
let sessionUserId = sessionStorage.getItem('zr_monitor_session_uid') || '';
if (sessionUserId) {
return { isFirstIn: false, uid: sessionUserId };
} else {
sessionUserId = uuid();
sessionStorage.setItem('zr_monitor_session_uid', sessionUserId);
return { isFirstIn: true, uid: sessionUserId };
}
}Reporting Strategies
Collected data must be sent to a server. Common methods include:
Dynamic Image requests (GET with query string).
HTTP HEAD requests.
POST requests using XMLHttpRequest or fetch.
Beacon API ( navigator.sendBeacon) for unload‑time reporting.
For non‑urgent data, store locally (LocalStorage, IndexedDB) and batch‑upload later. IndexedDB offers larger capacity and transactional operations.
function saveOfflineData(data) {
return new Promise((resolve, reject) => {
let request = db.transaction('zr_offline_data', 'readwrite')
.objectStore('zr_offline_data')
.add(data);
request.onerror = reject;
request.onsuccess = e => resolve(e.target.result);
});
}Conclusion
This article outlines the core ideas for building a front‑end monitoring client, covering error capture, resource and performance monitoring, AJAX and fetch interception, framework hooks, PV/UV tracking, and various reporting mechanisms. Further enhancements can be added based on specific project needs.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
