JavaScript系列(58)--性能监控系统详解

发布于:2025-02-10 ⋅ 阅读:(76) ⋅ 点赞:(0)

JavaScript性能监控系统详解 📊

今天,让我们深入探讨JavaScript的性能监控系统。性能监控对于保证应用的稳定性和用户体验至关重要。

性能监控基础概念 🌟

💡 小知识:JavaScript性能监控是指通过收集和分析各种性能指标,来评估和优化应用性能的过程。这包括页面加载时间、资源使用情况、运行时性能等多个方面。

性能指标收集 📈

// 1. 性能指标收集器
class PerformanceCollector {
    constructor() {
        this.metrics = new Map();
        this.observers = new Set();
    }
    
    // 收集页面加载性能
    collectPageMetrics() {
        const timing = performance.timing;
        const metrics = {
            dnsTime: timing.domainLookupEnd - timing.domainLookupStart,
            tcpTime: timing.connectEnd - timing.connectStart,
            ttfb: timing.responseStart - timing.requestStart,
            domReady: timing.domContentLoadedEventEnd - timing.navigationStart,
            loadComplete: timing.loadEventEnd - timing.navigationStart
        };
        
        this.metrics.set('page', metrics);
        this.notifyObservers('page', metrics);
    }
    
    // 收集资源加载性能
    collectResourceMetrics() {
        const resources = performance.getEntriesByType('resource');
        const metrics = resources.map(resource => ({
            name: resource.name,
            type: resource.initiatorType,
            duration: resource.duration,
            size: resource.transferSize,
            startTime: resource.startTime
        }));
        
        this.metrics.set('resources', metrics);
        this.notifyObservers('resources', metrics);
    }
    
    // 收集运行时性能
    collectRuntimeMetrics() {
        const metrics = {
            memory: performance.memory ? {
                usedJSHeapSize: performance.memory.usedJSHeapSize,
                totalJSHeapSize: performance.memory.totalJSHeapSize,
                jsHeapSizeLimit: performance.memory.jsHeapSizeLimit
            } : null,
            fps: this.calculateFPS()
        };
        
        this.metrics.set('runtime', metrics);
        this.notifyObservers('runtime', metrics);
    }
    
    calculateFPS() {
        let lastTime = performance.now();
        let frames = 0;
        let fps = 0;
        
        const updateFPS = () => {
            const now = performance.now();
            frames++;
            
            if (now > lastTime + 1000) {
                fps = Math.round((frames * 1000) / (now - lastTime));
                frames = 0;
                lastTime = now;
            }
            
            requestAnimationFrame(updateFPS);
        };
        
        requestAnimationFrame(updateFPS);
        return () => fps;
    }
    
    addObserver(observer) {
        this.observers.add(observer);
    }
    
    removeObserver(observer) {
        this.observers.delete(observer);
    }
    
    notifyObservers(type, data) {
        this.observers.forEach(observer => {
            observer.update(type, data);
        });
    }
}

// 2. 性能标记管理器
class PerformanceMarker {
    constructor() {
        this.marks = new Map();
    }
    
    mark(name) {
        performance.mark(name);
        this.marks.set(name, performance.now());
    }
    
    measure(name, startMark, endMark) {
        performance.measure(name, startMark, endMark);
        const measure = performance.getEntriesByName(name, 'measure')[0];
        return {
            name,
            duration: measure.duration,
            startTime: measure.startTime
        };
    }
    
    clearMarks() {
        performance.clearMarks();
        this.marks.clear();
    }
    
    clearMeasures() {
        performance.clearMeasures();
    }
}

// 3. 用户交互监控
class InteractionMonitor {
    constructor() {
        this.interactions = [];
        this.setupListeners();
    }
    
    setupListeners() {
        const observer = new PerformanceObserver(list => {
            const entries = list.getEntries();
            this.processInteractions(entries);
        });
        
        observer.observe({ 
            entryTypes: ['first-input', 'layout-shift', 'largest-contentful-paint']
        });
    }
    
    processInteractions(entries) {
        entries.forEach(entry => {
            this.interactions.push({
                type: entry.entryType,
                startTime: entry.startTime,
                duration: entry.duration,
                value: entry.value,
                element: entry.target
            });
        });
    }
    
    getInteractionMetrics() {
        return {
            fid: this.calculateFID(),
            cls: this.calculateCLS(),
            lcp: this.calculateLCP()
        };
    }
    
