1.4.2 • Published 7 months ago

@easycode/client-detector v1.4.2

Weekly downloads
-
License
-
Repository
github
Last release
7 months ago

介绍

兼容性

功能

设备信息搜集

错误日志

性能数据

todo

安装

npm安装

npm install @easycode/client-detector

浏览器引入

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Client detector demo</title>
    <!-- 引入client detector,暴露global对象: ClientDetector -->
    <script src="/src/build/client-detector.umd.cjs"></script>
    <!-- 初始化 client detector -->
    <script>
        (function(){
            const serviceHost = 'https://demo.com/data-bury'; // 必填,服务请求地址
            const serviceName = 'test-service'; // 必填且唯一,找管理员查询
            ClientDetector.init(serviceHost, { serviceName: serviceName});
        })();
    </script>
...

使用

上手

import { createClientDetector } from '@easycode/client-detector';

const serviceHost = 'https://demo.com/data-bury'; // 必填,服务请求地址
const serviceName = 'test-service'; // 必填且唯一,找管理员查询
const userId = 'visitor'; // 可选,用户id,默认是visitor
const buryId = ''; // 可选,32位uuid,前端生成,不填则由后端生成


const clientDetector = createClientDetector(serviceHost,{
    serviceName,
    userId,
    buryId
});


const Demo: FC<NavigatorDemoProps> = () => {
    useEffect(() => {
        // 发送客户端设备信息
        clientDetector.sendClientInfo();

    }, []);

    return (
        <div className={ styles.navigatorDemo }>
            Demo
        </div>
    );
};

设置UserId

import { createClientDetector } from '@easycode/client-detector';

const serviceHost = 'https://demo.com/data-bury'; // 必填,服务请求地址
const serviceName = 'test-service'; // 必填且唯一,找管理员查询
const userId = 'visitor'; // 用户id,可选
const buryId = ''; // 可选

const clientDetector = createClientDetector(serviceHost,{
    serviceName,
    userId,
    buryId
});


const Demo: FC<NavigatorDemoProps> = () => {
    useEffect(() => {
        // userId为visitor
        clientDetector.sendClientInfo();

        setTimeout(() => {
            // 设置userId
            clientDetector.setUserId('0000000');
            // 获取客户端设备信息
            clientDetector.sendClientInfo();
        }, 1000);

    }, []);

    return (
        <div className={ styles.navigatorDemo }>
            Demo
        </div>
    );
};

网络指纹

import { createClientDetector } from '@easycode/client-detector';

const serviceHost = 'https://demo.com/data-bury'; // 必填,服务请求地址
const serviceName = 'test-service'; // 必填且唯一,找管理员查询

const clientDetector = createClientDetector(serviceHost,{
    serviceName
});


const Demo: FC<NavigatorDemoProps> = () => {
    useEffect(() => {
        // 发送客户端设备信息
        // setFingerprint是一个异步方法,会在localstorage中缓存生成的网略指纹
        detector.setFingerprint().then(() => detector.sendClientInfo());
    }, []);

    return (
        <div className={ styles.navigatorDemo }>
            Demo
        </div>
    );
};

仅使用网络指纹

import { getFingerprint } from '@easycode/client-detector';


const Demo: FC<NavigatorDemoProps> = () => {
    useEffect(() => {
        // getFingerprint是一个异步函数
        const print = async () => {
            const id = await getFingerprint();
            console.log(id); // 输出一串hash: 801baad441a144716cb8e0a6181ca337
        }

        print();
    }, []);

    return (
        <div className={ styles.navigatorDemo }>
            Demo
        </div>
    );
};

错误收集功能

my-app
...
├── detector.tsx
├── error-boundary.tsx
├── error-demo.tsx
├── app.tsx
...
import { createClientDetector } from '@easycode/client-detector';
const serviceHost = 'https://host/burry/api'; // 服务请求地址
const serviceName = 'test-service'; // 必须唯一,找管理员查询
const userId = 'visitor'; // 用户id,可选
const buryId = ''; // 可选

export const clientDetector = createClientDetector(serviceHost,{
    serviceName,
    userId,
    buryId
});
import { ReactNode, Component, ErrorInfo } from 'react';
import { clientDetector } from './detector';

export interface ErrorBoundaryProps {
    children?: ReactNode;
}

export interface ErrorBoundaryState {
    hasError: boolean;
}

