当前位置: 首页 > article >正文

工程化与框架系列(36)--前端监控告警实践

前端监控告警实践 🔔

引言

前端监控是保障应用质量和用户体验的重要手段。本文将深入探讨前端监控的实现方案,包括性能监控、错误监控、用户行为监控等方面,以及相应的告警机制。

监控系统概述

前端监控系统主要包括以下方面:

  • 性能监控:页面加载、资源加载、接口性能等
  • 错误监控:JS错误、接口错误、资源加载错误等
  • 用户行为:PV/UV、点击行为、路由变化等
  • 业务监控:转化率、留存率、业务指标等
  • 告警系统:规则配置、通知分发、告警处理等

监控系统实现

监控管理器

// 监控管理器类
class MonitoringManager {
    private static instance: MonitoringManager;
    private config: MonitorConfig;
    private collectors: Map<string, Collector>;
    private processors: Map<string, Processor>;
    private reporters: Map<string, Reporter>;
    private alerter: Alerter;
    
    private constructor() {
        this.collectors = new Map();
        this.processors = new Map();
        this.reporters = new Map();
        this.config = {
            appId: '',
            userId: '',
            sessionId: '',
            environment: 'production',
            version: '1.0.0',
            sampling: 100
        };
    }
    
    // 获取单例实例
    static getInstance(): MonitoringManager {
        if (!MonitoringManager.instance) {
            MonitoringManager.instance = new MonitoringManager();
        }
        return MonitoringManager.instance;
    }
    
    // 初始化监控系统
    init(config: MonitorConfig): void {
        this.config = { ...this.config, ...config };
        
        // 初始化收集器
        this.initCollectors();
        
        // 初始化处理器
        this.initProcessors();
        
        // 初始化上报器
        this.initReporters();
        
        // 初始化告警器
        this.initAlerter();
        
        // 启动监控
        this.start();
    }
    
    // 初始化收集器
    private initCollectors(): void {
        // 性能收集器
        this.collectors.set(
            'performance',
            new PerformanceCollector()
        );
        
        // 错误收集器
        this.collectors.set(
            'error',
            new ErrorCollector()
        );
        
        // 行为收集器
        this.collectors.set(
            'behavior',
            new BehaviorCollector()
        );
        
        // 业务收集器
        this.collectors.set(
            'business',
            new BusinessCollector()
        );
    }
    
    // 初始化处理器
    private initProcessors(): void {
        // 性能处理器
        this.processors.set(
            'performance',
            new PerformanceProcessor()
        );
        
        // 错误处理器
        this.processors.set(
            'error',
            new ErrorProcessor()
        );
        
        // 行为处理器
        this.processors.set(
            'behavior',
            new BehaviorProcessor()
        );
        
        // 业务处理器
        this.processors.set(
            'business',
            new BusinessProcessor()
        );
    }
    
    // 初始化上报器
    private initReporters(): void {
        // HTTP上报器
        this.reporters.set(
            'http',
            new HttpReporter(this.config.reportUrl)
        );
        
        // 信标上报器
        this.reporters.set(
            'beacon',
            new BeaconReporter()
        );
        
        // 日志上报器
        this.reporters.set(
            'log',
            new LogReporter()
        );
    }
    
    // 初始化告警器
    private initAlerter(): void {
        this.alerter = new Alerter({
            rules: this.config.alertRules,
            channels: this.config.alertChannels
        });
    }
    
    // 启动监控
    private start(): void {
        // 启动收集器
        this.collectors.forEach(collector => {
            collector.start();
        });
        
        // 启动处理器
        this.processors.forEach(processor => {
            processor.start();
        });
        
        // 启动上报器
        this.reporters.forEach(reporter => {
            reporter.start();
        });
        
        // 启动告警器
        this.alerter.start();
    }
    
    // 停止监控
    stop(): void {
        // 停止收集器
        this.collectors.forEach(collector => {
            collector.stop();
        });
        
        // 停止处理器
        this.processors.forEach(processor => {
            processor.stop();
        });
        
        // 停止上报器
        this.reporters.forEach(reporter => {
            reporter.stop();
        });
        
        // 停止告警器
        this.alerter.stop();
    }
    
    // 手动上报
    report(data: MonitorData): void {
        // 采样判断
        if (!this.shouldSample()) {
            return;
        }
        
        // 数据处理
        const processor = this.processors.get(data.type);
        if (processor) {
            data = processor.process(data);
        }
        
        // 数据上报
        const reporter = this.reporters.get(this.config.reportType);
        if (reporter) {
            reporter.report(data);
        }
        
        // 告警检查
        this.alerter.check(data);
    }
    
