Back to blog
InspectionJanuary 1, 2024· 16 min read

Website Monitoring and Uptime Strategies: A Developer's Guide

A practical Fusebox guide to website monitoring and uptime strategies.

Website Monitoring and Uptime Strategies: A Developer's Guide

Downtime is expensive. Amazon loses $220,000 per minute when their site is down. For smaller businesses, even brief outages can mean lost customers who never return. Yet many companies only discover they're offline when customers complain on social media. This guide provides comprehensive monitoring strategies to ensure you know about problems before your users do.

Why Monitoring Matters

In 2017, a typo brought down Amazon S3, taking with it a significant portion of the internet. Companies with proper monitoring detected the issue in seconds and switched to failover systems. Those without monitoring lost hours of revenue. The difference between 99.9% uptime (8.76 hours of downtime per year) and 99.99% (52.56 minutes) can mean millions in revenue and reputation.

Comprehensive Uptime Monitor

Build a robust uptime monitoring system:

// Comprehensive Uptime Monitoring System
class UptimeMonitor {
    constructor(config) {
        this.config = {
            url: config.url,
            interval: config.interval || 60000, // 1 minute default
            timeout: config.timeout || 30000, // 30 seconds default
            retries: config.retries || 3,
            regions: config.regions || ['us-east', 'eu-west', 'asia-pacific']
        };
        
        this.status = {
            current: 'unknown',
            uptime: 0,
            downtime: 0,
            lastCheck: null,
            history: [],
            incidents: []
        };
        
        this.alertChannels = [];
        this.checks = new Map();
    }
    
    // Start monitoring
    start() {
        console.log(`Starting uptime monitoring for ${this.config.url}`);
        
        // Initial check
        this.performCheck();
        
        // Set up regular checks
        this.interval = setInterval(() => {
            this.performCheck();
        }, this.config.interval);
        
        // Set up multi-region checks
        this.startRegionalChecks();
    }
    
    // Perform uptime check
    async performCheck() {
        const startTime = Date.now();
        const checkResult = {
            timestamp: new Date().toISOString(),
            responseTime: null,
            statusCode: null,
            status: 'unknown',
            error: null,
            region: 'primary'
        };
        
        try {
            const response = await this.checkEndpoint(this.config.url);
            
            checkResult.responseTime = Date.now() - startTime;
            checkResult.statusCode = response.status;
            checkResult.status = response.ok ? 'up' : 'down';
            
            // Additional checks
            await this.performContentCheck(response, checkResult);
            await this.performPerformanceCheck(checkResult);
            
        } catch (error) {
            checkResult.status = 'down';
            checkResult.error = error.message;
            checkResult.responseTime = Date.now() - startTime;
        }
        
        // Update status
        this.updateStatus(checkResult);
        
        // Store history
        this.status.history.push(checkResult);
        if (this.status.history.length > 1440) { // Keep 24 hours at 1-minute intervals
            this.status.history.shift();
        }
        
        return checkResult;
    }
    
    // Check endpoint with retries
    async checkEndpoint(url, retries = this.config.retries) {
        for (let i = 0; i < retries; i++) {
            try {
                const controller = new AbortController();
                const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
                
                const response = await fetch(url, {
                    method: 'GET',
                    signal: controller.signal,
                    cache: 'no-cache',
                    headers: {
                        'User-Agent': 'UptimeMonitor/1.0'
                    }
                });
                
                clearTimeout(timeoutId);
                return response;
                
            } catch (error) {
                if (i === retries - 1) throw error;
                
                // Wait before retry
                await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
            }
        }
    }
    
    // Check content validity
    async performContentCheck(response, checkResult) {
        try {
            const text = await response.text();
            
            // Check for expected content
            if (this.config.expectedContent) {
                checkResult.contentValid = text.includes(this.config.expectedContent);
                if (!checkResult.contentValid) {
                    checkResult.status = 'degraded';
                    checkResult.error = 'Expected content not found';
                }
            }
            
            // Check for error indicators
            const errorIndicators = [
                '500 Internal Server Error',
                '503 Service Unavailable',
                'Database connection failed',
                'Maintenance mode'
            ];
            
            errorIndicators.forEach(indicator => {
                if (text.includes(indicator)) {
                    checkResult.status = 'error';
                    checkResult.error = `Error indicator found: ${indicator}`;
                }
            });
            
        } catch (error) {
            checkResult.contentError = error.message;
        }
    }
    