    calculateFID() {
        const fidEntry = this.interactions.find(
            i => i.type === 'first-input'
        );
        return fidEntry ? fidEntry.duration : null;
    }
    
    calculateCLS() {
        return this.interactions
            .filter(i => i.type === 'layout-shift')
            .reduce((sum, entry) => sum + entry.value, 0);
    }
    
    calculateLCP() {
        const lcpEntries = this.interactions
            .filter(i => i.type === 'largest-contentful-paint');
        return lcpEntries.length > 0 
            ? lcpEntries[lcpEntries.length - 1].startTime 
            : null;
    }
}

性能分析工具 🔍

// 1. 性能分析器
class PerformanceAnalyzer {
    constructor() {
        this.thresholds = new Map();
        this.issues = [];
    }
    
    setThreshold(metric, value) {
        this.thresholds.set(metric, value);
    }
    
    analyzeMetrics(metrics) {
        this.issues = [];
        
        // 分析页面加载性能
        if (metrics.page) {
            this.analyzePageMetrics(metrics.page);
        }
        
        // 分析资源加载性能
        if (metrics.resources) {
            this.analyzeResourceMetrics(metrics.resources);
        }
        
        // 分析运行时性能
        if (metrics.runtime) {
            this.analyzeRuntimeMetrics(metrics.runtime);
        }
        
        return this.issues;
    }
    
    analyzePageMetrics(pageMetrics) {
        if (pageMetrics.ttfb > this.thresholds.get('ttfb')) {
            this.issues.push({
                type: 'warning',
                metric: 'ttfb',
                message: 'Time to First Byte is too high',
                value: pageMetrics.ttfb,
                threshold: this.thresholds.get('ttfb')
            });
        }
        
        if (pageMetrics.domReady > this.thresholds.get('domReady')) {
            this.issues.push({
                type: 'error',
                metric: 'domReady',
                message: 'DOM Ready time is too high',
                value: pageMetrics.domReady,
                threshold: this.thresholds.get('domReady')
            });
        }
    }
    
    analyzeResourceMetrics(resources) {
        const largeResources = resources.filter(
            r => r.size > this.thresholds.get('resourceSize')
        );
        
        if (largeResources.length > 0) {
            this.issues.push({
                type: 'warning',
                metric: 'resourceSize',
                message: 'Large resources detected',
                resources: largeResources
            });
        }
    }
    
    analyzeRuntimeMetrics(runtime) {
        if (runtime.memory && 
            runtime.memory.usedJSHeapSize > 
            this.thresholds.get('heapSize')) {
            this.issues.push({
                type: 'warning',
                metric: 'memory',
                message: 'High memory usage detected',
                value: runtime.memory.usedJSHeapSize
            });
        }
        
        if (runtime.fps() < this.thresholds.get('fps')) {
            this.issues.push({
                type: 'error',
                metric: 'fps',
                message: 'Low FPS detected',
                value: runtime.fps()
            });
        }
    }
    
    generateReport() {
        return {
            timestamp: new Date(),
            issueCount: this.issues.length,
            criticalIssues: this.issues.filter(i => i.type === 'error'),
            warnings: this.issues.filter(i => i.type === 'warning'),
            recommendations: this.generateRecommendations()
        };
    }
    
    generateRecommendations() {
        return this.issues.map(issue => {
            switch (issue.metric) {
                case 'ttfb':
                    return 'Consider using a CDN or optimizing server response time';
                case 'domReady':
                    return 'Optimize JavaScript execution and reduce blocking resources';
                case 'resourceSize':
                    return 'Compress and optimize large resources';
                case 'memory':
                    return 'Check for memory leaks and optimize memory usage';
                case 'fps':
                    return 'Optimize animations and reduce DOM operations';
                default:
                    return 'Review and optimize the affected area';
            }
        });
    }
}

// 2. 性能监控仪表板
class PerformanceDashboard {
    constructor() {
        this.metrics = [];
        this.charts = new Map();
    }
    
    addMetric(metric) {
        this.metrics.push({
            ...metric,
            timestamp: new Date()
        });
        
        this.updateCharts();
    }
    
    createChart(type, data) {
        // 实现图表创建逻辑
        return {
            type,
            data,
            render: () => {
                // 渲染图表
            }
        };
    }
    