    // 采样判断
    private shouldSample(): boolean {
        return Math.random() * 100 < this.config.sampling;
    }
}

// 收集器基类
abstract class Collector {
    protected config: CollectorConfig;
    protected callback: (data: MonitorData) => void;
    
    constructor(config: CollectorConfig) {
        this.config = config;
    }
    
    setCallback(callback: (data: MonitorData) => void): void {
        this.callback = callback;
    }
    
    abstract start(): void;
    abstract stop(): void;
}

// 性能收集器
class PerformanceCollector extends Collector {
    private observer: PerformanceObserver | null = null;
    
    start(): void {
        // 收集性能指标
        this.collectMetrics();
        
        // 观察性能事件
        this.observePerformance();
    }
    
    stop(): void {
        this.observer?.disconnect();
        this.observer = null;
    }
    
    private collectMetrics(): void {
        // 收集导航计时
        const navigation = performance.getEntriesByType('navigation')[0];
        this.callback?.({
            type: 'performance',
            subType: 'navigation',
            data: navigation
        });
        
        // 收集资源计时
        const resources = performance.getEntriesByType('resource');
        resources.forEach(resource => {
            this.callback?.({
                type: 'performance',
                subType: 'resource',
                data: resource
            });
        });
        
        // 收集First Paint
        const paint = performance.getEntriesByType('paint');
        paint.forEach(entry => {
            this.callback?.({
                type: 'performance',
                subType: 'paint',
                data: entry
            });
        });
    }
    
    private observePerformance(): void {
        this.observer = new PerformanceObserver(list => {
            list.getEntries().forEach(entry => {
                this.callback?.({
                    type: 'performance',
                    subType: entry.entryType,
                    data: entry
                });
            });
        });
        
        // 观察的性能指标类型
        this.observer.observe({
            entryTypes: [
                'navigation',
                'resource',
                'paint',
                'largest-contentful-paint',
                'first-input',
                'layout-shift'
            ]
        });
    }
}

// 错误收集器
class ErrorCollector extends Collector {
    private errorHandler: (event: ErrorEvent) => void;
    private unhandledRejectionHandler: (event: PromiseRejectionEvent) => void;
    
    start(): void {
        // 监听JS错误
        this.errorHandler = (event: ErrorEvent) => {
            this.callback?.({
                type: 'error',
                subType: 'javascript',
                data: {
                    message: event.message,
                    filename: event.filename,
                    lineno: event.lineno,
                    colno: event.colno,
                    error: event.error
                }
            });
        };
        window.addEventListener('error', this.errorHandler);
        
        // 监听Promise错误
        this.unhandledRejectionHandler = (event: PromiseRejectionEvent) => {
            this.callback?.({
                type: 'error',
                subType: 'promise',
                data: {
                    reason: event.reason
                }
            });
        };
        window.addEventListener(
            'unhandledrejection',
            this.unhandledRejectionHandler
        );
    }
    
    stop(): void {
        window.removeEventListener('error', this.errorHandler);
        window.removeEventListener(
            'unhandledrejection',
            this.unhandledRejectionHandler
        );
    }
}

// 行为收集器
class BehaviorCollector extends Collector {
    private clickHandler: (event: MouseEvent) => void;
    private routeHandler: () => void;
    
    start(): void {
        // 监听点击事件
        this.clickHandler = (event: MouseEvent) => {
            const target = event.target as HTMLElement;
            this.callback?.({
                type: 'behavior',
                subType: 'click',
                data: {
                    path: this.getElementPath(target),
                    timestamp: Date.now()
                }
            });
        };
        document.addEventListener('click', this.clickHandler);
        
        // 监听路由变化
        this.routeHandler = () => {
            this.callback?.({
                type: 'behavior',
                subType: 'route',
                data: {
                    path: location.pathname,
                    timestamp: Date.now()
                }
            });
        };
        window.addEventListener('popstate', this.routeHandler);
    }
    
    stop(): void {
        document.removeEventListener('click', this.clickHandler);
        window.removeEventListener('popstate', this.routeHandler);
    }
    
    private getElementPath(element: HTMLElement): string {
        const path: string[] = [];
        let current: HTMLElement | null = element;
        
        while (current && current !== document.body) {
            let selector = current.tagName.toLowerCase();
            
            if (current.id) {
                selector += `#${current.id}`;
            } else if (current.className) {
                selector += `.${current.className.split(' ').join('.')}`;
            }
            
            path.unshift(selector);
            current = current.parentElement;
        }
        
        return path.join(' > ');
    }
}

