前端异常监控系统设计与实现

概述

前端监控包括行为监控、异常监控、性能监控等,本文主要讨论异常监控。对于前端而言,和后端处于同一个监控系统中,前端有自己的监控方案,后端也有自己的监控方案,但两者并不分离。因为一个用户在操作应用过程中如果出现异常,有可能是前端引起,也有可能是后端引起,需要有一个机制将前后端串联起来,使监控本身统一于监控系统。

因此,即使只讨论前端异常监控,其实也不能严格区分前后端界限,而要根据实际系统的设计,在最终的报表中体现出监控对开发和业务的帮助。

一般而言,一个监控系统,大致可以分为四个阶段:

  1. 日志采集 - 收集异常日志,先在本地做一定的处理,采取一定的方案上报到服务器

  2. 日志存储 - 后端接收前端上报的异常日志,经过一定处理,按照一定的存储方案存储

  3. 统计与分析 - 分为机器自动分析和人工分析

  4. 报告和警告 - 分为告警和预警

1 前端异常

前端异常是指在用户使用Web应用时无法快速得到符合预期结果的情况,不同的异常带来的后果程度不同,轻则引起用户使用不悦,重则导致产品无法使用,使用户丧失对产品的认可。

1.1 前端异常分类

根据异常代码的后果的程度,对前端异常的表现分为如下几类:

类别描述影响程度
出错界面呈现的内容与用户预期的内容不符功能可用但用户体验差
呆滞界面出现操作后没有反应的现象界面级局部不可用
损坏界面出现无法实现操作目的的现象部分功能无法正常使用
假死界面出现卡顿,无法对任何功能进行使用的现象用户很可能杀死应用
崩溃应用出现经常性自动退出或无法操作的现象直接导致用户流失

1.2 异常错误原因分类

前端产生异常的原因主要分5类:

原因案例频率
逻辑错误业务逻辑判断条件错误、事件绑定顺序错误、调用栈时序错误、错误的操作js对象经常
数据类型错误将null视作对象读取property、将undefined视作数组进行遍历、将字符串形式的数字直接用于加运算、函数参数未传经常
语法句法错误-较少
网络错误慢、服务端未返回数据但仍200、提交数据时网络中断、服务端500错误时前端未做任何错误处理偶尔
系统错误内存不够用、磁盘塞满、壳不支持API、不兼容较少

2 异常采集

2.1 采集内容

当异常出现的时候,我们需要知道异常的具体信息,根据异常的具体信息来决定采用什么样的解决方案。在采集异常信息时,可以遵循4W原则:

WHO did WHAT and get WHICH exception in WHICH environment?

a. 用户信息

出现异常时该用户的信息,例如该用户在当前时刻的状态、权限等,以及需要区分用户可多终端登录时,异常对应的是哪一个终端。

b. 行为信息

用户进行什么操作时产生了异常:所在的界面路径;执行了什么操作;操作时使用了哪些数据;当时的API吐了什么数据给客户端;如果是提交操作,提交了什么数据;上一个路径;上一个行为日志记录ID等。

c. 异常信息

产生异常的代码信息:用户操作的DOM元素节点;异常级别;异常类型;异常描述;代码stack信息等。

d. 环境信息

网络环境;设备型号和标识码;操作系统版本;客户端版本;API接口版本等。

完整日志字段表:

字段类型解释
requestIdString一个界面产生一个requestId
traceIdString一个阶段产生一个traceId,用于追踪和一个异常相关的所有日志记录
hashString这条log的唯一标识码,相当于logId
timeNumber当前日志产生的时间(保存时刻)
userIdString用户ID
userStatusNumber用户状态信息(是否可用/禁用)
userRolesArray用户的角色列表
userGroupsArray用户当前所在组
userLicensesArray许可证信息
pathString所在路径,URL
actionString进行了什么操作
refererString上一个路径,来源URL
prevActionString上一个操作
dataObject当前界面的state、data
dataSourcesArray<Object>上游api给了什么数据
dataSendObject提交了什么数据
targetElementHTMLElement用户操作的DOM元素
targetDOMPathArray<HTMLElement>该DOM元素的节点路径
targetCSSObject该元素的自定义样式表
targetAttrsObject该元素当前的属性及值
errorTypeString错误类型
errorLevelString异常级别
errorStackString错误stack信息
errorFilenameString出错文件
errorLineNoNumber出错行
errorColNoNumber出错列位置
errorMessageString错误描述(开发者定义)
errorTimeStampNumber时间戳
eventTypeString事件类型
pageXNumber事件x轴坐标
pageYNumber事件y轴坐标
screenXNumber事件x轴坐标
screenYNumber事件y轴坐标
pageWNumber页面宽度
pageHNumber页面高度
screenWNumber屏幕宽度
screenHNumber屏幕高度
eventKeyString触发事件的键
networkString网络环境描述
userAgentString客户端描述
deviceString设备描述
systemString操作系统描述
appVersionString应用版本
apiVersionString接口版本