    // Performance threshold check
    async performPerformanceCheck(checkResult) {
        // Response time thresholds
        const thresholds = {
            excellent: 200,
            good: 1000,
            acceptable: 3000
        };
        
        if (checkResult.responseTime > thresholds.acceptable) {
            checkResult.performance = 'poor';
            if (checkResult.status === 'up') {
                checkResult.status = 'slow';
            }
        } else if (checkResult.responseTime > thresholds.good) {
            checkResult.performance = 'acceptable';
        } else if (checkResult.responseTime > thresholds.excellent) {
            checkResult.performance = 'good';
        } else {
            checkResult.performance = 'excellent';
        }
    }
    
    // Update monitoring status
    updateStatus(checkResult) {
        const previousStatus = this.status.current;
        this.status.current = checkResult.status;
        this.status.lastCheck = checkResult.timestamp;
        
        // Update uptime/downtime
        if (checkResult.status === 'up' || checkResult.status === 'slow') {
            this.status.uptime += this.config.interval;
        } else {
            this.status.downtime += this.config.interval;
        }
        
        // Detect incidents
        if (previousStatus === 'up' && checkResult.status !== 'up') {
            this.startIncident(checkResult);
        } else if (previousStatus !== 'up' && checkResult.status === 'up') {
            this.endIncident(checkResult);
        }
        
        // Calculate uptime percentage
        const totalTime = this.status.uptime + this.status.downtime;
        this.status.uptimePercentage = totalTime > 0 ? 
            ((this.status.uptime / totalTime) * 100).toFixed(3) : 100;
    }
    
    // Start incident tracking
    startIncident(checkResult) {
        const incident = {
            id: Date.now(),
            startTime: checkResult.timestamp,
            endTime: null,
            duration: null,
            type: checkResult.status,
            error: checkResult.error,
            checks: [checkResult]
        };
        
        this.status.incidents.push(incident);
        
        // Send alerts
        this.sendAlert('down', incident);
    }
    
    // End incident
    endIncident(checkResult) {
        const currentIncident = this.status.incidents[this.status.incidents.length - 1];
        if (currentIncident && !currentIncident.endTime) {
            currentIncident.endTime = checkResult.timestamp;
            currentIncident.duration = Date.now() - new Date(currentIncident.startTime).getTime();
            
            // Send recovery alert
            this.sendAlert('recovered', currentIncident);
        }
    }
    
    // Multi-region monitoring
    startRegionalChecks() {
        this.config.regions.forEach(region => {
            // Simulate regional checks
            setInterval(async () => {
                const checkResult = await this.performRegionalCheck(region);
                this.checks.set(region, checkResult);
                
                // Detect regional issues
                this.analyzeRegionalHealth();
            }, this.config.interval);
        });
    }
    
    // Perform check from specific region
    async performRegionalCheck(region) {
        // In practice, this would use regional endpoints or services
        const startTime = Date.now();
        
        try {
            // Simulate regional latency
            const regionalLatency = {
                'us-east': 50,
                'eu-west': 150,
                'asia-pacific': 250
            };
            
            await new Promise(resolve => 
                setTimeout(resolve, regionalLatency[region] || 100)
            );
            
            const response = await this.checkEndpoint(this.config.url);
            
            return {
                region,
                status: response.ok ? 'up' : 'down',
                responseTime: Date.now() - startTime,
                timestamp: new Date().toISOString()
            };
        } catch (error) {
            return {
                region,
                status: 'down',
                error: error.message,
                responseTime: Date.now() - startTime,
                timestamp: new Date().toISOString()
            };
        }
    }
    
    // Analyze regional health
    analyzeRegionalHealth() {
        const regionalStatuses = Array.from(this.checks.values());
        const downRegions = regionalStatuses.filter(check => check.status === 'down');
        
        if (downRegions.length > 0 && downRegions.length < regionalStatuses.length) {
            // Partial outage
            this.sendAlert('partial-outage', {
                affectedRegions: downRegions.map(r => r.region),
                totalRegions: regionalStatuses.length
            });
        } else if (downRegions.length === regionalStatuses.length) {
            // Global outage
            this.sendAlert('global-outage', {
                regions: downRegions.map(r => r.region)
            });
        }
    }
    
