codewithgarry
Version:
Girish Sharma's NPX business card - DevOps Engineer & Cloud Architect - Connect with me directly via terminal!
365 lines (316 loc) âĸ 11.7 kB
JavaScript
/**
* Analytics and Monitoring System for CodeWithGarry NPX Card
* Tracks usage, performance, and errors for production insights
*/
class AnalyticsManager {
constructor() {
this.sessionId = this.generateSessionId();
this.events = [];
this.startTime = Date.now();
this.endpoint = '/analytics'; // For future backend integration
this.batchSize = 10;
this.flushInterval = 30000; // 30 seconds
this.init();
}
init() {
this.trackPageView();
this.setupErrorTracking();
this.setupPerformanceTracking();
this.setupUserInteractionTracking();
this.startBatchFlush();
console.log('đ Analytics initialized with session:', this.sessionId);
}
generateSessionId() {
return 'cwg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
trackEvent(eventName, properties = {}) {
const event = {
id: Date.now() + '_' + Math.random().toString(36).substr(2, 5),
sessionId: this.sessionId,
event: eventName,
timestamp: Date.now(),
url: typeof window !== 'undefined' ? window.location.href : 'cli',
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'node',
properties: {
...properties,
sessionDuration: Date.now() - this.startTime
}
};
this.events.push(event);
console.log('đ Event tracked:', eventName, properties);
// Auto-flush if batch is full
if (this.events.length >= this.batchSize) {
this.flush();
}
}
trackPageView() {
this.trackEvent('page_view', {
referrer: typeof document !== 'undefined' ? document.referrer : null,
screen: typeof screen !== 'undefined' ? {
width: screen.width,
height: screen.height
} : null
});
}
trackError(error, context = {}) {
this.trackEvent('error', {
message: error.message,
stack: error.stack,
name: error.name,
context: context
});
}
trackPerformance(metrics) {
this.trackEvent('performance', metrics);
}
trackUserInteraction(action, element = null) {
this.trackEvent('user_interaction', {
action: action,
element: element,
timestamp: Date.now()
});
}
setupErrorTracking() {
if (typeof window !== 'undefined') {
window.addEventListener('error', (event) => {
this.trackError(event.error, {
filename: event.filename,
lineno: event.lineno,
colno: event.colno
});
});
window.addEventListener('unhandledrejection', (event) => {
this.trackError(event.reason, {
type: 'unhandled_promise_rejection'
});
});
}
// Node.js error tracking
if (typeof process !== 'undefined') {
process.on('uncaughtException', (error) => {
this.trackError(error, { type: 'uncaught_exception' });
});
process.on('unhandledRejection', (reason) => {
this.trackError(new Error(reason), { type: 'unhandled_rejection' });
});
}
}
setupPerformanceTracking() {
if (typeof window !== 'undefined' && 'performance' in window) {
// Track page load performance
window.addEventListener('load', () => {
setTimeout(() => {
const navigation = performance.getEntriesByType('navigation')[0];
if (navigation) {
this.trackPerformance({
loadTime: navigation.loadEventEnd - navigation.loadEventStart,
domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
networkTime: navigation.responseEnd - navigation.requestStart,
renderTime: navigation.loadEventEnd - navigation.responseEnd
});
}
}, 1000);
});
// Track Core Web Vitals
this.trackCoreWebVitals();
}
}
trackCoreWebVitals() {
if ('PerformanceObserver' in window) {
// Track LCP (Largest Contentful Paint)
const lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
this.trackPerformance({
metric: 'lcp',
value: lastEntry.startTime
});
});
lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
// Track FID (First Input Delay)
const fidObserver = new PerformanceObserver((list) => {
const firstEntry = list.getEntries()[0];
this.trackPerformance({
metric: 'fid',
value: firstEntry.processingStart - firstEntry.startTime
});
});
fidObserver.observe({ entryTypes: ['first-input'] });
// Track CLS (Cumulative Layout Shift)
let clsValue = 0;
const clsObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
this.trackPerformance({
metric: 'cls',
value: clsValue
});
});
clsObserver.observe({ entryTypes: ['layout-shift'] });
}
}
setupUserInteractionTracking() {
if (typeof document !== 'undefined') {
// Track clicks
document.addEventListener('click', (event) => {
const element = event.target;
this.trackUserInteraction('click', {
tagName: element.tagName,
className: element.className,
id: element.id,
text: element.textContent?.slice(0, 50)
});
});
// Track form submissions
document.addEventListener('submit', (event) => {
this.trackUserInteraction('form_submit', {
formId: event.target.id,
formAction: event.target.action
});
});
// Track scroll depth
let maxScroll = 0;
const trackScroll = () => {
const scrollPercent = Math.round(
(window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100
);
if (scrollPercent > maxScroll) {
maxScroll = scrollPercent;
if (scrollPercent % 25 === 0) { // Track at 25%, 50%, 75%, 100%
this.trackUserInteraction('scroll', { depth: scrollPercent });
}
}
};
window.addEventListener('scroll', this.debounce(trackScroll, 1000));
}
}
flush() {
if (this.events.length === 0) return;
const eventsToSend = [...this.events];
this.events = [];
// Store locally for now (in production, send to analytics endpoint)
this.storeLocally(eventsToSend);
console.log('đ Flushed', eventsToSend.length, 'events');
}
storeLocally(events) {
try {
const existingData = JSON.parse(localStorage.getItem('cwg_analytics') || '[]');
const newData = [...existingData, ...events];
// Keep only last 1000 events to prevent storage bloat
const trimmedData = newData.slice(-1000);
localStorage.setItem('cwg_analytics', JSON.stringify(trimmedData));
} catch (error) {
console.warn('Failed to store analytics locally:', error);
}
}
startBatchFlush() {
setInterval(() => {
this.flush();
}, this.flushInterval);
// Flush on page unload
if (typeof window !== 'undefined') {
window.addEventListener('beforeunload', () => {
this.flush();
});
}
}
getStoredAnalytics() {
try {
return JSON.parse(localStorage.getItem('cwg_analytics') || '[]');
} catch (error) {
console.warn('Failed to retrieve stored analytics:', error);
return [];
}
}
generateReport() {
const data = this.getStoredAnalytics();
const report = {
totalEvents: data.length,
sessions: [...new Set(data.map(e => e.sessionId))].length,
eventTypes: {},
errors: data.filter(e => e.event === 'error'),
performance: data.filter(e => e.event === 'performance'),
userInteractions: data.filter(e => e.event === 'user_interaction')
};
// Count event types
data.forEach(event => {
report.eventTypes[event.event] = (report.eventTypes[event.event] || 0) + 1;
});
return report;
}
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
}
// Logger utility
class Logger {
constructor(level = 'info') {
this.level = level;
this.levels = {
error: 0,
warn: 1,
info: 2,
debug: 3
};
}
error(...args) {
if (this.levels[this.level] >= this.levels.error) {
console.error('â', new Date().toISOString(), ...args);
}
}
warn(...args) {
if (this.levels[this.level] >= this.levels.warn) {
console.warn('â ī¸', new Date().toISOString(), ...args);
}
}
info(...args) {
if (this.levels[this.level] >= this.levels.info) {
console.log('âšī¸', new Date().toISOString(), ...args);
}
}
debug(...args) {
if (this.levels[this.level] >= this.levels.debug) {
console.log('đ', new Date().toISOString(), ...args);
}
}
}
// Initialize analytics
function initMonitoring() {
const analytics = new AnalyticsManager();
const logger = new Logger(process?.env?.LOG_LEVEL || 'info');
// Make available globally
if (typeof window !== 'undefined') {
window.analytics = analytics;
window.logger = logger;
} else {
global.analytics = analytics;
global.logger = logger;
}
logger.info('đ Monitoring system initialized');
return { analytics, logger };
}
// Auto-initialize
if (typeof window !== 'undefined') {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initMonitoring);
} else {
initMonitoring();
}
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = {
AnalyticsManager,
Logger,
initMonitoring
};
}