前端小食堂 | Day16 - 前端监控の天眼通
👁️ 今日天眼:错误追踪与性能透视
1. 错误监控の捕虫网
// 🐞 全局错误捕获
window.addEventListener('error', (e) => {
sendToServer({
type: 'JS_ERROR',
message: e.message,
stack: e.error?.stack,
filename: e.filename,
lineno: e.lineno
});
});
// 🌩️ Promise未捕获异常
window.addEventListener('unhandledrejection', (e) => {
trackError({
type: 'PROMISE_REJECTION',
reason: e.reason?.message || String(e.reason)
});
});
// 🕸️ Vue组件错误边界
app.config.errorHandler = (err, vm, info) => {
console.error('🔥 组件崩溃:', err, `触发位置: ${info}`);
sentry.captureException(err); // 接入Sentry
};
🔔 核心配置:
- 接入 Sentry/LogRocket 等专业工具
- 自动记录用户操作路径
- 生产环境开启 SourceMap 映射
2. 性能分析の时光机
// 🚀 关键性能指标采集
const perfData = {
FP: performance.getEntriesByName('first-paint')[0]?.startTime,
FCP: performance.getEntriesByName('first-contentful-paint')[0]?.startTime,
LCP: performance.getEntriesByName('largest-contentful-paint')[0]?.startTime,
FID: performance.getEntriesByName('first-input-delay')[0]?.duration,
};
// 🧪 Performance Observer 监听
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
console.log(`📊 [${entry.name}]: ${entry.startTime.toFixed(2)}ms`);
});
});
observer.observe({ entryTypes: ['navigation', 'resource', 'paint'] });
// 📈 资源加载统计
const resources = performance.getEntriesByType('resource');
const slowResources = resources.filter(r => r.duration > 1000);
3. 用户体验温度计
// 😠 用户行为异常检测
let rageClickCount = 0;
useEventListener('click', (e) => {
if (e.target.closest('.error-message')) {
rageClickCount++;
if (rageClickCount > 3) {
trackEvent('RAGE_CLICK', { element: e.target.tagName });
}
}
}, { passive: true });
// ⏳ 长任务监控
const longTasks = [];
new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
if (entry.duration > 50) {
longTasks.push(entry);
console.warn(`⚠️ 长任务阻塞 ${entry.duration.toFixed(2)}ms`);
}
});
}).observe({ type: 'longtask', buffered: true });
// 🌐 网络请求统计
axios.interceptors.response.use(null, (error) => {
trackApiError(error.config.url, error.response?.status);
return Promise.reject(error);
});
❄️ 冷知识:Console 魔法咒语
// 🔍 精准调试定位
console.log('%c🔥 关键数据:', 'color: #ff4757;', data); // 彩色日志
console.table(dataList); // 表格展示
console.trace('调用堆栈追踪'); // 堆栈跟踪
// 🕵️ 条件式日志
const debug = import.meta.env.DEV;
const smartLog = debug ? console.log : () => {};
smartLog('开发环境专属日志');
// 🎯 性能标记
performance.mark('searchStart');
// ... 搜索逻辑 ...
performance.mark('searchEnd');
performance.measure('搜索耗时', 'searchStart', 'searchEnd');
🌟 实验室天眼系统
实现简易监控 SDK
class MiniMonitor {
constructor() {
this.errors = [];
this.performance = {};
this.init();
}
init() {
// 错误监听
window.addEventListener('error', this.captureError.bind(this));
// 性能采集
window.addEventListener('load', () => {
this.performance = {
FP: performance.getEntriesByName('first-paint')[0]?.startTime,
FCP: performance.getEntriesByName('first-contentful-paint')[0]?.startTime,
LoadTime: performance.timing.loadEventEnd - performance.timing.navigationStart
};
});
// 心跳检测
setInterval(() => this.sendData(), 10000);
}
captureError(e) {
this.errors.push({
type: e.error ? 'JS_ERROR' : 'RESOURCE_ERROR',
message: e.message || e.target.src,
timestamp: Date.now()
});
}
sendData() {
navigator.sendBeacon('/api/monitor', {
errors: this.errors,
performance: this.performance
});
this.errors = [];
}
}
// 使用示例
const monitor = new MiniMonitor();
明日秘境:《前端安全の金钟罩——XSS/CSRF攻防战》 🛡️
(留言告诉我你遇到过最诡异的线上bug,本天眼师为你隔空诊断!🔭)
🛎️ 本日避坑指南:
- 监控代码性能反噬
// ❌ 高频率无节制的日志
window.addEventListener('mousemove', trackMouse);
// ✅ 使用防抖 + 抽样
const sampledTrack = sampleRate(
debounce(trackMouse, 300),
0.3 // 30%抽样率
);
- 敏感数据泄露
// 🚨 避免记录用户隐私
const safeError = {
...error,
message: error.message.replace(/password:.*/, '***')
};
// 🛡️ 生产环境屏蔽敏感日志
if (process.env.NODE_ENV === 'production') {
console.log = () => {};
}
- SourceMap 安全
# 构建后自动上传SourceMap到监控平台
sentry-cli releases --org myorg files myapp@1.0 upload-sourcemaps ./dist --url-prefix '~/static/js/'
# 删除本地SourceMap
rm ./dist/*.map
- 错误分类策略
- `FATAL` : 导致页面崩溃
- `ERROR` : 功能不可用
- `WARN` : 降级体验
- `INFO` : 用户行为记录
🔮 监控指标速查表
指标 | 全称 | 健康阈值 | 测量方式 |
---|---|---|---|
FP | First Paint | <1s | performance.getEntriesByName('first-paint') |
FCP | First Contentful Paint | <1.5s | performance.getEntriesByName('first-contentful-paint') |
LCP | Largest Contentful Paint | <2.5s | PerformanceObserver |
FID | First Input Delay | <100ms | PerformanceObserver |
CLS | Cumulative Layout Shift | <0.1 | LayoutShift API |
TTFB | Time to First Byte | <800ms | performance.timing.responseStart - navigationStart |