    // Send alerts
    sendAlert(type, data) {
        const alert = {
            type,
            timestamp: new Date().toISOString(),
            url: this.config.url,
            data
        };
        
        // Send to all configured channels
        this.alertChannels.forEach(channel => {
            channel.send(alert);
        });
        
        console.warn('Alert:', alert);
    }
    
    // Add alert channel
    addAlertChannel(channel) {
        this.alertChannels.push(channel);
    }
    
    // Get status report
    getStatusReport() {
        return {
            url: this.config.url,
            status: this.status.current,
            uptimePercentage: this.status.uptimePercentage,
            lastCheck: this.status.lastCheck,
            incidents: {
                total: this.status.incidents.length,
                active: this.status.incidents.filter(i => !i.endTime).length,
                recent: this.status.incidents.slice(-5)
            },
            performance: this.calculatePerformanceMetrics(),
            regions: Array.from(this.checks.entries()).map(([region, check]) => ({
                region,
                status: check.status,
                responseTime: check.responseTime
            }))
        };
    }
    
    // Calculate performance metrics
    calculatePerformanceMetrics() {
        const recentChecks = this.status.history.slice(-60); // Last hour
        
        if (recentChecks.length === 0) return null;
        
        const responseTimes = recentChecks
            .filter(check => check.responseTime)
            .map(check => check.responseTime);
        
        return {
            average: responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length,
            min: Math.min(...responseTimes),
            max: Math.max(...responseTimes),
            p95: this.percentile(responseTimes, 0.95),
            p99: this.percentile(responseTimes, 0.99)
        };
    }
    
    // Calculate percentile
    percentile(arr, p) {
        const sorted = arr.slice().sort((a, b) => a - b);
        const index = Math.ceil(sorted.length * p) - 1;
        return sorted[index];
    }
    
    // Stop monitoring
    stop() {
        clearInterval(this.interval);
        console.log('Uptime monitoring stopped');
    }
}

// Alert channel implementations
class EmailAlertChannel {
    constructor(config) {
        this.config = config;
    }
    
    send(alert) {
        // Email implementation
        console.log(`Email alert: ${alert.type} for ${alert.url}`);
    }
}

class SlackAlertChannel {
    constructor(webhookUrl) {
        this.webhookUrl = webhookUrl;
    }
    
    async send(alert) {
        const message = {
            text: `🚨 ${alert.type.toUpperCase()}: ${alert.url}`,
            attachments: [{
                color: alert.type.includes('down') ? 'danger' : 'good',
                fields: [
                    {
                        title: 'Status',
                        value: alert.type,
                        short: true
                    },
                    {
                        title: 'Time',
                        value: alert.timestamp,
                        short: true
                    }
                ]
            }]
        };
        
        // Send to Slack
        console.log('Slack alert:', message);
    }
}

// Usage
const monitor = new UptimeMonitor({
    url: 'https://example.com',
    interval: 60000,
    expectedContent: 'Welcome'
});

monitor.addAlertChannel(new EmailAlertChannel({ to: 'alerts@example.com' }));
monitor.addAlertChannel(new SlackAlertChannel('https://hooks.slack.com/...'));

monitor.start();

Advanced Health Check System

Implement comprehensive health checks:

// Advanced Health Check System
class HealthCheckSystem {
    constructor() {
        this.checks = new Map();
        this.results = new Map();
        this.dependencies = new Map();
    }
    
    // Register health check
    registerCheck(name, check, options = {}) {
        this.checks.set(name, {
            name,
            check,
            critical: options.critical || false,
            timeout: options.timeout || 5000,
            interval: options.interval || 30000,
            dependencies: options.dependencies || []
        });
        
        // Start periodic check
        this.startCheck(name);
    }
    
    // Start periodic health check
    startCheck(name) {
        const checkConfig = this.checks.get(name);
        
        // Initial check
        this.runCheck(name);
        
        // Periodic checks
        setInterval(() => {
            this.runCheck(name);
        }, checkConfig.interval);
    }
    