// 业务收集器
class BusinessCollector extends Collector {
    private metrics: Map<string, number> = new Map();
    
    start(): void {
        // 定时上报业务指标
        setInterval(() => {
            this.reportMetrics();
        }, this.config.reportInterval);
    }
    
    stop(): void {
        this.metrics.clear();
    }
    
    // 记录业务指标
    record(name: string, value: number): void {
        this.metrics.set(name, value);
    }
    
    private reportMetrics(): void {
        this.metrics.forEach((value, name) => {
            this.callback?.({
                type: 'business',
                subType: 'metric',
                data: {
                    name,
                    value,
                    timestamp: Date.now()
                }
            });
        });
    }
}

// 处理器基类
abstract class Processor {
    protected config: ProcessorConfig;
    
    constructor(config: ProcessorConfig) {
        this.config = config;
    }
    
    abstract start(): void;
    abstract stop(): void;
    abstract process(data: MonitorData): MonitorData;
}

// 性能处理器
class PerformanceProcessor extends Processor {
    start(): void {}
    
    stop(): void {}
    
    process(data: MonitorData): MonitorData {
        if (data.type !== 'performance') {
            return data;
        }
        
        // 处理性能数据
        switch (data.subType) {
            case 'navigation':
                return this.processNavigation(data);
            case 'resource':
                return this.processResource(data);
            case 'paint':
                return this.processPaint(data);
            default:
                return data;
        }
    }
    
    private processNavigation(data: MonitorData): MonitorData {
        const entry = data.data as PerformanceNavigationTiming;
        return {
            ...data,
            data: {
                dns: entry.domainLookupEnd - entry.domainLookupStart,
                tcp: entry.connectEnd - entry.connectStart,
                ttfb: entry.responseStart - entry.requestStart,
                download: entry.responseEnd - entry.responseStart,
                domReady: entry.domContentLoadedEventEnd - entry.navigationStart,
                load: entry.loadEventEnd - entry.navigationStart
            }
        };
    }
    
    private processResource(data: MonitorData): MonitorData {
        const entry = data.data as PerformanceResourceTiming;
        return {
            ...data,
            data: {
                name: entry.name,
                type: entry.initiatorType,
                duration: entry.duration,
                size: entry.transferSize
            }
        };
    }
    
    private processPaint(data: MonitorData): MonitorData {
        const entry = data.data as PerformancePaintTiming;
        return {
            ...data,
            data: {
                name: entry.name,
                time: entry.startTime
            }
        };
    }
}

// 错误处理器
class ErrorProcessor extends Processor {
    start(): void {}
    
    stop(): void {}
    
    process(data: MonitorData): MonitorData {
        if (data.type !== 'error') {
            return data;
        }
        
        // 处理错误数据
        return {
            ...data,
            data: {
                ...data.data,
                timestamp: Date.now(),
                url: location.href,
                userAgent: navigator.userAgent
            }
        };
    }
}

// 行为处理器
class BehaviorProcessor extends Processor {
    start(): void {}
    
    stop(): void {}
    
    process(data: MonitorData): MonitorData {
        if (data.type !== 'behavior') {
            return data;
        }
        
        // 处理行为数据
        return {
            ...data,
            data: {
                ...data.data,
                url: location.href,
                title: document.title
            }
        };
    }
}

// 业务处理器
class BusinessProcessor extends Processor {
    start(): void {}
    
    stop(): void {}
    
    process(data: MonitorData): MonitorData {
        if (data.type !== 'business') {
            return data;
        }
        
        // 处理业务数据
        return {
            ...data,
            data: {
                ...data.data,
                env: this.config.environment
            }
        };
    }
}

// 上报器基类
abstract class Reporter {
    protected config: ReporterConfig;
    protected queue: MonitorData[] = [];
    protected timer: number | null = null;
    
    constructor(config: ReporterConfig) {
        this.config = config;
    }
    
    start(): void {
        // 定时上报
        this.timer = window.setInterval(() => {
            this.flush();
        }, this.config.flushInterval);
    }
    
    stop(): void {
        if (this.timer) {
            clearInterval(this.timer);
            this.timer = null;
        }
    }
    
    report(data: MonitorData): void {
        this.queue.push(data);
        
        // 队列满时立即上报
        if (this.queue.length >= this.config.maxBatchSize) {
            this.flush();
        }
    }
    
    protected abstract flush(): void;
}

