概述
前端监控包括行为监控、异常监控、性能监控等,本文主要讨论异常监控。对于前端而言,和后端处于同一个监控系统中,前端有自己的监控方案,后端也有自己的监控方案,但两者并不分离。因为一个用户在操作应用过程中如果出现异常,有可能是前端引起,也有可能是后端引起,需要有一个机制将前后端串联起来,使监控本身统一于监控系统。
因此,即使只讨论前端异常监控,其实也不能严格区分前后端界限,而要根据实际系统的设计,在最终的报表中体现出监控对开发和业务的帮助。
一般而言,一个监控系统,大致可以分为四个阶段:
-
日志采集 - 收集异常日志,先在本地做一定的处理,采取一定的方案上报到服务器
-
日志存储 - 后端接收前端上报的异常日志,经过一定处理,按照一定的存储方案存储
-
统计与分析 - 分为机器自动分析和人工分析
-
报告和警告 - 分为告警和预警
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接口版本等。
完整日志字段表:
| 字段 | 类型 | 解释 |
|---|---|---|
| requestId | String | 一个界面产生一个requestId |
| traceId | String | 一个阶段产生一个traceId,用于追踪和一个异常相关的所有日志记录 |
| hash | String | 这条log的唯一标识码,相当于logId |
| time | Number | 当前日志产生的时间(保存时刻) |
| userId | String | 用户ID |
| userStatus | Number | 用户状态信息(是否可用/禁用) |
| userRoles | Array | 用户的角色列表 |
| userGroups | Array | 用户当前所在组 |
| userLicenses | Array | 许可证信息 |
| path | String | 所在路径,URL |
| action | String | 进行了什么操作 |
| referer | String | 上一个路径,来源URL |
| prevAction | String | 上一个操作 |
| data | Object | 当前界面的state、data |
| dataSources | Array<Object> | 上游api给了什么数据 |
| dataSend | Object | 提交了什么数据 |
| targetElement | HTMLElement | 用户操作的DOM元素 |
| targetDOMPath | Array<HTMLElement> | 该DOM元素的节点路径 |
| targetCSS | Object | 该元素的自定义样式表 |
| targetAttrs | Object | 该元素当前的属性及值 |
| errorType | String | 错误类型 |
| errorLevel | String | 异常级别 |
| errorStack | String | 错误stack信息 |
| errorFilename | String | 出错文件 |
| errorLineNo | Number | 出错行 |
| errorColNo | Number | 出错列位置 |
| errorMessage | String | 错误描述(开发者定义) |
| errorTimeStamp | Number | 时间戳 |
| eventType | String | 事件类型 |
| pageX | Number | 事件x轴坐标 |
| pageY | Number | 事件y轴坐标 |
| screenX | Number | 事件x轴坐标 |
| screenY | Number | 事件y轴坐标 |
| pageW | Number | 页面宽度 |
| pageH | Number | 页面高度 |
| screenW | Number | 屏幕宽度 |
| screenH | Number | 屏幕高度 |
| eventKey | String | 触发事件的键 |
| network | String | 网络环境描述 |
| userAgent | String | 客户端描述 |
| device | String | 设备描述 |
| system | String | 操作系统描述 |
| appVersion | String | 应用版本 |
| apiVersion | String | 接口版本 |
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 前端存储日志
存储方案对比:
| 存储方式 | 类型 | 数据格式 | 容量 | 进程 | 检索 | 性能 |
|---|---|---|---|---|---|---|
| Cookie | key-value | string | 4k | 同步 | key | - |
| localStorage | key-value | string | 5M | 同步 | key | 读快写慢 |
| sessionStorage | key-value | string | 5M | 同步 | key | 读快写慢 |
| IndexedDB | NoSQL | object | 500M | 异步 | key, index | 读慢写快 |
| webSQL | SQL | - | 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, Drill | SQL接口,快速查询 | 交互式分析 |
| 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;
}
}
}
结语
本文系统性地探讨了前端异常监控的整体框架设计,从前端异常的分类、采集、存储、分析到告警和修复,涵盖了监控系统的完整生命周期。一个优秀的前端监控系统应该具备:
-
全面性 - 覆盖各种异常类型和场景
-
实时性 - 快速发现和响应问题
-
准确性 - 精确定位问题根源
-
智能性 - 自动分析和建议解决方案
-
可扩展性 - 支持多应用、多团队协作
2857

被折叠的 条评论
为什么被折叠?



