An open API service indexing awesome lists of open source software.

https://github.com/jsweber/client-detector


https://github.com/jsweber/client-detector

Last synced: about 2 months ago
JSON representation

Awesome Lists containing this project

README

        

# 介绍


前端埋点sdk,开发者利用该sdk可以去搜集设备信息、错误日志和性能数据(开发中)等。



# 兼容性


  • Chrome >=87

  • Firefox >=78

  • Safari >=14

  • Edge >=88

# 功能
## 设备信息搜集

搜集信息如下


os
操作系统( 举例:Windows/Mac/Android/iOS/Linux )


osVersion
操作系统版本,(举例:11 | 10 | Unknown ...)


browserName
浏览器( 举例:Chrome | Firefox | Edge ... )


browserVersion
浏览器版本( 举例:Chrome: 104.0.0.0 ... )


screenSizeWidth
屏幕宽度( 举例:2560 )px


screenSizeHeight
屏幕高度( 举例:1440 )px


windowSizeWidth
窗口宽度( 举例:2495 )px


windowSizeHeight
窗口高度 ( 举例:1440 )px


language
使用语言( 举例:zh-CN ...)


isWeChart
是否在微信环境(true | false)


isMobile
是否在移动端(true | false)


userAgent
浏览器的navigator.userAgent


platform
浏览器的navigator.platform


gpu
gpu信息(ANGLE (NVIDIA Corporation, GeForce GTX 1660 Ti/PCIe/SSE2, OpenGL 4.5.0))


## 错误日志

为了方便确定发生错误的环境,错误信息会包括设备信息,

在此基础上会增加错误相关字段,新增字段见下表


errorName
错误名称( 举例:TypeError, ParseError)


errorMessage
错误信息


errorLevel
错误级别:0-3,一般来说数字越小越严重


errorStack
错误栈,依赖浏览器兼容性


errorComponentStack
组件级别错误栈,依赖前端框架实现,已知react支持


## 性能数据
todo

# 安装

## npm安装

```sh
npm install @easycode/client-detector
```

## 浏览器引入

通过script标签引入cdn上的umd包

copy以下代码放在script标签中,如下示例


```html



Client detector demo




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

...

```


# 使用
## 上手

```js
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 = () => {
useEffect(() => {
// 发送客户端设备信息
clientDetector.sendClientInfo();

}, []);

return (


Demo

);
};

```

## 设置UserId

在具体业务场景中,我们发现userId并不一定能在初始化ClientDetector阶段获取,

所以提供setUserId方法,开发者可以通过该方法设置userId,设置后发送的所有埋点请求都会带上userId,

注意! 在调用setUserId前的请求不会带有userid,使用默认值visitor

```js
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 = () => {
useEffect(() => {
// userId为visitor
clientDetector.sendClientInfo();

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

}, []);

return (


Demo

);
};

```

## 网络指纹

version >= 1.1.0

网络指纹指通过客户端信息分辨用户的技术,当app没有用户功能去区分使用者时,可以使用网络指纹,它会根据客户端信息生成一个hash值,帮助后台系统做区分。

```js
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 = () => {
useEffect(() => {
// 发送客户端设备信息
// setFingerprint是一个异步方法,会在localstorage中缓存生成的网略指纹
detector.setFingerprint().then(() => detector.sendClientInfo());
}, []);

return (


Demo

);
};

```

## 仅使用网络指纹

version >= 1.1.0

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

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

print();
}, []);

return (


Demo

);
};

```

## 错误收集功能

version >= 1.2.0


案例目录结构

```
my-app
...
├── detector.tsx
├── error-boundary.tsx
├── error-demo.tsx
├── app.tsx
...
```

detector.tsx

```js
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
});

```


error-boundary.tsx 捕获错误,通过client-detector发送错误

```js
import { ReactNode, Component, ErrorInfo } from 'react';
import { clientDetector } from './detector';

export interface ErrorBoundaryProps {
children?: ReactNode;
}

export interface ErrorBoundaryState {
hasError: boolean;
}

class ErrorBoundary extends Component {
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

Something went wrong.

;
}

return this.props.children;
}
}

export default ErrorBoundary;

```

error-demo.tsx

```js
import { FC, ReactNode, useEffect } from 'react';

export interface ErrorDemoProps {
children?: ReactNode;
}

const ErrorDemo: FC = () => {

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

return (


ErrorDemo

);
};

export default ErrorDemo;

```


app.tsx 使用上述组件

```js
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 = () => {
useEffect(() => {
// 发送设备信息
clientDetector.sendClientInfo();
}, []);

return (


请求发送测试页面



);
};

export default App;

```

## 单例模式

version >= 1.3.0



为了简化使用,client-detector提供单例模式,并且继续提供工厂模式(createClientDetector创建实例)的方式。



### 单例模式初始化

在入口文件main.ts中初始化

```js
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(
,
);
```

### 单例模式搜集设备信息

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

const App = () => {

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

init();
}, []);

return (

...

);
};

export default App;

```

### 单例模式发送错误



# 开发

```sh

npm run dev
```