    // Run individual check
    async runCheck(name) {
        const checkConfig = this.checks.get(name);
        const startTime = Date.now();
        
        const result = {
            name,
            timestamp: new Date().toISOString(),
            status: 'unknown',
            responseTime: null,
            details: {},
            error: null
        };
        
        try {
            // Check dependencies first
            const depStatus = await this.checkDependencies(checkConfig.dependencies);
            if (!depStatus.healthy) {
                result.status = 'degraded';
                result.error = `Dependencies unhealthy: ${depStatus.failed.join(', ')}`;
                result.responseTime = Date.now() - startTime;
                this.results.set(name, result);
                return result;
            }
            
            // Run check with timeout
            const checkPromise = checkConfig.check();
            const timeoutPromise = new Promise((_, reject) => 
                setTimeout(() => reject(new Error('Check timeout')), checkConfig.timeout)
            );
            
            const checkResult = await Promise.race([checkPromise, timeoutPromise]);
            
            result.status = checkResult.healthy ? 'healthy' : 'unhealthy';
            result.details = checkResult.details || {};
            result.responseTime = Date.now() - startTime;
            
        } catch (error) {
            result.status = 'unhealthy';
            result.error = error.message;
            result.responseTime = Date.now() - startTime;
        }
        
        this.results.set(name, result);
        return result;
    }
    
    // Check dependencies
    async checkDependencies(dependencies) {
        if (dependencies.length === 0) {
            return { healthy: true, failed: [] };
        }
        
        const failed = [];
        for (const dep of dependencies) {
            const depResult = this.results.get(dep);
            if (!depResult || depResult.status !== 'healthy') {
                failed.push(dep);
            }
        }
        
        return {
            healthy: failed.length === 0,
            failed
        };
    }
    
    // Get overall health status
    getOverallHealth() {
        const allResults = Array.from(this.results.values());
        const criticalChecks = Array.from(this.checks.entries())
            .filter(([_, config]) => config.critical)
            .map(([name, _]) => name);
        
        // Check critical services
        const criticalResults = allResults.filter(r => criticalChecks.includes(r.name));
        const criticalHealthy = criticalResults.every(r => r.status === 'healthy');
        
        // Calculate health score
        const healthyCount = allResults.filter(r => r.status === 'healthy').length;
        const totalCount = allResults.length;
        const healthScore = totalCount > 0 ? (healthyCount / totalCount) * 100 : 0;
        
        return {
            status: !criticalHealthy ? 'unhealthy' : 
                   healthScore >= 90 ? 'healthy' : 
                   healthScore >= 70 ? 'degraded' : 'unhealthy',
            score: healthScore.toFixed(2),
            checks: {
                total: totalCount,
                healthy: healthyCount,
                unhealthy: totalCount - healthyCount
            },
            critical: {
                healthy: criticalResults.filter(r => r.status === 'healthy').length,
                total: criticalResults.length
            },
            timestamp: new Date().toISOString()
        };
    }
    
    // Get detailed report
    getDetailedReport() {
        const overall = this.getOverallHealth();
        const checkResults = Array.from(this.results.entries()).map(([name, result]) => ({
            ...result,
            critical: this.checks.get(name).critical
        }));
        
        return {
            overall,
            checks: checkResults.sort((a, b) => {
                // Sort by status and criticality
                if (a.critical && !b.critical) return -1;
                if (!a.critical && b.critical) return 1;
                if (a.status === 'unhealthy' && b.status !== 'unhealthy') return -1;
                if (a.status !== 'unhealthy' && b.status === 'unhealthy') return 1;
                return 0;
            }),
            recommendations: this.generateRecommendations()
        };
    }
    
    // Generate recommendations
    generateRecommendations() {
        const recommendations = [];
        
        this.results.forEach((result, name) => {
            if (result.status === 'unhealthy') {
                const config = this.checks.get(name);
                
                recommendations.push({
                    check: name,
                    priority: config.critical ? 'critical' : 'high',
                    issue: result.error || 'Check failed',
                    action: `Investigate and fix ${name} service`
                });
            } else if (result.responseTime > 5000) {
                recommendations.push({
                    check: name,
                    priority: 'medium',
                    issue: 'Slow response time',
                    action: `Optimize ${name} performance`
                });
            }
        });
        
        return recommendations;
    }
}

// Example health checks
const healthSystem = new HealthCheckSystem();