class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
    constructor(props: ErrorBoundaryProps) {
        super(props);
        this.state = { hasError: false };
    }

    static getDerivedStateFromError(error: Error) {
        console.log(error);
        // 更新 state 使下一次渲染能够显示降级后的 UI
        return { hasError: true };
    }

    componentDidCatch(error: Error, errorInfo: ErrorInfo) {
        // 发送错误信息
        // errorInfo是以组件为单位的调用栈
        clientDetector.sendError0(error, errorInfo.componentStack || '');
        // 等同于 clientDetector.sendError(error, errorInfo.componentStack || '', 0);
    }

    render() {
        if (this.state.hasError) {
        // 你可以自定义降级后的 UI 并渲染
            return <h1>Something went wrong.</h1>;
        }

        return this.props.children; 
    }
}

export default ErrorBoundary;
import { FC, ReactNode, useEffect } from 'react';

export interface ErrorDemoProps {
    children?: ReactNode;
}

const ErrorDemo: FC<ErrorDemoProps> = () => {

    useEffect(() => {
        throw new TypeError('error message');
    }, []);

    return (
        <div>
            ErrorDemo
        </div>
    );
};

export default ErrorDemo;
import { FC, ReactNode, useEffect } from 'react';
import { clientDetector } from './detector';
import ErrorBoundary from './error-boundary';
import ErrorDemo from './error-demo'

export interface AppProps {
    children?: ReactNode;
}

const App: FC<AppProps> = () => {
    useEffect(() => {
        // 发送设备信息
        clientDetector.sendClientInfo();
    }, []);

    return (
        <ErrorBoundary>
            <div>
                请求发送测试页面
            </div>
            <ErrorDemo />
        </ErrorBoundary>
    );
};

export default App;

单例模式

单例模式初始化

import * as ReactDOM from 'react-dom/client';
import { init } from '@easycode/client-detector';
import App from './app';

const serviceHost = 'https://openxlab.org.cn/gw/data-bury'; // 必填,服务请求地址
const serviceName = 'test-service'; // 必填且唯一,找管理员查询

init(serviceHost, {
    serviceName,
});

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
    <App />,
);

单例模式搜集设备信息

import { detector } from '@easycode/client-detector';
import { useEffect } from 'react';

const App = () => {

    useEffect(() => {
        const init = async () => {
            detector.sendClientInfo();
        };

        init();
    }, []);

    return (

        <div>...</div>
    );
};

export default App;

Console 日志搜集

从 1.4.0 版本开始,SDK 提供了 console 日志搜集功能。在生产环境下,会自动拦截并搜集 console.info 和 console.error 的日志信息,并批量发送到服务器。

功能特点

  1. 自动搜集 - 在初始化时自动启用(可选关闭)
  2. 批量处理 - 日志会被缓存并批量发送,减少请求次数
  3. 智能队列 - 使用 requestIdleCallback 在浏览器空闲时发送,不影响主线程
  4. 设备信息 - 每批日志会自动附带设备信息,方便追踪问题

配置方式

import { init } from '@easycode/client-detector';

// 默认开启 console 功能
init(serviceHost, { 
    serviceName: 'your-service' 
});

// 禁用 console 功能
init(serviceHost, { 
    serviceName: 'your-service' 
}, 'production', false);

日志格式

发送到服务器的日志格式如下:

{
    // 日志信息
    consoleLogs: [{
        level: 'info' | 'error',  // 日志级别
        message: string,          // 日志内容
        timestamp: number         // 时间戳
    }],
    
    // 设备信息(自动附加)
    os: string,                  // 操作系统
    browserName: string,         // 浏览器名称
    browserVersion: string,      // 浏览器版本
    // ... 其他设备信息
}

使用示例

// 记录普通信息
console.info('用户点击了按钮', { 
    buttonId: 'submit',
    timestamp: Date.now() 
});

// 记录错误信息
console.error('API 请求失败', new Error('Network Error'), { 
    url: '/api/data',
    status: 500 
});

// 支持多种数据类型
console.info(
    '复杂数据', 
    { obj: { id: 1 } },
    [1, 2, 3],
    null,
    undefined
);

注意事项

  1. 只在生产环境(production)下生效
  2. 开发环境下使用原始的 console 方法
  3. 日志会在以下情况触发发送:
    • 累积到 10 条消息
    • 距离上次发送超过 5 秒
    • 浏览器空闲时
  4. 所有原始日志仍然会在控制台显示
  5. 如果发送失败,日志会重新加入队列等待下次发送
npm run dev
1.4.2

7 months ago

1.4.1

7 months ago

1.4.0

7 months ago

1.2.0

1 year ago

1.1.0

1 year ago

1.3.6

1 year ago

1.0.9

1 year ago

1.3.5

1 year ago

1.0.8

1 year ago

1.3.4

1 year ago

1.3.3

1 year ago

1.3.2

1 year ago

1.3.1

1 year ago

1.3.0

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago

0.1.0

1 year ago