2.2 异常捕获

前端捕获异常分为全局捕获和单点捕获。

a. 全局捕获

通过全局的接口,将捕获代码集中写在一个地方:

javascript

// 全局错误捕获
window.addEventListener('error', function(event) {
    // 处理错误信息
    const errorInfo = {
        message: event.message,
        filename: event.filename,
        lineno: event.lineno,
        colno: event.colno,
        error: event.error
    };
    // 上报错误
    reportError(errorInfo);
});

// 未处理的Promise rejection
window.addEventListener('unhandledrejection', function(event) {
    // 处理Promise错误
    reportError({
        type: 'promise',
        reason: event.reason
    });
});
b. 单点捕获

在业务代码中对单个代码块进行包裹,或在逻辑流程中打点:

javascript

// try-catch 包裹重要代码块
try {
    // 业务逻辑代码
    riskyOperation();
} catch (error) {
    // 记录错误信息
    logError({
        type: 'business',
        error: error,
        context: 'riskyOperation'
    });
}

// 重要函数包装
function monitoredFunction(originalFunction) {
    return function(...args) {
        try {
            const result = originalFunction.apply(this, args);
            return result;
        } catch (error) {
            logError({
                function: originalFunction.name,
                args: args,
                error: error
            });
            throw error;
        }
    };
}

2.3 跨域脚本异常

由于浏览器安全策略限制,跨域脚本报错时,无法直接获取错误的详细信息,只能得到一个Script Error

解决方案:

方案一: 配置CORS

html

<script src="http://cdn.example.com/script.js" crossorigin="anonymous"></script>

javascript

// 服务端设置
app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
    next();
});

方案二: 使用try-catch包装

javascript

// 对可能出错的跨域脚本进行包装
function safeRequire(url) {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = url;
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
    });
}

try {
    await safeRequire('http://cdn.example.com/script.js');
} catch (error) {
    logError({
        type: 'cross_origin',
        url: 'http://cdn.example.com/script.js',
        error: error
    });
}

2.4 异常录制

异常录制通过"时间""空间"两个维度记录异常发生前到发生的整个过程,帮助还原异常现场。

录制原理:

  • 记录用户的操作过程(events和mutation)

  • 上传到服务器进行处理和存储

  • 重现时按顺序播放记录

2.5 异常级别

将异常划分到"重要——紧急"模型中:

级别描述处理优先级
A级紧急重要,需要快速响应立即处理
B级重要不紧急高优先级
C级紧急不重要中优先级
D级不紧急不重要低优先级

3 整理与上报方案

3.1 前端存储日志

存储方案对比:

存储方式类型数据格式容量进程检索性能
Cookiekey-valuestring4k同步key-
localStoragekey-valuestring5M同步key读快写慢
sessionStoragekey-valuestring5M同步key读快写慢
IndexedDBNoSQLobject500M异步key, index读慢写快
webSQLSQL-60M异步field-
FileSystem---异步--

推荐方案:IndexedDB

javascript

// 使用IndexedDB存储日志
class LogStorage {
    constructor() {
        this.dbName = 'FrontendLogs';
        this.version = 1;
    }
    
    async init() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open(this.dbName, this.version);
            
            request.onerror = () => reject(request.error);
            request.onsuccess = () => {
                this.db = request.result;
                resolve(this.db);
            };
            
            request.onupgradeneeded = (event) => {
                const db = event.target.result;
                if (!db.objectStoreNames.contains('logs')) {
                    const store = db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
                    store.createIndex('timestamp', 'timestamp', { unique: false });
                    store.createIndex('level', 'level', { unique: false });
                }
            };
        });
    }
    
    async addLog(log) {
        const transaction = this.db.transaction(['logs'], 'readwrite');
        const store = transaction.objectStore('logs');
        return store.add({
            ...log,
            timestamp: Date.now()
        });
    }
}

