一、为什么要做前端日志系统
想象一下这样的场景:用户在你的网站上操作时突然报错了,但开发者完全不知道发生了什么。这时候如果有套日志系统,就能像黑匣子一样记录下用户的操作路径和错误信息,问题排查效率能提升10倍。
前端日志系统主要解决两个核心问题:
- 错误收集:自动捕获代码运行时异常、接口报错等
- 行为追踪:记录用户点击、页面跳转等关键操作
二、基础搭建:jQuery错误监控
技术栈:jQuery + Web API
// 全局错误监控
$(document).ajaxError(function(event, jqxhr, settings, error) {
// 组装错误数据
const logData = {
type: 'API_ERROR',
url: settings.url,
method: settings.type,
status: jqxhr.status,
error: error,
timestamp: new Date().toISOString()
};
// 发送到日志服务器
sendLog(logData);
});
// 未捕获的异常监控
window.onerror = function(message, source, lineno, colno, error) {
const logData = {
type: 'JS_ERROR',
message: message,
source: source,
line: lineno,
column: colno,
stack: error && error.stack,
timestamp: new Date().toISOString()
};
sendLog(logData);
};
// 日志发送函数
function sendLog(data) {
$.ajax({
url: '/api/logs',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify(data),
timeout: 3000 // 3秒超时
});
}
这个基础版本已经能捕获两种常见错误:
- AJAX请求失败(接口500错误等)
- JavaScript运行时错误(undefined变量等)
三、增强版:用户行为追踪
技术栈:jQuery + Web Storage
// 用户行为追踪器
const BehaviorTracker = {
MAX_RECORDS: 50, // 最大记录数
STORAGE_KEY: 'user_behavior',
init: function() {
// 从本地存储加载已有记录
this.records = JSON.parse(
localStorage.getItem(this.STORAGE_KEY) || '[]'
);
// 绑定事件监听
this.bindEvents();
// 定时上传
setInterval(this.uploadRecords.bind(this), 10000);
},
bindEvents: function() {
// 追踪点击事件
$(document).on('click', '[data-track]', function(e) {
const target = $(this);
this.addRecord({
type: 'CLICK',
element: target.data('track'),
text: target.text().trim(),
timestamp: new Date().toISOString()
});
}.bind(this));
// 追踪页面浏览
$(window).on('beforeunload', function() {
this.addRecord({
type: 'PAGE_VIEW',
path: location.pathname,
timestamp: new Date().toISOString()
});
}.bind(this));
},
addRecord: function(record) {
this.records.push(record);
// 控制记录数量
if (this.records.length > this.MAX_RECORDS) {
this.records.shift();
}
localStorage.setItem(
this.STORAGE_KEY,
JSON.stringify(this.records)
);
},
uploadRecords: function() {
if (this.records.length === 0) return;
$.ajax({
url: '/api/behavior',
method: 'POST',
data: JSON.stringify(this.records),
success: function() {
// 上传成功后清空本地记录
this.records = [];
localStorage.removeItem(this.STORAGE_KEY);
}.bind(this)
});
}
};
// 初始化追踪器
$(document).ready(function() {
BehaviorTracker.init();
});
这个增强版实现了:
- 关键元素点击追踪(通过data-track属性标记)
- 页面浏览记录
- 本地存储缓冲,避免频繁请求
- 定时批量上传机制
四、实战优化:性能与可靠性
技术栈:jQuery + IndexedDB
// 高性能日志存储器
const LogDB = {
DB_NAME: 'FrontendLogs',
DB_VERSION: 1,
STORE_NAME: 'logs',
db: null,
// 初始化数据库
init: function(callback) {
const request = indexedDB.open(this.DB_NAME, this.DB_VERSION);
request.onupgradeneeded = function(e) {
const db = e.target.result;
if (!db.objectStoreNames.contains(this.STORAGE_NAME)) {
db.createObjectStore(this.STORE_NAME, {
keyPath: 'id',
autoIncrement: true
});
}
}.bind(this);
request.onsuccess = function(e) {
this.db = e.target.result;
callback && callback();
}.bind(this);
},
// 添加日志
addLog: function(log) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.STORE_NAME], 'readwrite');
const store = transaction.objectStore(this.STORE_NAME);
const request = store.add({
...log,
createdAt: new Date().getTime()
});
request.onsuccess = resolve;
request.onerror = reject;
});
},
// 获取所有日志
getAllLogs: function() {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.STORE_NAME], 'readonly');
const store = transaction.objectStore(this.STORE_NAME);
const request = store.getAll();
request.onsuccess = function() {
resolve(request.result || []);
};
request.onerror = reject;
});
},
// 清除日志
clearLogs: function() {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.STORE_NAME], 'readwrite');
const store = transaction.objectStore(this.STORE_NAME);
const request = store.clear();
request.onsuccess = resolve;
request.onerror = reject;
});
}
};
// 使用示例
$(document).ready(function() {
LogDB.init(function() {
// 数据库初始化完成后
window.onerror = function(...args) {
const log = formatErrorLog(args);
LogDB.addLog(log);
};
// 定时上传
setInterval(function() {
LogDB.getAllLogs().then(logs => {
if (logs.length > 0) {
return $.ajax({
url: '/api/logs/batch',
method: 'POST',
data: JSON.stringify(logs)
}).then(function() {
return LogDB.clearLogs();
});
}
});
}, 15000); // 每15秒上传一次
});
});
这个优化版本的特点是:
- 使用IndexedDB存储日志,容量更大(相比localStorage)
- 异步API设计,避免阻塞主线程
- 批量上传机制,减少网络请求
- 断网时自动保存,网络恢复后继续上传
五、应用场景与注意事项
典型应用场景
- 生产环境错误监控:快速定位线上问题
- 用户行为分析:了解用户操作路径
- 性能优化:记录页面加载耗时等指标
- A/B测试:追踪不同版本的转化率
技术优缺点
优点:
- 实现成本低,jQuery项目可快速接入
- 不依赖第三方服务,数据自主可控
- 轻量级,对页面性能影响小
缺点:
- 需要自行搭建后端接收服务
- 大数据量时需要考虑存储优化
- 用户可能禁用JavaScript导致失效
注意事项
- 隐私保护:避免记录敏感信息(如密码)
- 性能影响:控制日志采集频率
- 数据清洗:后端要做好日志处理
- 采样率:高流量网站要考虑采样策略
六、完整实现方案
技术栈:jQuery + Web API + IndexedDB
// 完整配置对象
const LoggerConfig = {
appId: 'YOUR_APP_ID', // 应用标识
reportUrl: '/api/logs', // 上报地址
maxRetry: 3, // 最大重试次数
sampling: 0.1, // 采样率(10%)
version: '1.0.0' // SDK版本
};
// 主日志类
class FrontendLogger {
constructor(config) {
this.config = {...LoggerConfig, ...config};
this.retryCount = 0;
this.init();
}
init() {
// 初始化数据库
this.db = new LogDB();
this.db.init(() => {
// 错误监控
this.setupErrorHandling();
// 行为追踪
this.setupBehaviorTracking();
// 性能监控
this.setupPerformance();
});
}
setupErrorHandling() {
// 捕获全局错误
window.onerror = this.handleWindowError.bind(this);
// 捕获Promise错误
window.addEventListener('unhandledrejection', this.handlePromiseError.bind(this));
// 捕获AJAX错误
$(document).ajaxError(this.handleAjaxError.bind(this));
}
setupBehaviorTracking() {
// 页面浏览
$(window).on('load beforeunload', this.trackPageView.bind(this));
// 按钮点击
$(document).on('click', '[data-track]', this.trackClick.bind(this));
}
setupPerformance() {
// 使用Performance API获取性能数据
if (window.performance) {
setTimeout(() => {
const timing = performance.timing;
const metrics = {
dns: timing.domainLookupEnd - timing.domainLookupStart,
tcp: timing.connectEnd - timing.connectStart,
ttfb: timing.responseStart - timing.requestStart,
download: timing.responseEnd - timing.responseStart,
domReady: timing.domComplete - timing.domLoading,
load: timing.loadEventEnd - timing.navigationStart
};
this.saveLog({
type: 'PERFORMANCE',
metrics: metrics
});
}, 0);
}
}
// 处理错误的方法...
// 保存日志的方法...
// 上报日志的方法...
}
// 使用示例
$(document).ready(function() {
const logger = new FrontendLogger({
appId: 'my_ecommerce_site'
});
// 也可以手动记录业务日志
$('#checkoutBtn').click(function() {
logger.saveLog({
type: 'BUSINESS',
action: 'checkout',
items: cart.getItems()
});
});
});
这个完整实现包含:
- 模块化设计,易于扩展
- 完整的错误监控体系
- 灵活的行为追踪配置
- 性能数据采集
- 采样率控制等生产级功能
七、总结与建议
实现一个前端日志系统并不复杂,关键是要考虑清楚你的业务需要什么样的数据。对于大多数项目,建议从基础版本开始,随着业务增长再逐步完善。
部署后要重点关注:
- 日志数据的有效性:避免收集无用数据
- 系统稳定性:不要因为日志系统导致主业务崩溃
- 数据安全:做好敏感信息过滤
最后提醒,jQuery虽然现在不是主流了,但在老项目中仍然广泛使用。这套方案可以很好地帮助维护老项目,快速建立监控能力。
评论