// Database health check
healthSystem.registerCheck('database', async () => {
    try {
        // Simulate database query
        const startTime = Date.now();
        await new Promise(resolve => setTimeout(resolve, 100));
        
        return {
            healthy: true,
            details: {
                responseTime: Date.now() - startTime,
                connections: 45,
                maxConnections: 100
            }
        };
    } catch (error) {
        return {
            healthy: false,
            details: { error: error.message }
        };
    }
}, { critical: true });

// API health check
healthSystem.registerCheck('api', async () => {
    try {
        const response = await fetch('/api/health');
        return {
            healthy: response.ok,
            details: {
                statusCode: response.status,
                version: response.headers.get('x-api-version')
            }
        };
    } catch (error) {
        return {
            healthy: false,
            details: { error: error.message }
        };
    }
}, { critical: true });

// Cache health check
healthSystem.registerCheck('cache', async () => {
    try {
        // Simulate cache check
        const testKey = 'health-check-' + Date.now();
        localStorage.setItem(testKey, 'test');
        const value = localStorage.getItem(testKey);
        localStorage.removeItem(testKey);
        
        return {
            healthy: value === 'test',
            details: {
                type: 'localStorage',
                available: true
            }
        };
    } catch (error) {
        return {
            healthy: false,
            details: { error: error.message }
        };
    }
}, { dependencies: ['database'] });

Real User Monitoring (RUM)

Monitor actual user experience:

// Real User Monitoring System
class RealUserMonitoring {
    constructor() {
        this.metrics = {
            navigation: [],
            resources: [],
            errors: [],
            interactions: []
        };
        
        this.sessionId = this.generateSessionId();
        this.userId = this.getUserId();
        
        this.initializeMonitoring();
    }
    
    // Initialize monitoring
    initializeMonitoring() {
        // Navigation timing
        this.collectNavigationTiming();
        
        // Resource timing
        this.collectResourceTiming();
        
        // Error monitoring
        this.setupErrorMonitoring();
        
        // User interaction monitoring
        this.setupInteractionMonitoring();
        
        // Web Vitals
        this.collectWebVitals();
        
        // Send metrics periodically
        this.startMetricReporting();
    }
    
    // Collect navigation timing
    collectNavigationTiming() {
        if ('performance' in window && 'getEntriesByType' in performance) {
            const navigation = performance.getEntriesByType('navigation')[0];
            
            if (navigation) {
                const timing = {
                    timestamp: new Date().toISOString(),
                    sessionId: this.sessionId,
                    userId: this.userId,
                    url: window.location.href,
                    
                    // Key metrics
                    domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
                    loadComplete: navigation.loadEventEnd - navigation.loadEventStart,
                    
                    // Detailed timing
                    dns: navigation.domainLookupEnd - navigation.domainLookupStart,
                    tcp: navigation.connectEnd - navigation.connectStart,
                    ssl: navigation.secureConnectionStart > 0 ? 
                         navigation.connectEnd - navigation.secureConnectionStart : 0,
                    ttfb: navigation.responseStart - navigation.requestStart,
                    download: navigation.responseEnd - navigation.responseStart,
                    domProcessing: navigation.domComplete - navigation.domInteractive,
                    
                    // Total time
                    totalTime: navigation.loadEventEnd - navigation.fetchStart
                };
                
                this.metrics.navigation.push(timing);
            }
        }
    }
    
    // Collect resource timing
    collectResourceTiming() {
        if ('performance' in window && 'getEntriesByType' in performance) {
            const resources = performance.getEntriesByType('resource');
            
            resources.forEach(resource => {
                const timing = {
                    timestamp: new Date().toISOString(),
                    sessionId: this.sessionId,
                    url: resource.name,
                    type: resource.initiatorType,
                    
                    // Size
                    transferSize: resource.transferSize,
                    encodedBodySize: resource.encodedBodySize,
                    decodedBodySize: resource.decodedBodySize,
                    
                    // Timing
                    duration: resource.duration,
                    dns: resource.domainLookupEnd - resource.domainLookupStart,
                    tcp: resource.connectEnd - resource.connectStart,
                    ttfb: resource.responseStart - resource.startTime,
                    download: resource.responseEnd - resource.responseStart
                };
                
                this.metrics.resources.push(timing);
            });
        }
    }
    
