UNPKG

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
/** * 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 }; }