sobey-monitor-sdk 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/dist/frameworks/index.d.ts +7 -0
- package/dist/frameworks/react.d.ts +74 -0
- package/dist/frameworks/vue.d.ts +21 -0
- package/dist/index.cjs.js +224 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +99 -4
- package/dist/index.esm.js +222 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +224 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/reporter/sender.d.ts +1 -0
- package/dist/types/index.d.ts +8 -2
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -287,10 +287,13 @@ class Sender {
|
|
|
287
287
|
/**
|
|
288
288
|
* 使用 sendBeacon 发送
|
|
289
289
|
* 返回 true 表示成功加入队列,false 表示失败需要降级
|
|
290
|
+
* sendBeacon 发送的 Content-Type 是 text/plain,需要使用 /beacon 接口
|
|
290
291
|
*/
|
|
291
292
|
sendByBeacon(url, payload) {
|
|
292
293
|
try {
|
|
293
|
-
|
|
294
|
+
// sendBeacon 需要使用 /beacon 接口,因为它发送的是 text/plain
|
|
295
|
+
const beaconUrl = url.replace(/\/report$/, '/beacon');
|
|
296
|
+
const success = navigator.sendBeacon(beaconUrl, payload);
|
|
294
297
|
if (!success && config.get().debug) {
|
|
295
298
|
console.warn('[Monitor] sendBeacon returned false, falling back to fetch');
|
|
296
299
|
}
|
|
@@ -1358,6 +1361,223 @@ function installBehaviorMonitor() {
|
|
|
1358
1361
|
installInputTracker();
|
|
1359
1362
|
}
|
|
1360
1363
|
|
|
1364
|
+
/**
|
|
1365
|
+
* Vue 3 框架集成
|
|
1366
|
+
* 捕获 Vue 组件内的渲染错误
|
|
1367
|
+
*/
|
|
1368
|
+
/**
|
|
1369
|
+
* Vue 3 错误监控插件
|
|
1370
|
+
*
|
|
1371
|
+
* @example
|
|
1372
|
+
* ```ts
|
|
1373
|
+
* import { createApp } from 'vue';
|
|
1374
|
+
* import { VueMonitorPlugin } from 'sobey-monitor-sdk';
|
|
1375
|
+
*
|
|
1376
|
+
* const app = createApp(App);
|
|
1377
|
+
* app.use(VueMonitorPlugin, {
|
|
1378
|
+
* appId: 'your-app-id',
|
|
1379
|
+
* dsn: 'http://your-server/api/report',
|
|
1380
|
+
* });
|
|
1381
|
+
* app.mount('#app');
|
|
1382
|
+
* ```
|
|
1383
|
+
*/
|
|
1384
|
+
const VueMonitorPlugin = {
|
|
1385
|
+
install(app, options) {
|
|
1386
|
+
if (!options || !options.appId || !options.dsn) {
|
|
1387
|
+
console.warn('[Monitor] VueMonitorPlugin requires appId and dsn in options');
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
// 初始化 SDK(如果尚未初始化)
|
|
1391
|
+
try {
|
|
1392
|
+
monitor.init(options);
|
|
1393
|
+
}
|
|
1394
|
+
catch (e) {
|
|
1395
|
+
// SDK 可能已经初始化,忽略错误
|
|
1396
|
+
}
|
|
1397
|
+
// 保存原有的错误处理器
|
|
1398
|
+
const originalErrorHandler = app.config.errorHandler;
|
|
1399
|
+
// 设置 Vue 错误处理器
|
|
1400
|
+
app.config.errorHandler = (err, instance, info) => {
|
|
1401
|
+
// 提取错误信息
|
|
1402
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1403
|
+
// 获取组件名称
|
|
1404
|
+
let componentName = 'Unknown';
|
|
1405
|
+
if (instance) {
|
|
1406
|
+
componentName = instance.$options?.name ||
|
|
1407
|
+
instance.$.type?.name ||
|
|
1408
|
+
instance.$.type?.__name ||
|
|
1409
|
+
'AnonymousComponent';
|
|
1410
|
+
}
|
|
1411
|
+
// 添加面包屑
|
|
1412
|
+
context.addBreadcrumb({
|
|
1413
|
+
type: 'framework',
|
|
1414
|
+
category: 'vue_error',
|
|
1415
|
+
data: {
|
|
1416
|
+
componentName,
|
|
1417
|
+
info,
|
|
1418
|
+
message: error.message,
|
|
1419
|
+
},
|
|
1420
|
+
});
|
|
1421
|
+
// 上报错误
|
|
1422
|
+
reporter.reportError({
|
|
1423
|
+
type: 'vue_error',
|
|
1424
|
+
message: error.message,
|
|
1425
|
+
stack: error.stack,
|
|
1426
|
+
componentName,
|
|
1427
|
+
lifecycleHook: info,
|
|
1428
|
+
});
|
|
1429
|
+
// 调用原有的错误处理器
|
|
1430
|
+
if (typeof originalErrorHandler === 'function') {
|
|
1431
|
+
originalErrorHandler(err, instance, info);
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
// 设置警告处理器(仅在开发模式下)
|
|
1435
|
+
if (options.debug) {
|
|
1436
|
+
const originalWarnHandler = app.config.warnHandler;
|
|
1437
|
+
app.config.warnHandler = (msg, instance, trace) => {
|
|
1438
|
+
// 添加面包屑
|
|
1439
|
+
context.addBreadcrumb({
|
|
1440
|
+
type: 'console',
|
|
1441
|
+
category: 'vue_warning',
|
|
1442
|
+
data: { message: msg },
|
|
1443
|
+
});
|
|
1444
|
+
// 调用原有的警告处理器
|
|
1445
|
+
if (typeof originalWarnHandler === 'function') {
|
|
1446
|
+
originalWarnHandler(msg, instance, trace);
|
|
1447
|
+
}
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
if (options.debug) {
|
|
1451
|
+
console.log('[Monitor] Vue plugin installed');
|
|
1452
|
+
}
|
|
1453
|
+
},
|
|
1454
|
+
};
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* React 框架集成
|
|
1458
|
+
* 提供 ErrorBoundary 组件捕获渲染错误
|
|
1459
|
+
*/
|
|
1460
|
+
/**
|
|
1461
|
+
* 创建 React 错误边界组件
|
|
1462
|
+
*
|
|
1463
|
+
* @param React - React 库引用
|
|
1464
|
+
* @param config - SDK 配置
|
|
1465
|
+
* @returns ErrorBoundary 组件类
|
|
1466
|
+
*
|
|
1467
|
+
* @example
|
|
1468
|
+
* ```tsx
|
|
1469
|
+
* import React from 'react';
|
|
1470
|
+
* import ReactDOM from 'react-dom';
|
|
1471
|
+
* import { createReactErrorBoundary } from 'sobey-monitor-sdk';
|
|
1472
|
+
*
|
|
1473
|
+
* const ErrorBoundary = createReactErrorBoundary(React, {
|
|
1474
|
+
* appId: 'your-app-id',
|
|
1475
|
+
* dsn: 'http://your-server/api/report',
|
|
1476
|
+
* });
|
|
1477
|
+
*
|
|
1478
|
+
* ReactDOM.render(
|
|
1479
|
+
* <ErrorBoundary fallback={<h1>Something went wrong</h1>}>
|
|
1480
|
+
* <App />
|
|
1481
|
+
* </ErrorBoundary>,
|
|
1482
|
+
* document.getElementById('root')
|
|
1483
|
+
* );
|
|
1484
|
+
* ```
|
|
1485
|
+
*/
|
|
1486
|
+
function createReactErrorBoundary(React, config) {
|
|
1487
|
+
if (!config || !config.appId || !config.dsn) {
|
|
1488
|
+
console.warn('[Monitor] createReactErrorBoundary requires appId and dsn in config');
|
|
1489
|
+
// 返回一个空的组件
|
|
1490
|
+
return class EmptyBoundary extends React.Component {
|
|
1491
|
+
render() {
|
|
1492
|
+
return this.props.children;
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
// 初始化 SDK(如果尚未初始化)
|
|
1497
|
+
try {
|
|
1498
|
+
monitor.init(config);
|
|
1499
|
+
}
|
|
1500
|
+
catch (e) {
|
|
1501
|
+
// SDK 可能已经初始化,忽略错误
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* React 错误边界组件
|
|
1505
|
+
*/
|
|
1506
|
+
return class ErrorMonitorBoundary extends React.Component {
|
|
1507
|
+
constructor(props) {
|
|
1508
|
+
super(props);
|
|
1509
|
+
this.state = { hasError: false, error: null };
|
|
1510
|
+
}
|
|
1511
|
+
static getDerivedStateFromError(error) {
|
|
1512
|
+
return { hasError: true, error };
|
|
1513
|
+
}
|
|
1514
|
+
componentDidCatch(error, errorInfo) {
|
|
1515
|
+
// 获取组件堆栈
|
|
1516
|
+
const componentStack = errorInfo?.componentStack || '';
|
|
1517
|
+
// 添加面包屑
|
|
1518
|
+
context.addBreadcrumb({
|
|
1519
|
+
type: 'framework',
|
|
1520
|
+
category: 'react_error',
|
|
1521
|
+
data: {
|
|
1522
|
+
message: error.message,
|
|
1523
|
+
componentStack: componentStack.substring(0, 500),
|
|
1524
|
+
},
|
|
1525
|
+
});
|
|
1526
|
+
// 上报错误
|
|
1527
|
+
reporter.reportError({
|
|
1528
|
+
type: 'react_error',
|
|
1529
|
+
message: error.message,
|
|
1530
|
+
stack: error.stack,
|
|
1531
|
+
componentStack,
|
|
1532
|
+
});
|
|
1533
|
+
// 调用自定义错误回调
|
|
1534
|
+
if (typeof this.props.onError === 'function') {
|
|
1535
|
+
this.props.onError(error, errorInfo);
|
|
1536
|
+
}
|
|
1537
|
+
if (config.debug) {
|
|
1538
|
+
console.error('[Monitor] React error caught:', error);
|
|
1539
|
+
console.error('[Monitor] Component stack:', componentStack);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
render() {
|
|
1543
|
+
if (this.state.hasError) {
|
|
1544
|
+
// 显示降级 UI
|
|
1545
|
+
return this.props.fallback || null;
|
|
1546
|
+
}
|
|
1547
|
+
return this.props.children;
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* React Hook: 用于手动上报错误
|
|
1553
|
+
*
|
|
1554
|
+
* @example
|
|
1555
|
+
* ```tsx
|
|
1556
|
+
* import { useMonitorError } from 'sobey-monitor-sdk';
|
|
1557
|
+
*
|
|
1558
|
+
* function MyComponent() {
|
|
1559
|
+
* const reportError = useMonitorError();
|
|
1560
|
+
*
|
|
1561
|
+
* const handleClick = async () => {
|
|
1562
|
+
* try {
|
|
1563
|
+
* await riskyOperation();
|
|
1564
|
+
* } catch (error) {
|
|
1565
|
+
* reportError(error, { action: 'riskyOperation' });
|
|
1566
|
+
* }
|
|
1567
|
+
* };
|
|
1568
|
+
*
|
|
1569
|
+
* return <button onClick={handleClick}>Click</button>;
|
|
1570
|
+
* }
|
|
1571
|
+
* ```
|
|
1572
|
+
*/
|
|
1573
|
+
function createUseMonitorError(React) {
|
|
1574
|
+
return function useMonitorError() {
|
|
1575
|
+
return React.useCallback((error, extra) => {
|
|
1576
|
+
monitor.captureError(error, extra);
|
|
1577
|
+
}, []);
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1361
1581
|
/**
|
|
1362
1582
|
* 前端监控 SDK
|
|
1363
1583
|
* @description 错误监控、性能监控、行为监控
|
|
@@ -1469,5 +1689,5 @@ class MonitorSDK {
|
|
|
1469
1689
|
// 导出单例
|
|
1470
1690
|
const monitor = new MonitorSDK();
|
|
1471
1691
|
|
|
1472
|
-
export { monitor as default, monitor };
|
|
1692
|
+
export { VueMonitorPlugin, createReactErrorBoundary, createUseMonitorError, monitor as default, monitor };
|
|
1473
1693
|
//# sourceMappingURL=index.esm.js.map
|