3.2 前端整理日志

日志整理流程:

javascript

// 日志整理Worker
class LogProcessor {
    processLog(rawLog) {
        // 1. 分类日志
        const categorizedLog = this.categorizeLog(rawLog);
        
        // 2. 生成唯一标识
        const hash = this.generateHash(categorizedLog);
        
        // 3. 丰富日志信息
        const enrichedLog = this.enrichLog(categorizedLog);
        
        // 4. 建立索引
        this.createIndex(enrichedLog, hash);
        
        return { log: enrichedLog, hash };
    }
    
    categorizeLog(log) {
        // 根据type字段分类
        const categories = {
            'error': 'error',
            'warn': 'warning', 
            'info': 'information',
            'user_action': 'behavior'
        };
        
        return {
            ...log,
            category: categories[log.type] || 'unknown'
        };
    }
    
    generateHash(log) {
        // 使用object-hashcode生成唯一hash
        const str = JSON.stringify(log);
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            const char = str.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash;
        }
        return hash.toString();
    }
}

3.3 上报日志

四种上报策略:

a. 即时上报

javascript

class ImmediateReporter {
    async report(log) {
        try {
            const response = await fetch('/api/logs/immediate', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(log)
            });
            
            if (response.ok) {
                return true;
            } else {
                // 失败重试
                return this.retry(log);
            }
        } catch (error) {
            return this.retry(log);
        }
    }
    
    async retry(log, maxRetries = 3) {
        for (let i = 0; i < maxRetries; i++) {
            try {
                await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
                const response = await fetch('/api/logs/immediate', {
                    method: 'POST',
                    body: JSON.stringify(log)
                });
                if (response.ok) return true;
            } catch (error) {
                console.warn(`上报失败,第${i + 1}次重试`);
            }
        }
        return false;
    }
}
b. 批量上报

javascript

class BatchReporter {
    constructor() {
        this.batch = [];
        this.batchSize = 100;
        this.flushInterval = 60000; // 1分钟
        this.startFlushTimer();
    }
    
    addLog(log) {
        this.batch.push(log);
        if (this.batch.length >= this.batchSize) {
            this.flush();
        }
    }
    
    startFlushTimer() {
        setInterval(() => {
            if (this.batch.length > 0) {
                this.flush();
            }
        }, this.flushInterval);
    }
    
    async flush() {
        if (this.batch.length === 0) return;
        
        const batchToSend = [...this.batch];
        this.batch = [];
        
        try {
            await fetch('/api/logs/batch', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(batchToSend)
            });
        } catch (error) {
            // 失败后重新加入队列
            this.batch.unshift(...batchToSend);
        }
    }
}
c. 区块上报

javascript

class BlockReporter {
    async reportBlock(traceId) {
        // 1. 获取与traceId相关的所有日志
        const relatedLogs = await this.getRelatedLogs(traceId);
        
        // 2. 过滤已上报的日志
        const newLogs = await this.filterReportedLogs(relatedLogs);
        
        // 3. 打包上报
        if (newLogs.length > 0) {
            await this.sendBlock(newLogs, traceId);
        }
    }
    
    async getRelatedLogs(traceId) {
        // 从存储中获取相关日志
        return indexedDB.getAll('logs')
            .then(logs => logs.filter(log => log.traceId === traceId));
    }
}
d. 用户主动提交

javascript

class UserFeedback {
    showFeedbackDialog(error) {
        const dialog = document.createElement('div');
        dialog.innerHTML = `
            <div class="feedback-dialog">
                <h3>遇到问题了吗?</h3>
                <p>请描述您遇到的问题:</p>
                <textarea id="feedback-desc"></textarea>
                <button id="submit-feedback">提交反馈</button>
                <button id="cancel-feedback">取消</button>
            </div>
        `;
        
        document.getElementById('submit-feedback').onclick = () => {
            this.submitFeedback(error);
        };
    }
    
    async submitFeedback(error) {
        const description = document.getElementById('feedback-desc').value;
        await fetch('/api/feedback', {
            method: 'POST',
            body: JSON.stringify({
                error,
                description,
                timestamp: Date.now()
            })
        });
    }
}

3.4 压缩上报数据

javascript