// HTTP上报器
class HttpReporter extends Reporter {
    protected async flush(): void {
        if (this.queue.length === 0) {
            return;
        }
        
        try {
            const data = this.queue.slice();
            this.queue = [];
            
            await fetch(this.config.url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(data)
            });
        } catch (error) {
            console.error('Failed to report data:', error);
            
            // 失败重试
            this.queue.push(...data);
        }
    }
}

// 信标上报器
class BeaconReporter extends Reporter {
    protected flush(): void {
        if (this.queue.length === 0) {
            return;
        }
        
        const data = this.queue.slice();
        this.queue = [];
        
        const blob = new Blob(
            [JSON.stringify(data)],
            { type: 'application/json' }
        );
        
        navigator.sendBeacon(this.config.url, blob);
    }
}

// 日志上报器
class LogReporter extends Reporter {
    protected flush(): void {
        if (this.queue.length === 0) {
            return;
        }
        
        const data = this.queue.slice();
        this.queue = [];
        
        console.log('Monitor Data:', data);
    }
}

// 告警器
class Alerter {
    private config: AlertConfig;
    private rules: Map<string, AlertRule> = new Map();
    private state: Map<string, AlertState> = new Map();
    
    constructor(config: AlertConfig) {
        this.config = config;
    }
    
    start(): void {
        // 初始化告警规则
        this.config.rules.forEach(rule => {
            this.rules.set(rule.id, rule);
        });
    }
    
    stop(): void {
        this.rules.clear();
        this.state.clear();
    }
    
    // 检查告警
    check(data: MonitorData): void {
        this.rules.forEach(rule => {
            if (this.matchRule(rule, data)) {
                this.processAlert(rule, data);
            }
        });
    }
    
    // 匹配规则
    private matchRule(rule: AlertRule, data: MonitorData): boolean {
        // 类型匹配
        if (rule.type !== data.type) {
            return false;
        }
        
        // 子类型匹配
        if (rule.subType && rule.subType !== data.subType) {
            return false;
        }
        
        // 条件匹配
        return this.evaluateCondition(rule.condition, data.data);
    }
    
    // 评估条件
    private evaluateCondition(
        condition: AlertCondition,
        data: any
    ): boolean {
        const value = data[condition.field];
        
        switch (condition.operator) {
            case '>':
                return value > condition.value;
            case '<':
                return value < condition.value;
            case '>=':
                return value >= condition.value;
            case '<=':
                return value <= condition.value;
            case '==':
                return value == condition.value;
            case '!=':
                return value != condition.value;
            default:
                return false;
        }
    }
    
    // 处理告警
    private processAlert(rule: AlertRule, data: MonitorData): void {
        const state = this.state.get(rule.id) || {
            count: 0,
            firstTime: Date.now(),
            lastTime: Date.now()
        };
        
        // 更新状态
        state.count++;
        state.lastTime = Date.now();
        this.state.set(rule.id, state);
        
        // 检查告警条件
        if (this.shouldAlert(rule, state)) {
            this.sendAlert(rule, state, data);
        }
    }
    
    // 判断是否需要告警
    private shouldAlert(rule: AlertRule, state: AlertState): boolean {
        // 检查告警间隔
        const interval = state.lastTime - state.firstTime;
        if (interval < rule.timeWindow) {
            return false;
        }
        
        // 检查告警次数
        return state.count >= rule.threshold;
    }
    
    // 发送告警
    private async sendAlert(
        rule: AlertRule,
        state: AlertState,
        data: MonitorData
    ): Promise<void> {
        const alert: Alert = {
            id: rule.id,
            name: rule.name,
            level: rule.level,
            message: this.formatMessage(rule, state, data),
            time: Date.now()
        };
        
        // 发送到不同渠道
        await Promise.all(
            this.config.channels.map(channel => {
                return this.sendToChannel(channel, alert);
            })
        );
        
        // 重置状态
        this.state.delete(rule.id);
    }
    
    // 格式化告警消息
    private formatMessage(
        rule: AlertRule,
        state: AlertState,
        data: MonitorData
    ): string {
        return `
            告警:${rule.name}
            级别:${rule.level}
            时间:${new Date().toLocaleString()}
            次数:${state.count}
            详情:${JSON.stringify(data)}
        `;
    }
    
    // 发送到告警渠道
    private async sendToChannel(
        channel: AlertChannel,
        alert: Alert
    ): Promise<void> {
        try {
            await fetch(channel.url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    channel: channel.type,
                    alert
                })
            });
        } catch (error) {
            console.error(
                `Failed to send alert to ${channel.type}:`,
                error
            );
        }
    }
}