    updateCharts() {
        // 更新页面加载时间趋势
        this.charts.set('loadTime', this.createChart(
            'line',
            this.metrics.map(m => ({
                x: m.timestamp,
                y: m.page.loadComplete
            }))
        ));
        
        // 更新资源使用趋势
        this.charts.set('resources', this.createChart(
            'bar',
            this.metrics.map(m => ({
                x: m.timestamp,
                y: m.resources.length
            }))
        ));
        
        // 更新内存使用趋势
        this.charts.set('memory', this.createChart(
            'area',
            this.metrics.map(m => ({
                x: m.timestamp,
                y: m.runtime.memory?.usedJSHeapSize
            }))
        ));
    }
    
    generateReport(timeRange) {
        const filteredMetrics = this.metrics.filter(
            m => m.timestamp >= timeRange.start && 
                m.timestamp <= timeRange.end
        );
        
        return {
            summary: this.calculateSummary(filteredMetrics),
            trends: this.calculateTrends(filteredMetrics),
            issues: this.findIssues(filteredMetrics)
        };
    }
}

// 3. 性能优化建议生成器
class OptimizationAdvisor {
    constructor() {
        this.rules = new Map();
    }
    
    addRule(name, condition, recommendation) {
        this.rules.set(name, { condition, recommendation });
    }
    
    analyze(metrics) {
        const recommendations = [];
        
        for (const [name, rule] of this.rules) {
            if (rule.condition(metrics)) {
                recommendations.push({
                    name,
                    recommendation: rule.recommendation,
                    priority: this.calculatePriority(metrics, name)
                });
            }
        }
        
        return recommendations.sort((a, b) => b.priority - a.priority);
    }
    
    calculatePriority(metrics, ruleName) {
        // 根据性能指标的严重程度计算优化建议的优先级
        const weights = {
            loadTime: 0.3,
            memory: 0.2,
            fps: 0.3,
            resources: 0.2
        };
        
        let score = 0;
        
        if (metrics.page?.loadComplete > 3000) {
            score += weights.loadTime;
        }
        
        if (metrics.runtime?.memory?.usedJSHeapSize > 
            metrics.runtime?.memory?.jsHeapSizeLimit * 0.8) {
            score += weights.memory;
        }
        
        if (metrics.runtime?.fps() < 30) {
            score += weights.fps;
        }
        
        if (metrics.resources?.length > 50) {
            score += weights.resources;
        }
        
        return score;
    }
}

报警和通知系统 🚨

// 1. 报警管理器
class AlertManager {
    constructor() {
        this.alerts = new Map();
        this.handlers = new Map();
    }
    
    addAlert(name, condition, options = {}) {
        this.alerts.set(name, {
            condition,
            threshold: options.threshold,
            cooldown: options.cooldown || 5 * 60 * 1000, // 5分钟
            lastTriggered: 0,
            status: 'inactive'
        });
    }
    
    addHandler(severity, handler) {
        if (!this.handlers.has(severity)) {
            this.handlers.set(severity, []);
        }
        this.handlers.get(severity).push(handler);
    }
    
    checkAlerts(metrics) {
        const now = Date.now();
        const triggeredAlerts = [];
        
        for (const [name, alert] of this.alerts) {
            if (alert.status === 'inactive' && 
                now - alert.lastTriggered > alert.cooldown) {
                if (alert.condition(metrics)) {
                    alert.status = 'active';
                    alert.lastTriggered = now;
                    triggeredAlerts.push(name);
                }
            }
        }
        
        return triggeredAlerts;
    }
    
    async notify(alertName, data) {
        const alert = this.alerts.get(alertName);
        if (!alert) return;
        
        const handlers = this.handlers.get(alert.severity) || [];
        await Promise.all(
            handlers.map(handler => handler(alertName, data))
        );
    }
}

// 2. 通知发送器
class NotificationSender {
    constructor() {
        this.channels = new Map();
    }
    
    addChannel(name, sender) {
        this.channels.set(name, sender);
    }
    
    async send(channel, message) {
        const sender = this.channels.get(channel);
        if (!sender) {
            throw new Error(`Channel ${channel} not found`);
        }
        
        try {
            await sender(message);
        } catch (error) {
            console.error(`Failed to send notification to ${channel}:`, error);
            throw error;
        }
    }
    
    async broadcast(message) {
        const results = await Promise.allSettled(
            Array.from(this.channels.entries())
                .map(([channel, sender]) => this.send(channel, message))
        );
        
        return results.reduce((summary, result, index) => {
            const channel = Array.from(this.channels.keys())[index];
            summary[channel] = result.status === 'fulfilled';
            return summary;
        }, {});
    }
}

// 3. 报告生成器
class ReportGenerator {
    constructor() {
        this.templates = new Map();
    }
    