class CompressedReporter {
    async sendCompressed(logs) {
        // 使用lz-string压缩
        const dataStr = JSON.stringify(logs);
        const compressed = LZString.compress(dataStr);
        
        // 或者使用gzip压缩
        // const compressed = pako.gzip(dataStr);
        
        await fetch('/api/logs/compressed', {
            method: 'POST',
            headers: {
                'Content-Encoding': 'lz-string', // 或 'gzip'
                'Content-Type': 'application/octet-stream'
            },
            body: compressed
        });
    }
}

4 日志接收与存储

4.1 接入层与消息队列

系统架构示意图:

text

客户端 → 接入层 → 消息队列 → 处理服务 → 存储系统

4.2 日志存储系统

主流日志存储方案对比:

系统类型代表产品特点适用场景
Hbase系HBase, BigTable列式存储,高吞吐大数据量,随机读写
Dremel系BigQuery, DrillSQL接口,快速查询交互式分析
Lucene系ElasticSearch, Solr全文检索,实时搜索日志检索,监控

4.3 搜索方案

ELK Stack架构:

  • ElasticSearch - 存储、索引、搜索

  • Logstash - 日志收集、处理、转发

  • Kibana - 数据可视化、查询统计

5 日志统计与分析

5.1 用户纬度分析

追踪用户的操作路径和行为模式,分析异常对特定用户群体的影响。

5.2 时间维度分析

分析异常发生的时间规律,识别高峰期和异常模式。

5.3 性能维度分析

监控应用性能指标,关联性能异常与业务异常。

5.4 运行环境维度

分析不同环境下的异常分布,识别环境相关的问题。

5.5 细粒度代码追踪

利用sourcemap还原压缩代码,精确定位问题。

5.6 场景回溯

通过日志回放重现异常发生过程。

6 监控与通知

6.1 自定义触发条件的告警

javascript

class AlertSystem {
    constructor() {
        this.rules = [];
        this.loadRules();
    }
    
    addRule(rule) {
        this.rules.push(rule);
    }
    
    checkAlerts(log) {
        for (const rule of this.rules) {
            if (this.evaluateRule(rule, log)) {
                this.triggerAlert(rule, log);
            }
        }
    }
    
    evaluateRule(rule, log) {
        // 根据规则条件评估是否需要告警
        return rule.conditions.every(condition => {
            switch (condition.operator) {
                case 'eq': return log[condition.field] === condition.value;
                case 'gt': return log[condition.field] > condition.value;
                case 'lt': return log[condition.field] < condition.value;
                case 'contains': return log[condition.field].includes(condition.value);
                default: return false;
            }
        });
    }
    
    triggerAlert(rule, log) {
        // 触发告警
        this.sendNotification(rule, log);
        this.createIncident(rule, log);
    }
}

6.2 推送渠道

  • 邮件通知

  • 短信提醒

  • 微信/钉钉消息

  • 电话告警

6.3 推送频率控制

javascript

class NotificationManager {
    constructor() {
        this.sentAlerts = new Map();
    }
    
    shouldSend(alertKey, cooldown) {
        const lastSent = this.sentAlerts.get(alertKey);
        if (!lastSent) return true;
        
        return Date.now() - lastSent > cooldown;
    }
    
    markSent(alertKey) {
        this.sentAlerts.set(alertKey, Date.now());
    }
}

6.4 自动报表系统

javascript

class ReportGenerator {
    async generateDailyReport() {
        const stats = await this.collectDailyStats();
        const template = this.loadReportTemplate();
        const report = this.renderReport(template, stats);
        await this.sendReport(report);
    }
    
    async collectDailyStats() {
        return {
            totalErrors: await this.getErrorCount(),
            uniqueUsersAffected: await this.getAffectedUserCount(),
            topErrors: await this.getTopErrors(),
            resolutionRate: await this.getResolutionRate()
        };
    }
}

6.5 自动产生bug工单

javascript

class BugTrackerIntegration {
    async createBugTicket(error) {
        const ticket = {
            title: `[自动] ${error.message}`,
            description: this.generateBugDescription(error),
            severity: this.calculateSeverity(error),
            labels: ['auto-generated', 'frontend'],
            metadata: {
                errorHash: error.hash,
                traceId: error.traceId
            }
        };
        
        // 调用工单系统API
        const response = await fetch('/api/bug-tracker/tickets', {
            method: 'POST',
            body: JSON.stringify(ticket)
        });
        
        return response.json();
    }
}

