From 0f6b78eea31dbd137022a354c8d5adc1baa321f8 Mon Sep 17 00:00:00 2001 From: yuantao Date: Mon, 30 Mar 2026 18:41:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=20=E6=9B=B4=E6=96=B0README?= =?UTF-8?q?=E5=92=8C=E4=BD=BF=E7=94=A8=E7=A4=BA=E4=BE=8B=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=94=99=E8=AF=AF=E7=BA=A7=E5=88=AB=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=92=8C=E5=8E=BB=E9=87=8D=E5=8A=9F=E8=83=BD=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 150 ++++++++++++++++++++++++++++++-- USAGE_EXAMPLES.js | 104 +++++++++++++++++++--- package.json | 2 +- test/webhook-test.js | 199 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 432 insertions(+), 23 deletions(-) create mode 100644 test/webhook-test.js diff --git a/README.md b/README.md index 7e51aa8..821e301 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,29 @@ ## ✨ 核心特性 - 🎯 **零配置使用**: 开箱即用,支持多种导入方式 + - 🔍 **全面错误捕获**: 全局错误、Promise错误、控制台错误、网络错误、小程序错误 + - 🧠 **环境智能**: 自动检测生产环境,非生产环境优雅降级 + - ⚡ **高性能**: 异步发送错误,不阻塞主线程 + - 🔄 **重试机制**: 网络失败自动重试,可配置次数和间隔 + - 📊 **错误统计**: 内置统计功能,便于数据分析 + - 🔧 **高度可定制**: 支持自定义发送器和格式化函数 + - 📱 **全平台支持**: H5、微信小程序、App、支付宝小程序等 + - 🛡️ **类型安全**: 完整的 TypeScript 类型定义 + - 📦 **多格式输出**: 支持 ESM、CommonJS、UMD 格式 +- 🎚️ **错误级别控制**: 支持 strict/standard/silent 三种监控模式 + +- 🔄 **错误去重**: 相同错误在指定间隔内只上报一次,避免重复告警 + ## 📦 安装 ```bash @@ -36,33 +49,61 @@ pnpm add uniapp-error-monitor ## 🚀 快速开始 + + ### 方式一:命名导出(推荐) + + ```javascript -import { initErrorMonitor, reportError, getErrorStats, wrapPromise } from 'uniapp-error-monitor' + +import { + initErrorMonitor, + reportError, + getErrorStats, + wrapPromise, + setErrorLevel, + clearErrorCache, + ERROR_LEVEL +} from 'uniapp-error-monitor' + // 初始化错误监控 initErrorMonitor({ - webhookUrl: 'https://your-webhook-url.com', // 必填 - enableGlobalError: true, // 启用全局错误捕获 - enablePromiseError: true, // 启用 Promise 错误捕获 - enableConsoleError: false, // 禁用 console.error 捕获 + webhookUrl: 'https://your-webhook-url.com', // 必填 + enableGlobalError: true, // 启用全局错误捕获 + enablePromiseError: true, // 启用 Promise 错误捕获 + enableConsoleError: false, // 禁用 console.error 捕获 + errorLevel: ERROR_LEVEL.STRICT, // 错误级别:strict/standard/silent + dedupInterval: 60000, // 错误去重间隔(毫秒),默认1分钟 }) + +// 设置错误级别(可选) +setErrorLevel(ERROR_LEVEL.STANDARD) // 切换为标准模式 + + // 手动上报错误 reportError('manual', new Error('自定义错误'), { page: 'index', action: '用户操作失败' }) + // Promise 包装(自动捕获 Promise 错误) const result = await wrapPromise( fetch('https://api.example.com/data') ) + // 获取错误统计 const stats = getErrorStats() console.log('错误统计:', stats) + + +// 清空错误去重缓存(可选) +clearErrorCache() + ``` ### 方式二:类实例(高级用法) @@ -116,25 +157,71 @@ ErrorMonitor.reportError('manual', new Error('测试错误')) ## ⚙️ 配置选项 + + ```typescript + interface ErrorMonitorOptions { // 基础配置 webhookUrl?: string // Webhook 地址(可选,使用环境变量) enableGlobalError?: boolean // 启用全局错误捕获(默认:true) enablePromiseError?: boolean // 启用 Promise 错误捕获(默认:true) enableConsoleError?: boolean // 启用 console.error 捕获(默认:false) + + + // 错误级别配置 + errorLevel?: 'strict' | 'standard' | 'silent' // 错误级别(默认:silent) + + // 错误去重配置 + dedupInterval?: number // 相同错误去重间隔(ms)(默认:60000,即1分钟) + + // 重试配置 maxRetries?: number // 最大重试次数(默认:3) retryDelay?: number // 重试延迟时间(ms)(默认:1000) - + + // 高级配置 forceEnable?: boolean // 强制启用错误监控(忽略环境检查) sender?: (errorInfo: ErrorInfo) => Promise // 自定义发送器 formatter?: (errorInfo: ErrorInfo) => string // 自定义格式化函数 } + ``` + + +### 错误级别说明 + + + +| 级别 | 说明 | 监控范围 | + +|------|------|----------| + +| `strict` | 严格模式 | 监控所有错误(global、promise、console、miniProgram、api、network) | + +| `standard` | 标准模式 | 监控基本错误(global、promise、miniProgram) | + +| `silent` | 静默模式 | 仅监控严重错误(miniProgram、pageNotFound) | + + + +### 错误严重程度分类 + + + +| 严重程度 | 错误类型 | + +|----------|----------| + +| **critical** (严重) | miniProgram, pageNotFound | + +| **normal** (普通) | global, promise, api, network, manual | + +| **minor** (轻微) | console | + ## 📊 错误类型 | 类型 | 说明 | 自动捕获 | 手动上报 | 触发场景 | @@ -173,6 +260,35 @@ resetErrorStats() console.log('错误统计已重置') ``` +### 错误级别控制 + +```javascript +import { setErrorLevel, getErrorLevel, ERROR_LEVEL } from 'uniapp-error-monitor' + +// 获取当前错误级别 +const currentLevel = getErrorLevel() +console.log('当前错误级别:', currentLevel) + +// 设置错误级别 +setErrorLevel(ERROR_LEVEL.STRICT) // 严格模式:监控所有错误 +setErrorLevel(ERROR_LEVEL.STANDARD) // 标准模式:监控基本错误 +setErrorLevel(ERROR_LEVEL.SILENT) // 静默模式:仅监控严重错误 +``` + +### 错误去重管理 + +```javascript +import { clearErrorCache } from 'uniapp-error-monitor' + +// 清空错误去重缓存(允许相同错误重新上报) +clearErrorCache() + +// 去重机制说明: +// - 相同错误在 dedupInterval 间隔内只会被上报一次 +// - 默认间隔为 60 秒(60000ms) +// - 可通过配置 dedupInterval 自定义间隔时间 +``` + ### 丰富的错误上下文 ```javascript @@ -216,33 +332,51 @@ setInterval(() => { ## 🛡️ TypeScript 支持 + + 完整的类型安全支持: + ```typescript + import type { ErrorMonitorOptions, ErrorType, - ErrorStats, - EnvironmentInfo, + ErrorStats, + EnvironmentInfo, ErrorInfo } from 'uniapp-error-monitor' +import { + initErrorMonitor, + reportError, + setErrorLevel, + ERROR_LEVEL +} from 'uniapp-error-monitor' + + // 类型安全的配置 + const options: ErrorMonitorOptions = { webhookUrl: 'https://example.com/webhook', enableGlobalError: true, enablePromiseError: true, + errorLevel: ERROR_LEVEL.STRICT, + dedupInterval: 60000, maxRetries: 5, retryDelay: 2000, } + // 类型安全的错误上报 + const reportTypeSafeError = (type: ErrorType, message: string) => { reportError(type, new Error(message), { timestamp: Date.now(), userId: '12345' }) } + ``` ## 📱 平台兼容性 diff --git a/USAGE_EXAMPLES.js b/USAGE_EXAMPLES.js index 40109e2..015d219 100644 --- a/USAGE_EXAMPLES.js +++ b/USAGE_EXAMPLES.js @@ -12,17 +12,23 @@ import { initErrorMonitor, reportError, getErrorStats, - wrapPromise + wrapPromise, + setErrorLevel, + getErrorLevel, + clearErrorCache, + ERROR_LEVEL } from 'uniapp-error-monitor' // 初始化错误监控 initErrorMonitor({ - webhookUrl: 'https://your-webhook-url.com', // 必填 - enableGlobalError: true, // 启用全局错误捕获 - enablePromiseError: true, // 启用 Promise 错误捕获 - enableConsoleError: false, // 禁用 console.error 捕获 - maxRetries: 3, // 最大重试次数 - retryDelay: 1000, // 重试延迟时间(ms) + webhookUrl: 'https://your-webhook-url.com', // 必填 + enableGlobalError: true, // 启用全局错误捕获 + enablePromiseError: true, // 启用 Promise 错误捕获 + enableConsoleError: false, // 禁用 console.error 捕获 + errorLevel: ERROR_LEVEL.STRICT, // 错误级别:strict/standard/silent + dedupInterval: 60000, // 错误去重间隔(毫秒),默认1分钟 + maxRetries: 3, // 最大重试次数 + retryDelay: 1000, // 重试延迟时间(ms) }) // 手动上报错误 @@ -104,37 +110,73 @@ ErrorMonitorDefault.initErrorMonitor({ ErrorMonitorDefault.reportError('manual', new Error('测试错误')) // ============================================================================ + // 4. TypeScript 类型安全使用 + // ============================================================================ + + import type { + ErrorMonitorOptions, + ErrorType, - ErrorStats, + + ErrorStats, + EnvironmentInfo + } from 'uniapp-error-monitor' + + // 类型安全的配置 + const options: ErrorMonitorOptions = { + webhookUrl: 'https://example.com/webhook', + enableGlobalError: true, + enablePromiseError: true, + + errorLevel: ERROR_LEVEL.STRICT, + + dedupInterval: 60000, + maxRetries: 5, + retryDelay: 2000, + forceEnable: false + } + + // 类型安全的错误上报 + const reportTypeSafeError = (type: ErrorType, message: string) => { + reportError(type, new Error(message), { + timestamp: Date.now(), + userId: '12345' + }) + } + + // 类型安全的统计获取 + const getSafeStats = (): ErrorStats => { + return getErrorStats() + } // ============================================================================ @@ -216,15 +258,49 @@ reportError('global', new Error('页面崩溃'), { }) // ============================================================================ -// 8. 批量错误处理 +// 9. 错误级别控制 +// ============================================================================ + +// 获取当前错误级别 +const currentLevel = getErrorLevel() +console.log('当前错误级别:', currentLevel) + +// 设置错误级别 +setErrorLevel(ERROR_LEVEL.STRICT) // 严格模式:监控所有错误 +setErrorLevel(ERROR_LEVEL.STANDARD) // 标准模式:监控基本错误 +setErrorLevel(ERROR_LEVEL.SILENT) // 静默模式:仅监控严重错误 + +// 错误级别说明: +// - strict: 监控所有错误(global, promise, console, miniProgram, api, network) +// - standard: 监控基本错误(global, promise, miniProgram) +// - silent: 仅监控严重错误(miniProgram, pageNotFound) + +// ============================================================================ +// 10. 错误去重管理 +// ============================================================================ + +// 清空错误去重缓存(允许相同错误重新上报) +clearErrorCache() + +// 去重机制说明: +// - 相同错误在 dedupInterval 间隔内只会被上报一次 +// - 默认间隔为 60 秒(60000ms) +// - 可通过配置 dedupInterval 自定义间隔时间 + +// 示例:配置更短的去重间隔 +initErrorMonitor({ + webhookUrl: 'https://your-webhook-url.com', + dedupInterval: 30000, // 30秒去重间隔 +}) + +// ============================================================================ +// 11. 批量错误处理 // ============================================================================ // 重置错误统计(在页面刷新或特定事件后) -const resetStats = () => { - import { resetErrorStats } from 'uniapp-error-monitor' - resetErrorStats() - console.log('错误统计已重置') -} +import { resetErrorStats } from 'uniapp-error-monitor' +resetErrorStats() +console.log('错误统计已重置') // 定时检查错误状态 setInterval(() => { diff --git a/package.json b/package.json index 5d210c3..a96e965 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uniapp-error-monitor", - "version": "1.1.1", + "version": "1.2.1", "description": "专门为UniApp环境设计的错误监控和上报工具,支持全局错误捕获、Promise错误捕获、网络错误捕获等", "main": "dist/index.js", "module": "dist/index.esm.js", diff --git a/test/webhook-test.js b/test/webhook-test.js new file mode 100644 index 0000000..398376b --- /dev/null +++ b/test/webhook-test.js @@ -0,0 +1,199 @@ +/** + * Webhook 真实发送测试 + * 运行方式:node test/webhook-test.js + */ + +const https = require('https') + +// 测试用的 webhook 地址 +const TEST_WEBHOOK = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=9a401eb2-065a-4882-82e9-b438bcd1eac4' + +// 模拟 uni 环境(使用真实的 HTTP 请求) +global.uni = { + getSystemInfoSync: () => ({ + appName: '错误监控测试', + appVersion: '1.0.0', + platform: 'node', + system: 'Node.js ' + process.version, + model: 'Server', + mode: 'test', + }), + + // 使用真实的 HTTPS 请求 + request: (options) => { + console.log('\n========================================') + console.log('📤 发送真实请求到 Webhook') + console.log('========================================') + console.log('URL:', options.url) + console.log('Method:', options.method) + console.log('数据:', JSON.stringify(options.data, null, 2)) + + const url = new URL(options.url) + const reqOptions = { + hostname: url.hostname, + port: 443, + path: url.pathname + url.search, + method: options.method || 'POST', + headers: { + 'Content-Type': 'application/json', + } + } + + const req = https.request(reqOptions, (res) => { + let data = '' + res.on('data', (chunk) => { data += chunk }) + res.on('end', () => { + console.log('\n📥 响应状态:', res.statusCode) + console.log('响应内容:', data) + if (options.success) { + options.success({ + statusCode: res.statusCode, + data: JSON.parse(data) + }) + } + }) + }) + + req.on('error', (e) => { + console.error('❌ 请求错误:', e.message) + if (options.fail) { + options.fail(e) + } + }) + + req.write(JSON.stringify(options.data)) + req.end() + + return { abort: () => req.destroy() } + }, + + onError: (callback) => { global._uniOnErrorCallback = callback }, + onPageNotFound: (callback) => { global._uniOnPageNotFoundCallback = callback }, +} + +// 模拟 getCurrentPages +global.getCurrentPages = () => [ + { + route: 'pages/test/test', + $page: { fullPath: '/pages/test/test?id=webhook-test' } + } +] + +// 模拟 window 环境 +global.window = { + location: { href: 'http://localhost:8080/webhook-test' }, + onerror: null, + addEventListener: () => {}, +} +global.navigator = { userAgent: 'Node.js Error Monitor Test' } + +// 设置环境变量 +process.env.MODE = 'production' +// 注意:webhookUrl 需要通过 initErrorMonitor 传入,不能通过环境变量 + +// 读取并修改源代码 +const fs = require('fs') +const path = require('path') +const sourcePath = path.join(__dirname, '../src/index.js') +let sourceCode = fs.readFileSync(sourcePath, 'utf-8') + +// 替换 import.meta.env +sourceCode = sourceCode.replace(/import\.meta\.env\.MODE/g, 'process.env.MODE || "production"') +sourceCode = sourceCode.replace(/import\.meta\.env\.VITE_WEBHOOK/g, 'process.env.VITE_WEBHOOK || ""') + +const tempModulePath = path.join(__dirname, 'temp-webhook-index.js') +fs.writeFileSync(tempModulePath, sourceCode) + +// 导入模块 +const errorMonitor = require('./temp-webhook-index.js') +const { initErrorMonitor, reportError, getErrorStats, resetErrorStats, setErrorLevel, clearErrorCache, ERROR_LEVEL } = errorMonitor + +// 延迟函数 +function delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +// 主测试函数 +async function runWebhookTest() { + console.log('\n========================================') + console.log(' 错误监控 Webhook 真实发送测试') + console.log('========================================\n') + + // 初始化 + resetErrorStats() + clearErrorCache() + initErrorMonitor({ + webhookUrl: TEST_WEBHOOK, + forceEnable: true, + errorLevel: ERROR_LEVEL.STRICT, + }) + console.log('✅ 错误监控已初始化') + console.log('📍 Webhook:', TEST_WEBHOOK) + await delay(500) + + // 测试1: 发送手动错误 + console.log('\n----------------------------------------') + console.log('🧪 测试1: 发送手动错误') + console.log('----------------------------------------') + reportError('manual', new Error('这是一条测试错误消息 - 手动上报'), { + testId: 'test-001', + testTime: new Date().toISOString(), + description: '用于验证 webhook 发送功能' + }) + await delay(2000) + + // 测试2: 发送 API 错误 + console.log('\n----------------------------------------') + console.log('🧪 测试2: 发送 API 错误') + console.log('----------------------------------------') + clearErrorCache() + reportError('api', { + config: { + url: 'https://api.example.com/test-api', + method: 'POST', + data: { userId: 123 }, + header: { 'Authorization': 'Bearer xxx' }, + startTime: Date.now() - 500, + }, + statusCode: 500, + data: { + code: 500, + msg: '服务器内部错误', + } + }) + await delay(2000) + + // 测试3: 发送网络错误 + console.log('\n----------------------------------------') + console.log('🧪 测试3: 发送网络错误') + console.log('----------------------------------------') + clearErrorCache() + reportError('network', new Error('网络连接超时'), { + url: 'https://api.example.com/timeout', + method: 'GET', + retryCount: 3, + networkType: 'wifi', + }) + await delay(2000) + + // 清理 + console.log('\n----------------------------------------') + console.log('📊 测试统计') + console.log('----------------------------------------') + const stats = getErrorStats() + console.log('总错误数:', stats.total) + console.log('手动错误:', stats.manual) + console.log('API错误:', stats.api) + console.log('网络错误:', stats.network) + + // 清理临时文件 + try { + fs.unlinkSync(tempModulePath) + } catch (e) {} + + console.log('\n========================================') + console.log(' 测试完成!请检查企业微信机器人') + console.log('========================================\n') +} + +runWebhookTest().catch(console.error) \ No newline at end of file