    // Setup error monitoring
    setupErrorMonitoring() {
        // JavaScript errors
        window.addEventListener('error', (event) => {
            this.metrics.errors.push({
                timestamp: new Date().toISOString(),
                sessionId: this.sessionId,
                userId: this.userId,
                type: 'javascript',
                message: event.message,
                filename: event.filename,
                line: event.lineno,
                column: event.colno,
                stack: event.error?.stack,
                url: window.location.href
            });
        });
        
        // Promise rejections
        window.addEventListener('unhandledrejection', (event) => {
            this.metrics.errors.push({
                timestamp: new Date().toISOString(),
                sessionId: this.sessionId,
                userId: this.userId,
                type: 'unhandled-promise',
                reason: event.reason,
                promise: event.promise,
                url: window.location.href
            });
        });
        
        // Resource errors
        window.addEventListener('error', (event) => {
            if (event.target !== window) {
                this.metrics.errors.push({
                    timestamp: new Date().toISOString(),
                    sessionId: this.sessionId,
                    userId: this.userId,
                    type: 'resource',
                    tagName: event.target.tagName,
                    src: event.target.src || event.target.href,
                    message: 'Resource failed to load',
                    url: window.location.href
                });
            }
        }, true);
    }
    
    // Setup interaction monitoring
    setupInteractionMonitoring() {
        // Click tracking
        document.addEventListener('click', (event) => {
            const target = event.target;
            const interaction = {
                timestamp: new Date().toISOString(),
                sessionId: this.sessionId,
                userId: this.userId,
                type: 'click',
                target: {
                    tagName: target.tagName,
                    id: target.id,
                    className: target.className,
                    text: target.textContent?.substring(0, 50)
                },
                coordinates: {
                    x: event.clientX,
                    y: event.clientY
                },
                url: window.location.href
            };
            
            this.metrics.interactions.push(interaction);
        });
        
        // Form submissions
        document.addEventListener('submit', (event) => {
            const form = event.target;
            this.metrics.interactions.push({
                timestamp: new Date().toISOString(),
                sessionId: this.sessionId,
                userId: this.userId,
                type: 'form-submit',
                form: {
                    id: form.id,
                    action: form.action,
                    method: form.method
                },
                url: window.location.href
            });
        });
    }
    
    // Collect Web Vitals
    collectWebVitals() {
        // Largest Contentful Paint
        new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
                this.metrics.webVitals = {
                    ...this.metrics.webVitals,
                    lcp: {
                        value: entry.startTime,
                        element: entry.element?.tagName
                    }
                };
            }
        }).observe({ entryTypes: ['largest-contentful-paint'] });
        
        // First Input Delay
        new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
                this.metrics.webVitals = {
                    ...this.metrics.webVitals,
                    fid: {
                        value: entry.processingStart - entry.startTime,
                        eventType: entry.name
                    }
                };
            }
        }).observe({ entryTypes: ['first-input'] });
        
        // Cumulative Layout Shift
        let clsValue = 0;
        new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
                if (!entry.hadRecentInput) {
                    clsValue += entry.value;
                }
            }
            this.metrics.webVitals = {
                ...this.metrics.webVitals,
                cls: { value: clsValue }
            };
        }).observe({ entryTypes: ['layout-shift'] });
    }
    
    // Send metrics to server
    async sendMetrics() {
        const payload = {
            sessionId: this.sessionId,
            userId: this.userId,
            timestamp: new Date().toISOString(),
            metrics: {
                navigation: this.metrics.navigation,
                resources: this.metrics.resources.slice(-50), // Last 50 resources
                errors: this.metrics.errors,
                interactions: this.metrics.interactions.slice(-20), // Last 20 interactions
                webVitals: this.metrics.webVitals
            },
            context: {
                userAgent: navigator.userAgent,
                screenResolution: `${screen.width}x${screen.height}`,
                viewport: `${window.innerWidth}x${window.innerHeight}`,
                connection: navigator.connection?.effectiveType
            }
        };
        
        // Send to monitoring endpoint
        try {
            await fetch('/api/rum', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(payload)
            });
            
            // Clear sent metrics
            this.metrics.resources = [];
            this.metrics.errors = [];
            this.metrics.interactions = [];
        } catch (error) {
            console.error('Failed to send RUM metrics:', error);
        }
    }
    
    // Start metric reporting
    startMetricReporting() {
        // Send initial metrics after page load
        window.addEventListener('load', () => {
            setTimeout(() => this.sendMetrics(), 1000);
        });
        
        // Send metrics periodically
        setInterval(() => this.sendMetrics(), 30000);
        
        // Send metrics before page unload
        window.addEventListener('beforeunload', () => {
            this.sendMetrics();
        });
    }
    
    // Helper methods
    generateSessionId() {
        return 'session-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
    }
    
    getUserId() {
        let userId = localStorage.getItem('rum-user-id');
        if (!userId) {
            userId = 'user-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
            localStorage.setItem('rum-user-id', userId);
        }
        return userId;
    }
}