7 修复异常

7.1 SourceMap解析

javascript

class SourceMapProcessor {
    async parseErrorStack(stack, sourceMap) {
        const lines = stack.split('\n');
        const parsedStack = [];
        
        for (const line of lines) {
            const match = line.match(/at (.+?) \((.+?):(\d+):(\d+)\)/);
            if (match) {
                const [, method, file, lineNo, column] = match;
                const originalPosition = await this.mapPosition(
                    file, parseInt(lineNo), parseInt(column), sourceMap
                );
                
                parsedStack.push({
                    method,
                    originalFile: originalPosition.source,
                    originalLine: originalPosition.line,
                    originalColumn: originalPosition.column
                });
            }
        }
        
        return parsedStack;
    }
}

7.2 从告警到预警

javascript

class PredictiveAlert {
    analyzePatterns(historicalData) {
        // 使用机器学习算法分析历史数据
        const patterns = this.mlAlgorithm.detectPatterns(historicalData);
        
        patterns.forEach(pattern => {
            this.alertRules.addPredictiveRule(pattern);
        });
    }
    
    checkPredictiveConditions(currentMetrics) {
        return this.alertRules.checkPredictiveRules(currentMetrics);
    }
}

7.3 智能修复

javascript

class AutoFixSystem {
    suggestFix(error) {
        const fixes = this.fixDatabase.query({
            errorType: error.type,
            errorMessage: error.message,
            context: error.context
        });
        
        return fixes.length > 0 ? fixes[0] : null;
    }
    
    async applyDataModelFix(apiResponse, expectedModel) {
        // 自动修正数据类型
        return this.dataTransformer.transform(apiResponse, expectedModel);
    }
}

8 异常测试

8.1 主动异常测试

javascript

class ExceptionTesting {
    constructor() {
        this.testCases = this.loadTestCases();
    }
    
    async runTestSuite() {
        for (const testCase of this.testCases) {
            try {
                await this.executeTestCase(testCase);
                this.recordResult(testCase, 'passed');
            } catch (error) {
                this.recordResult(testCase, 'failed', error);
            }
        }
    }
    
    addTestCase(condition, expectedException) {
        this.testCases.push({
            condition,
            expectedException,
            timestamp: Date.now()
        });
    }
}

8.2 随机异常测试

javascript

class RandomTesting {
    async startRandomTesting() {
        while (this.isRunning) {
            const randomAction = this.generateRandomAction();
            try {
                await this.performAction(randomAction);
                await this.delay(this.getRandomDelay());
            } catch (error) {
                this.recordUnexpectedError(error, randomAction);
            }
        }
    }
    
    generateRandomAction() {
        const actions = ['click', 'scroll', 'input', 'navigate'];
        const elements = ['button', 'input', 'link', 'dropdown'];
        
        return {
            type: actions[Math.floor(Math.random() * actions.length)],
            target: elements[Math.floor(Math.random() * elements.length)],
            data: this.generateRandomData()
        };
    }
}

9 部署

9.1 多客户端支持

javascript

class CrossPlatformMonitoring {
    generateRequestId(userId, deviceId, timestamp) {
        return `${userId}-${deviceId}-${timestamp}-${Math.random().toString(36).substr(2, 9)}`;
    }
    
    trackUserSession(userId, deviceInfo) {
        const session = {
            userId,
            deviceId: deviceInfo.id,
            platform: deviceInfo.platform,
            startTime: Date.now(),
            requestId: this.generateRequestId(userId, deviceInfo.id, Date.now())
        };
        
        this.activeSessions.set(session.requestId, session);
        return session.requestId;
    }
}

9.2 集成便捷性

javascript

// 前端SDK
class MonitoringSDK {
    init(config) {
        this.config = config;
        this.setupGlobalHandlers();
        this.startBackgroundTasks();
        return this;
    }
    
    trackError(error, context) {
        const log = this.buildLogEntry('error', { error, ...context });
        this.storage.addLog(log);
    }
    
    trackUserAction(action, data) {
        const log = this.buildLogEntry('user_action', { action, data });
        this.storage.addLog(log);
    }
}

// 使用示例
const monitor = new MonitoringSDK().init({
    appId: 'your-app-id',
    endpoint: 'https://logs.yourdomain.com',
    samplingRate: 1.0
});

9.3 管理系统可扩展性

javascript

