As a best-selling author, I invite you to explore my books on Amazon. Don't forget to follow me on Medium and show your support. Thank you! Your support means the world!
JavaScript Custom Analytics: A Comprehensive Implementation Guide
Analytics systems provide crucial insights into user behavior and application performance. Let's explore how to build a robust custom analytics system using JavaScript.
Custom Event Tracking Implementation
The foundation of any analytics system lies in event tracking. We can create a flexible event tracking system using the Beacon API, which ensures reliable data transmission even during page unload.
class EventTracker {
constructor(endpoint) {
this.endpoint = endpoint;
this.queue = [];
}
track(eventName, data) {
const event = {
name: eventName,
timestamp: Date.now(),
data,
userId: this.getUserId()
};
if (navigator.sendBeacon) {
navigator.sendBeacon(this.endpoint, JSON.stringify(event));
} else {
fetch(this.endpoint, {
method: 'POST',
body: JSON.stringify(event)
});
}
}
getUserId() {
return localStorage.getItem('userId') || this.generateUserId();
}
generateUserId() {
const userId = Math.random().toString(36).substring(2);
localStorage.setItem('userId', userId);
return userId;
}
}
Efficient Data Batching
To optimize network usage, we can implement a batching mechanism that collects multiple events before sending them:
class BatchEventTracker extends EventTracker {
constructor(endpoint, batchSize = 10, flushInterval = 5000) {
super(endpoint);
this.batchSize = batchSize;
this.events = [];
setInterval(() => this.flush(), flushInterval);
}
track(eventName, data) {
this.events.push({
name: eventName,
timestamp: Date.now(),
data,
userId: this.getUserId()
});
if (this.events.length >= this.batchSize) {
this.flush();
}
}
flush() {
if (this.events.length === 0) return;
const batch = this.events.splice(0, this.events.length);
navigator.sendBeacon(this.endpoint, JSON.stringify(batch));
}
}
Session Management Implementation
Tracking user sessions effectively requires maintaining session state and handling timeouts:
class SessionManager {
constructor(timeout = 30 * 60 * 1000) {
this.timeout = timeout;
this.sessionId = this.getStoredSessionId() || this.createSession();
this.setupSessionTimeout();
}
createSession() {
const sessionId = Date.now().toString(36) + Math.random().toString(36).substr(2);
localStorage.setItem('sessionId', sessionId);
localStorage.setItem('sessionStart', Date.now().toString());
return sessionId;
}
getStoredSessionId() {
const sessionId = localStorage.getItem('sessionId');
const sessionStart = parseInt(localStorage.getItem('sessionStart'));
if (!sessionId || !sessionStart) return null;
if (Date.now() - sessionStart > this.timeout) return null;
return sessionId;
}
setupSessionTimeout() {
document.addEventListener('mousemove', () => this.resetTimeout());
document.addEventListener('keypress', () => this.resetTimeout());
}
resetTimeout() {
localStorage.setItem('sessionStart', Date.now().toString());
}
}
Performance Metrics Collection
The Performance API provides powerful tools for measuring application performance:
class PerformanceTracker {
constructor() {
this.metrics = {};
}
trackPageLoad() {
const perfData = window.performance.timing;
const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
const domReadyTime = perfData.domContentLoadedEventEnd - perfData.navigationStart;
this.metrics.pageLoad = {
total: pageLoadTime,
domReady: domReadyTime
};
}
trackResourceTiming() {
const resources = performance.getEntriesByType('resource');
this.metrics.resources = resources.map(resource => ({
name: resource.name,
duration: resource.duration,
type: resource.initiatorType
}));
}
mark(name) {
performance.mark(name);
}
measure(name, startMark, endMark) {
performance.measure(name, startMark, endMark);
return performance.getEntriesByName(name)[0].duration;
}
}
Data Sampling Implementation
Implementing sampling helps manage data volume while maintaining accuracy:
class SamplingManager {
constructor(sampleRate = 0.1) {
this.sampleRate = sampleRate;
}
shouldSample() {
return Math.random() < this.sampleRate;
}
setSamplingRate(rate) {
this.sampleRate = Math.max(0, Math.min(1, rate));
}
adjustSamplingRate(eventCount) {
const threshold = 10000;
if (eventCount > threshold) {
this.sampleRate = Math.max(0.01, this.sampleRate * 0.8);
}
}
}
Error Tracking System
A comprehensive error tracking system captures JavaScript errors and their context:
class ErrorTracker {
constructor(endpoint) {
this.endpoint = endpoint;
this.setupErrorListener();
}
setupErrorListener() {
window.onerror = (msg, url, lineNo, columnNo, error) => {
this.trackError({
message: msg,
url: url,
line: lineNo,
column: columnNo,
stack: error?.stack,
userAgent: navigator.userAgent,
timestamp: Date.now()
});
};
window.addEventListener('unhandledrejection', (event) => {
this.trackError({
message: event.reason,
type: 'unhandledrejection',
timestamp: Date.now()
});
});
}
trackError(errorData) {
navigator.sendBeacon(this.endpoint, JSON.stringify(errorData));
}
}
User Journey Analysis
Tracking user paths and interactions provides valuable behavioral insights:
class UserJourneyTracker {
constructor() {
this.journey = [];
this.setupListeners();
}
setupListeners() {
document.addEventListener('click', (e) => {
this.trackInteraction('click', e.target);
});
window.addEventListener('popstate', () => {
this.trackNavigation(window.location.pathname);
});
}
trackInteraction(type, element) {
this.journey.push({
type,
element: element.tagName,
path: this.getElementPath(element),
timestamp: Date.now()
});
}
trackNavigation(path) {
this.journey.push({
type: 'navigation',
path,
timestamp: Date.now()
});
}
getElementPath(element) {
const path = [];
while (element && element.nodeType === Node.ELEMENT_NODE) {
path.unshift(element.nodeName.toLowerCase());
element = element.parentNode;
}
return path.join(' > ');
}
}
Privacy Compliance Implementation
Ensuring privacy compliance is crucial for any analytics system:
class PrivacyManager {
constructor() {
this.consentStatus = this.getStoredConsent();
}
getStoredConsent() {
return localStorage.getItem('analyticsConsent') === 'true';
}
setConsent(status) {
this.consentStatus = status;
localStorage.setItem('analyticsConsent', status);
}
anonymizeData(data) {
if (!data) return data;
const anonymized = {...data};
if (anonymized.userId) {
anonymized.userId = this.hashValue(anonymized.userId);
}
if (anonymized.email) {
delete anonymized.email;
}
return anonymized;
}
hashValue(value) {
return btoa(value).substring(0, 10);
}
canTrack() {
return this.consentStatus;
}
}
Integration Example
Here's how to bring all these components together:
class AnalyticsSystem {
constructor(config) {
this.eventTracker = new BatchEventTracker(config.endpoint);
this.sessionManager = new SessionManager();
this.performanceTracker = new PerformanceTracker();
this.errorTracker = new ErrorTracker(config.errorEndpoint);
this.userJourneyTracker = new UserJourneyTracker();
this.privacyManager = new PrivacyManager();
this.samplingManager = new SamplingManager(config.sampleRate);
}
track(eventName, data) {
if (!this.privacyManager.canTrack()) return;
if (!this.samplingManager.shouldSample()) return;
const enrichedData = {
...data,
sessionId: this.sessionManager.sessionId,
journey: this.userJourneyTracker.journey
};
const anonymizedData = this.privacyManager.anonymizeData(enrichedData);
this.eventTracker.track(eventName, anonymizedData);
}
}
// Usage
const analytics = new AnalyticsSystem({
endpoint: '/analytics',
errorEndpoint: '/errors',
sampleRate: 0.1
});
analytics.track('pageView', {
page: window.location.pathname,
referrer: document.referrer
});
This implementation provides a solid foundation for a custom analytics system. It's extensible, privacy-conscious, and performance-optimized. The modular structure allows for easy updates and maintenance while supporting various analytics requirements.
Remember to adjust the implementation based on specific needs, such as data retention policies, sampling rates, and privacy requirements. Regular testing and monitoring ensure the system performs reliably at scale.
101 Books
101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.
Check out our book Golang Clean Code available on Amazon.
Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!
Our Creations
Be sure to check out our creations:
Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools
We are on Medium
Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva
Top comments (0)