// 接口定义
interface MonitorConfig {
    appId: string;
    userId: string;
    sessionId: string;
    environment: string;
    version: string;
    sampling: number;
    reportUrl?: string;
    reportType?: string;
    alertRules?: AlertRule[];
    alertChannels?: AlertChannel[];
}

interface CollectorConfig {
    reportInterval?: number;
}

interface ProcessorConfig {
    environment: string;
}

interface ReporterConfig {
    url: string;
    flushInterval: number;
    maxBatchSize: number;
}

interface AlertConfig {
    rules: AlertRule[];
    channels: AlertChannel[];
}

interface MonitorData {
    type: string;
    subType: string;
    data: any;
}

interface AlertRule {
    id: string;
    name: string;
    type: string;
    subType?: string;
    level: 'info' | 'warning' | 'error' | 'critical';
    condition: AlertCondition;
    timeWindow: number;
    threshold: number;
}

interface AlertCondition {
    field: string;
    operator: '>' | '<' | '>=' | '<=' | '==' | '!=';
    value: any;
}

interface AlertState {
    count: number;
    firstTime: number;
    lastTime: number;
}

interface Alert {
    id: string;
    name: string;
    level: string;
    message: string;
    time: number;
}

interface AlertChannel {
    type: string;
    url: string;
}

// 使用示例
const monitor = MonitoringManager.getInstance();

// 初始化监控
monitor.init({
    appId: 'my-app',
    userId: 'user-123',
    sessionId: 'session-456',
    environment: 'production',
    version: '1.0.0',
    sampling: 100,
    reportUrl: 'https://monitor.example.com/report',
    reportType: 'http',
    alertRules: [
        {
            id: 'error-rate',
            name: '错误率告警',
            type: 'error',
            level: 'error',
            condition: {
                field: 'count',
                operator: '>',
                value: 10
            },
            timeWindow: 60000,
            threshold: 5
        }
    ],
    alertChannels: [
        {
            type: 'webhook',
            url: 'https://alert.example.com/webhook'
        }
    ]
});

// 手动上报
monitor.report({
    type: 'business',
    subType: 'conversion',
    data: {
        name: 'purchase',
        value: 100
    }
});

// 停止监控
monitor.stop();

最佳实践与建议

  1. 监控范围

    • 性能指标
    • 错误信息
    • 用户行为
    • 业务数据
  2. 采集策略

    • 采样控制
    • 批量上报
    • 优先级分级
    • 实时性要求
  3. 告警机制

    • 规则配置
    • 级别定义
    • 通知方式
    • 处理流程
  4. 数据处理

    • 数据清洗
    • 聚合分析
    • 存储策略
    • 查询优化

总结

前端监控系统需要考虑以下方面:

  1. 监控指标的全面性
  2. 数据采集的可靠性
  3. 处理分析的及时性
  4. 告警通知的准确性
  5. 系统运维的可维护性

通过完善的监控体系,可以及时发现和解决问题,提高应用质量。

学习资源

  1. 性能监控指标
  2. 错误监控方案
  3. 用户行为分析
  4. 告警系统设计
  5. 监控平台实践

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

终身学习,共同成长。

咱们下一期见

💻


http://www.kler.cn/a/590604.html

相关文章:

  • SpringMVC(八)Knife4j 接口文档
  • Anaconda 入门指南
  • 在群晖DS923+手动安装我Wordpress最新版
  • 定义模型生成数据表
  • rust 中的package、crate、module
  • python打印输出到文件
  • Go Ebiten小游戏开发:2048
  • Qt5.15.2实现Qt for WebAssembly与示例
  • 学习单片机需要多长时间才能进行简单的项目开发?
  • 04 1个路由器配置一个子网的dhcp服务
  • Java爬虫如何处理动态加载的内容?
  • RTSP/Onvif安防视频EasyNVR平台 vs.多协议接入视频汇聚EasyCVR平台:设备分组的区别
  • 在Android中,子线程可以更新UI吗
  • Vue相关面试题
  • 单片机开发资源分析的实战——以STM32F103C8T6为例子的单片机资源分析
  • 【Pycharm】Pycharm创建.py文件时自动生成文件头
  • 主流开源大模型能力对比矩阵
  • 【HDLBits】Module合集(练习均用两种方法解决)
  • 关于软航OFFICE文档控件软件在Chrome 133版本上提示扩展已停用的原因及处理办法
  • 【C++标准库类型】深入理解vector类型(1):从基础到实践