本文目录导读:

前端统计方案
使用 Axios 拦截器
// 全局错误计数器
const errorCounter = {
total: 0,
byStatus: {},
byUrl: {},
add(url, status) {
this.total++
this.byStatus[status] = (this.byStatus[status] || 0) + 1
this.byUrl[url] = (this.byUrl[url] || 0) + 1
},
report() {
console.table({
total: this.total,
byStatus: this.byStatus,
byUrl: this.byUrl
})
}
}
// Axios 拦截器
axios.interceptors.response.use(
response => response,
error => {
const config = error.config
const status = error.response?.status || 0
errorCounter.add(config.url, status)
// 当错误达到阈值时报告
if (errorCounter.total % 10 === 0) {
errorCounter.report()
// 可以发送到服务器
sendToServer(errorCounter)
}
return Promise.reject(error)
}
)
使用 Fetch API 封装
class ApiMonitor {
constructor() {
this.errors = {
total: 0,
recent: [], // 最近100条错误记录
byEndpoint: {},
byStatusCode: {}
}
}
async fetch(url, options = {}) {
try {
const response = await fetch(url, options)
if (!response.ok) {
this.recordError(url, response.status)
}
return response
} catch (error) {
this.recordError(url, 0, error.message)
throw error
}
}
recordError(url, status, message = '') {
this.errors.total++
this.errors.recent.unshift({
url,
status,
message,
timestamp: Date.now()
})
// 只保留最近100条
if (this.errors.recent.length > 100) {
this.errors.recent.pop()
}
// 按端点统计
this.errors.byEndpoint[url] = (this.errors.byEndpoint[url] || 0) + 1
// 按状态码统计
this.errors.byStatusCode[status] = (this.errors.byStatusCode[status] || 0) + 1
}
getStats() {
return {
...this.errors,
recent: this.errors.recent.slice(0, 10) // 只返回最近10条
}
}
}
// 使用
const api = new ApiMonitor()
const data = await api.fetch('/api/users')
后端统计方案
Node.js + Express 中间件
const errorStats = {
total: 0,
byRoute: {},
byStatusCode: {},
byHour: {},
// 每小时轮转
rotateHourly() {
const hour = new Date().getHours()
if (!this.byHour[hour]) {
this.byHour[hour] = { total: 0, errors: {} }
}
}
}
// 错误统计中间件
const errorMonitor = (err, req, res, next) => {
errorStats.total++
// 按路由统计
const route = req.baseUrl + req.path
if (!errorStats.byRoute[route]) {
errorStats.byRoute[route] = 0
}
errorStats.byRoute[route]++
// 按状态码统计
const status = err.status || 500
if (!errorStats.byStatusCode[status]) {
errorStats.byStatusCode[status] = 0
}
errorStats.byStatusCode[status]++
// 按小时统计
const hour = new Date().getHours()
if (!errorStats.byHour[hour]) {
errorStats.byHour[hour] = 0
}
errorStats.byHour[hour]++
next(err)
}
// 获取统计信息接口
app.get('/api/stats/errors', (req, res) => {
res.json(errorStats)
})
// 重置统计
app.delete('/api/stats/errors', (req, res) => {
Object.assign(errorStats, {
total: 0,
byRoute: {},
byStatusCode: {},
byHour: {}
})
res.sendStatus(200)
})
综合监控方案
使用装饰器模式
class ApiMonitor {
constructor(options = {}) {
this.options = {
reportInterval: 60000, // 1分钟报告一次
maxErrorsBeforeAlert: 10, // 10次错误触发告警
...options
}
this.stats = this.initStats()
this.startReporting()
}
initStats() {
return {
total: 0,
successful: 0,
failed: 0,
lastMinuteErrors: 0,
errorRate: 0,
endpoints: {}
}
}
wrap(endpoint, handler) {
return async (...args) => {
const startTime = Date.now()
try {
const result = await handler(...args)
this.recordSuccess(endpoint, Date.now() - startTime)
return result
} catch (error) {
this.recordError(endpoint, error)
throw error
}
}
}
recordSuccess(endpoint, duration) {
if (!this.stats.endpoints[endpoint]) {
this.stats.endpoints[endpoint] = {
total: 0,
errors: 0,
avgDuration: 0
}
}
const ep = this.stats.endpoints[endpoint]
ep.total++
ep.avgDuration = (ep.avgDuration * (ep.total - 1) + duration) / ep.total
this.stats.total++
this.stats.successful++
}
recordError(endpoint, error) {
if (!this.stats.endpoints[endpoint]) {
this.stats.endpoints[endpoint] = {
total: 0,
errors: 0,
lastError: null
}
}
const ep = this.stats.endpoints[endpoint]
ep.total++
ep.errors++
ep.lastError = {
message: error.message,
timestamp: Date.now()
}
this.stats.total++
this.stats.failed++
this.stats.lastMinuteErrors++
// 检查是否需要告警
if (ep.errors >= this.options.maxErrorsBeforeAlert) {
this.triggerAlert(endpoint, ep)
}
}
triggerAlert(endpoint, stats) {
console.warn(`⚠️ API Alert: ${endpoint} has ${stats.errors} errors`)
// 发送告警通知
this.sendAlert({
endpoint,
errors: stats.errors,
lastError: stats.lastError,
timestamp: new Date()
})
}
startReporting() {
setInterval(() => {
this.stats.errorRate = (this.stats.lastMinuteErrors / (this.stats.total || 1)) * 100
console.table({
'Total Requests': this.stats.total,
'Failed': this.stats.failed,
'Error Rate': `${this.stats.errorRate.toFixed(2)}%`,
'Last Minute Errors': this.stats.lastMinuteErrors
})
// 重置每分钟统计
this.stats.lastMinuteErrors = 0
// 发送到监控系统
this.sendToMonitoring(this.stats)
}, this.options.reportInterval)
}
async sendToMonitoring(stats) {
// 发送到你的监控系统
await fetch('/api/monitoring', {
method: 'POST',
body: JSON.stringify(stats)
})
}
}
// 使用
const monitor = new ApiMonitor({ maxErrorsBeforeAlert: 5 })
const apiHandler = {
getUser: monitor.wrap('/api/users', async (id) => {
const response = await fetch(`/api/users/${id}`)
return response.json()
})
}
实用辅助工具
错误率仪表板
class ErrorDashboard {
constructor() {
this.errors = []
this.MAX_RECORDS = 1000
}
addError(error) {
this.errors.push({
...error,
timestamp: new Date()
})
if (this.errors.length > this.MAX_RECORDS) {
this.errors.shift()
}
}
getErrorRate(timeWindow = 60000) { // 默认最近1分钟
const now = Date.now()
const recent = this.errors.filter(e =>
now - e.timestamp.getTime() < timeWindow
)
return {
count: recent.length,
rate: (recent.length / (timeWindow / 1000)).toFixed(2) + '/s'
}
}
getTopErrors(limit = 5) {
const errorCounts = {}
this.errors.forEach(e => {
const key = `${e.url}:${e.status}`
errorCounts[key] = (errorCounts[key] || 0) + 1
})
return Object.entries(errorCounts)
.sort((a, b) => b[1] - a[1])
.slice(0, limit)
.map(([key, count]) => ({
endpoint: key.split(':')[0],
status: key.split(':')[1],
count
}))
}
}
使用示例
// 全局监控
const monitor = new ApiMonitor()
const dashboard = new ErrorDashboard()
// 在请求封装中使用
async function request(url, options) {
try {
const response = await fetch(url, options)
return response
} catch (error) {
monitor.recordError(url, error)
dashboard.addError({
url,
status: error.status || 500,
message: error.message
})
// 检查错误率
const errorRate = dashboard.getErrorRate()
if (errorRate.count > 10) {
console.warn(`High error rate detected: ${errorRate.rate}`)
}
throw error
}
}
这些方案可以根据你的具体需求进行组合和定制,关键是要:
- 统一拦截所有请求
- 记录详细的错误信息
- 按需生成统计报告
- 设置告警阈值