// Initialize RUM
const rum = new RealUserMonitoring();

Status Page Generator

Create public status pages:

// Status Page Generator
class StatusPageGenerator {
    constructor(monitors) {
        this.monitors = monitors;
        this.incidents = [];
        this.maintenances = [];
    }
    
    // Generate status page HTML
    generateHTML() {
        const overallStatus = this.calculateOverallStatus();
        const serviceStatuses = this.getServiceStatuses();
        
        return `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>System Status</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: -apple-system, sans-serif; background: #f5f5f5; }
        .container { max-width: 1000px; margin: 0 auto; padding: 20px; }
        
        .status-hero {
            background: ${overallStatus.color};
            color: white;
            padding: 40px;
            border-radius: 10px;
            text-align: center;
            margin-bottom: 30px;
        }
        
        .status-hero h1 { font-size: 36px; margin-bottom: 10px; }
        .status-hero p { font-size: 18px; opacity: 0.9; }
        
        .services {
            background: white;
            border-radius: 10px;
            padding: 30px;
            margin-bottom: 30px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        
        .service-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 15px 0;
            border-bottom: 1px solid #eee;
        }
        
        .service-item:last-child { border-bottom: none; }
        
        .service-name { font-weight: 500; }
        
        .service-status {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .status-indicator {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #4CAF50;
        }
        
        .status-indicator.degraded { background: #FF9800; }
        .status-indicator.down { background: #f44336; }
        
        .uptime-graph {
            display: flex;
            gap: 1px;
            height: 40px;
            margin: 20px 0;
        }
        
        .uptime-bar {
            flex: 1;
            background: #4CAF50;
            position: relative;
            cursor: pointer;
        }
        
        .uptime-bar.degraded { background: #FF9800; }
        .uptime-bar.down { background: #f44336; }
        
        .uptime-bar:hover::after {
            content: attr(data-tooltip);
            position: absolute;
            bottom: 100%;
            left: 50%;
            transform: translateX(-50%);
            background: #333;
            color: white;
            padding: 5px 10px;
            border-radius: 4px;
            white-space: nowrap;
            font-size: 12px;
        }
        
        .incidents {
            background: white;
            border-radius: 10px;
            padding: 30px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        
        .incident {
            padding: 20px 0;
            border-bottom: 1px solid #eee;
        }
        
        .incident:last-child { border-bottom: none; }
        
        .incident-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        
        .incident-title { font-weight: 600; }
        
        .incident-status {
            padding: 4px 12px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 500;
        }
        
        .incident-status.resolved { 
            background: #e8f5e9; 
            color: #2e7d32;
        }
        
        .incident-status.ongoing { 
            background: #ffebee; 
            color: #c62828;
        }
        
        .incident-update {
            margin-top: 10px;
            padding-left: 20px;
            border-left: 3px solid #e0e0e0;
        }
        
        .update-time {
            font-size: 12px;
            color: #666;
            margin-bottom: 5px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="status-hero">
            <h1>${overallStatus.message}</h1>
            <p>${overallStatus.description}</p>
        </div>
        
        <div class="services">
            <h2>Service Status</h2>
            ${serviceStatuses.map(service => `
                <div class="service-item">
                    <div class="service-name">${service.name}</div>
                    <div class="service-status">
                        <span>${service.uptime}% uptime</span>
                        <div class="status-indicator ${service.status}"></div>
                    </div>
                </div>
            `).join('')}
            
            <h3 style="margin-top: 30px;">90 Day Uptime</h3>
            <div class="uptime-graph">
                ${this.generateUptimeGraph()}
            </div>
        </div>
        
        <div class="incidents">
            <h2>Recent Incidents</h2>
            ${this.incidents.length === 0 ? 
                '<p style="color: #666; margin-top: 20px;">No recent incidents</p>' :
                this.incidents.map(incident => `
                    <div class="incident">
                        <div class="incident-header">
                            <div class="incident-title">${incident.title}</div>
                            <div class="incident-status ${incident.resolved ? 'resolved' : 'ongoing'}">
                                ${incident.resolved ? 'Resolved' : 'Ongoing'}
                            </div>
                        </div>
                        ${incident.updates.map(update => `
                            <div class="incident-update">
                                <div class="update-time">${update.time}</div>
                                <div>${update.message}</div>
                            </div>
                        `).join('')}
                    </div>
                `).join('')
            }
        </div>
    </div>
    
    <script>
        // Auto-refresh every 60 seconds
        setTimeout(() => location.reload(), 60000);
    </script>
</body>
</html>
        `;
    }
    