class MultiTenantSystem {
    constructor() {
        this.applications = new Map();
    }
    
    registerApplication(appConfig) {
        const app = {
            id: appConfig.id,
            name: appConfig.name,
            settings: appConfig.settings,
            createdAt: Date.now()
        };
        
        this.applications.set(app.id, app);
        this.setupApplicationResources(app);
    }
    
    setupApplicationResources(app) {
        // 为每个应用创建独立的存储空间和配置
        this.createLogDatabase(app.id);
        this.setupAlertRules(app.id, app.settings.alertRules);
    }
}

9.4 日志系统权限管理

javascript

class PermissionManager {
    constructor() {
        this.roles = {
            viewer: ['read_logs', 'read_reports'],
            developer: ['read_logs', 'read_reports', 'create_bugs'],
            admin: ['read_logs', 'read_reports', 'manage_rules', 'manage_users']
        };
    }
    
    checkPermission(user, resource, action) {
        const userRoles = user.roles || [];
        const permissions = new Set();
        
        userRoles.forEach(role => {
            (this.roles[role] || []).forEach(perm => permissions.add(perm));
        });
        
        return permissions.has(this.getPermissionString(resource, action));
    }
    
    maskSensitiveData(log, userPermissions) {
        if (!userPermissions.includes('view_sensitive_data')) {
            return this.applyDataMasking(log);
        }
        return log;
    }
}

10 其他监控维度

10.1 性能监控

javascript

class PerformanceMonitor {
    trackMetrics() {
        // 核心性能指标
        this.trackFP(); // 首次绘制
        this.trackFCP(); // 首次内容绘制
        this.trackLCP(); // 最大内容绘制
        this.trackFID(); // 首次输入延迟
        this.trackCLS(); // 累积布局偏移
    }
    
    trackFP() {
        const observer = new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
                if (entry.name === 'first-paint') {
                    this.reportMetric('first_paint', entry.startTime);
                }
            }
        });
        observer.observe({ entryTypes: ['paint'] });
    }
}

10.2 API监控

javascript

class APIMonitor {
    interceptAPIRequests() {
        const originalFetch = window.fetch;
        window.fetch = async (...args) => {
            const startTime = Date.now();
            try {
                const response = await originalFetch.apply(this, args);
                const duration = Date.now() - startTime;
                
                this.trackAPICall({
                    url: args[0],
                    method: args[1]?.method || 'GET',
                    status: response.status,
                    duration,
                    timestamp: startTime
                });
                
                return response;
            } catch (error) {
                const duration = Date.now() - startTime;
                this.trackAPIFailure({
                    url: args[0],
                    method: args[1]?.method || 'GET',
                    error: error.message,
                    duration,
                    timestamp: startTime
                });
                throw error;
            }
        };
    }
}

10.3 数据脱敏

javascript

class DataMasking {
    constructor(rules) {
        this.maskingRules = rules;
    }
    
    applyMasking(data) {
        if (typeof data !== 'object' || data === null) {
            return data;
        }
        
        const masked = Array.isArray(data) ? [] : {};
        
        for (const [key, value] of Object.entries(data)) {
            if (this.shouldMaskField(key)) {
                masked[key] = this.maskValue(value, key);
            } else if (typeof value === 'object' && value !== null) {
                masked[key] = this.applyMasking(value);
            } else {
                masked[key] = value;
            }
        }
        
        return masked;
    }
    
    shouldMaskField(fieldName) {
        return this.maskingRules.some(rule => 
            fieldName.match(rule.pattern)
        );
    }
    
    maskValue(value, fieldName) {
        const rule = this.maskingRules.find(r => fieldName.match(r.pattern));
        if (!rule) return value;
        
        switch (rule.strategy) {
            case 'hash':
                return this.hashValue(value);
            case 'partial':
                return this.partialMask(value);
            case 'redact':
                return '[REDACTED]';
            default:
                return value;
        }
    }
}

结语

本文系统性地探讨了前端异常监控的整体框架设计,从前端异常的分类、采集、存储、分析到告警和修复,涵盖了监控系统的完整生命周期。一个优秀的前端监控系统应该具备:

  1. 全面性 - 覆盖各种异常类型和场景

  2. 实时性 - 快速发现和响应问题

  3. 准确性 - 精确定位问题根源

  4. 智能性 - 自动分析和建议解决方案

  5. 可扩展性 - 支持多应用、多团队协作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值