    addTemplate(name, template) {
        this.templates.set(name, template);
    }
    
    generateReport(data, templateName = 'default') {
        const template = this.templates.get(templateName);
        if (!template) {
            throw new Error(`Template ${templateName} not found`);
        }
        
        return template(data);
    }
    
    async saveReport(report, format = 'json') {
        const fileName = `performance-report-${new Date().toISOString()}.${format}`;
        
        switch (format) {
            case 'json':
                await fs.writeFile(fileName, JSON.stringify(report, null, 2));
                break;
            case 'html':
                await fs.writeFile(fileName, this.generateHtmlReport(report));
                break;
            case 'pdf':
                await this.generatePdfReport(report, fileName);
                break;
            default:
                throw new Error(`Unsupported format: ${format}`);
        }
        
        return fileName;
    }
    
    generateHtmlReport(report) {
        // 生成HTML格式的报告
        return `
            <!DOCTYPE html>
            <html>
                <head>
                    <title>Performance Report</title>
                </head>
                <body>
                    <h1>Performance Report</h1>
                    <div class="metrics">
                        ${this.renderMetrics(report.metrics)}
                    </div>
                    <div class="issues">
                        ${this.renderIssues(report.issues)}
                    </div>
                    <div class="recommendations">
                        ${this.renderRecommendations(report.recommendations)}
                    </div>
                </body>
            </html>
        `;
    }
}

最佳实践建议 💡

  1. 性能监控策略
// 1. 性能预算管理器
class PerformanceBudgetManager {
    constructor() {
        this.budgets = new Map();
    }
    
    setBudget(metric, limit) {
        this.budgets.set(metric, limit);
    }
    
    checkBudget(metric, value) {
        const limit = this.budgets.get(metric);
        if (!limit) return true;
        
        return value <= limit;
    }
    
    generateBudgetReport(metrics) {
        const report = {
            timestamp: new Date(),
            violations: [],
            status: 'pass'
        };
        
        for (const [metric, limit] of this.budgets) {
            const value = metrics[metric];
            if (value > limit) {
                report.violations.push({
                    metric,
                    limit,
                    value,
                    overage: value - limit
                });
                report.status = 'fail';
            }
        }
        
        return report;
    }
}

// 2. 性能监控配置管理器
class MonitoringConfigManager {
    constructor() {
        this.config = {
            sampleRate: 0.1,
            metrics: new Set(),
            alertThresholds: new Map()
        };
    }
    
    setSampleRate(rate) {
        if (rate < 0 || rate > 1) {
            throw new Error('Sample rate must be between 0 and 1');
        }
        this.config.sampleRate = rate;
    }
    
    enableMetric(metric) {
        this.config.metrics.add(metric);
    }
    
    disableMetric(metric) {
        this.config.metrics.delete(metric);
    }
    
    setAlertThreshold(metric, threshold) {
        this.config.alertThresholds.set(metric, threshold);
    }
    
    shouldCollect() {
        return Math.random() < this.config.sampleRate;
    }
}

// 3. 性能数据存储管理器
class MetricsStorageManager {
    constructor() {
        this.storage = new Map();
        this.retention = 30 * 24 * 60 * 60 * 1000; // 30天
    }
    
    store(metrics) {
        const timestamp = Date.now();
        this.storage.set(timestamp, metrics);
        this.cleanup();
    }
    
    cleanup() {
        const cutoff = Date.now() - this.retention;
        for (const [timestamp] of this.storage) {
            if (timestamp < cutoff) {
                this.storage.delete(timestamp);
            }
        }
    }
    
    query(timeRange) {
        const results = [];
        for (const [timestamp, metrics] of this.storage) {
            if (timestamp >= timeRange.start && 
                timestamp <= timeRange.end) {
                results.push({ timestamp, metrics });
            }
        }
        return results;
    }
}

结语 📝

JavaScript性能监控系统是保证应用性能和用户体验的关键工具。通过本文,我们学习了:

  1. 性能指标的收集和分析
  2. 性能监控工具的实现
  3. 报警和通知系统的构建
  4. 性能报告的生成
  5. 最佳实践和优化建议

💡 学习建议:在实施性能监控时,要注意平衡监控的全面性和系统性能的开销。选择合适的采样率和监控指标,避免监控系统本身成为性能瓶颈。同时,要建立完善的报警机制和响应流程,确保能够及时发现和解决性能问题。


如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻


网站公告

今日签到

点亮在社区的每一天
去签到