    // Calculate overall status
    calculateOverallStatus() {
        const statuses = this.monitors.map(m => m.getStatusReport());
        const hasDown = statuses.some(s => s.status === 'down');
        const hasDegraded = statuses.some(s => s.status === 'degraded');
        
        if (hasDown) {
            return {
                status: 'down',
                message: 'System Outage',
                description: 'Some services are currently experiencing issues',
                color: '#f44336'
            };
        } else if (hasDegraded) {
            return {
                status: 'degraded',
                message: 'Partial Outage',
                description: 'Some services are operating in a degraded state',
                color: '#FF9800'
            };
        } else {
            return {
                status: 'operational',
                message: 'All Systems Operational',
                description: 'All services are running normally',
                color: '#4CAF50'
            };
        }
    }
    
    // Get service statuses
    getServiceStatuses() {
        return this.monitors.map(monitor => {
            const report = monitor.getStatusReport();
            return {
                name: new URL(report.url).hostname,
                status: report.status,
                uptime: report.uptimePercentage || '100.00'
            };
        });
    }
    
    // Generate uptime graph
    generateUptimeGraph() {
        // Simulate 90 days of uptime data
        const days = [];
        for (let i = 89; i >= 0; i--) {
            const date = new Date();
            date.setDate(date.getDate() - i);
            
            // Random status for demo
            const rand = Math.random();
            const status = rand > 0.95 ? 'down' : rand > 0.9 ? 'degraded' : 'up';
            
            days.push(`<div class="uptime-bar ${status}" data-tooltip="${date.toLocaleDateString()}: ${status}"></div>`);
        }
        
        return days.join('');
    }
    
    // Add incident
    addIncident(incident) {
        this.incidents.unshift(incident);
        if (this.incidents.length > 10) {
            this.incidents.pop();
        }
    }
    
    // Schedule maintenance
    scheduleMaintenance(maintenance) {
        this.maintenances.push(maintenance);
    }
}

// Usage
const statusPage = new StatusPageGenerator([monitor]);
// document.body.innerHTML = statusPage.generateHTML();

Best Practices for Monitoring

  1. Monitor from Multiple Locations: Single location monitoring can miss regional issues
  2. Set Realistic Thresholds: Avoid alert fatigue with appropriate limits
  3. Test Complete User Journeys: Not just homepage availability
  4. Include Dependencies: Monitor third-party services you rely on
  5. Implement Escalation: Different alerts for different severity levels
  6. Monitor Performance: Not just uptime, but response times
  7. Track Error Rates: High error rates indicate problems
  8. Use Synthetic Monitoring: Complement RUM with synthetic tests
  9. Create Status Pages: Keep users informed during incidents
  10. Practice Incident Response: Regular drills improve real incident handling

Common Monitoring Failures

  • Single Point of Failure: Monitoring system goes down with the site
  • Alert Fatigue: Too many false positives lead to ignored alerts
  • Incomplete Coverage: Missing critical user paths
  • No Performance Monitoring: Site is "up" but unusably slow
  • Ignoring Dependencies: Third-party service failures
  • No Historical Data: Can't identify trends or patterns
  • Poor Alert Routing: Right alerts don't reach right people
  • No Automated Response: Manual intervention for everything
  • Missing Context: Alerts without enough information to act
  • No Post-Mortems: Not learning from incidents

Remember: The best monitoring system is one that tells you about problems before your users notice them. Invest in comprehensive monitoring—the cost of downtime far exceeds the cost of prevention.