mirror of
https://gitee.com/xiangheng/x_admin.git
synced 2025-10-06 00:27:00 +08:00
错误上报sdk
This commit is contained in:
62
admin/src/views/error/test.vue
Normal file
62
admin/src/views/error/test.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import XLog from '../../../../x_error_sdk/x-log'
|
||||
import x_log_browser from '../../../../x_error_sdk/x-log-browser'
|
||||
const xlog = new XLog(
|
||||
{
|
||||
Dns: 'http://localhost:5174/api',
|
||||
Pid: 'e19e3be20de94f49b68fafb4c30668bc',
|
||||
Uid: '10'
|
||||
},
|
||||
x_log_browser
|
||||
)
|
||||
defineProps<{ msg: string }>()
|
||||
function error() {
|
||||
throw new Error('主动抛error')
|
||||
}
|
||||
function promiseError() {
|
||||
Promise.reject('主动抛promise1')
|
||||
Promise.reject(new Error('主动抛promiseError2'))
|
||||
}
|
||||
function yufaError() {
|
||||
// try {
|
||||
setTimeout(() => {
|
||||
a = 1
|
||||
// a = 2;
|
||||
}, 100)
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
}
|
||||
function loadError() {
|
||||
const img = new Image()
|
||||
console.log(img)
|
||||
img.src = 'http://a.cn'
|
||||
document.body.appendChild(img)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button class="a b" type="button" aa="11" @click="error">主动抛error</button>
|
||||
<button class="a b" type="button" aa="11" @click="yufaError">语法错误</button>
|
||||
<button class="a b c" id="abv" type="button" @click="promiseError">promiseerror</button>
|
||||
<button type="button" @click="loadError">资源加载错误</button>
|
||||
|
||||
<!-- <img src="http://a.cn/a/b" alt="" srcset="" />
|
||||
<link rel="stylesheet" href="http://a.cn/c/c/c" /> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
color: #fff;
|
||||
background-color: rgb(150, 149, 149);
|
||||
border: 0;
|
||||
padding: 6px 10px;
|
||||
margin: 6px;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
@@ -20,9 +20,9 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户端id" prop="ClientId" class="w-[280px]">
|
||||
<!-- <el-form-item label="客户端id" prop="ClientId" class="w-[280px]">
|
||||
<el-input v-model="queryParams.ClientId" />
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="用户id" prop="UserId" class="w-[280px]">
|
||||
<el-input v-model="queryParams.UserId" />
|
||||
</el-form-item>
|
||||
@@ -47,10 +47,10 @@
|
||||
<el-form-item label="ip" prop="Ip" class="w-[280px]">
|
||||
<el-input v-model="queryParams.Ip" />
|
||||
</el-form-item>
|
||||
<el-form-item label="ua记录" prop="Ua" class="w-[280px]">
|
||||
<!-- <el-form-item label="ua记录" prop="Ua" class="w-[280px]">
|
||||
<el-input v-model="queryParams.Ua" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="CreateTime" class="w-[280px]">
|
||||
</el-form-item> -->
|
||||
<el-form-item label="创建时间" prop="CreateTime" class="w-[425px]">
|
||||
<daterange-picker
|
||||
v-model:startTime="queryParams.CreateTimeStart"
|
||||
v-model:endTime="queryParams.CreateTimeEnd"
|
||||
@@ -128,21 +128,56 @@
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户端id" prop="ClientId" min-width="130" />
|
||||
<el-table-column label="用户id" prop="UserId" min-width="130" />
|
||||
<el-table-column label="系统" prop="Os" min-width="130" />
|
||||
<el-table-column label="浏览器" prop="Browser" min-width="130" />
|
||||
<el-table-column label="国家" prop="Country" min-width="130" />
|
||||
<el-table-column label="省份" prop="Province" min-width="130" />
|
||||
<el-table-column label="城市" prop="City" min-width="130" />
|
||||
<el-table-column label="电信运营商" prop="Operator" min-width="130" />
|
||||
<el-table-column label="ip" prop="Ip" min-width="130" />
|
||||
<el-table-column label="屏幕" prop="Width" min-width="130">
|
||||
<!-- <el-table-column label="客户端id" prop="ClientId" min-width="130" /> -->
|
||||
<el-table-column label="用户id" prop="UserId">
|
||||
<template #default="{ row }">
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
:width="500"
|
||||
trigger="hover"
|
||||
:content="row.ClientId"
|
||||
>
|
||||
<template #reference>
|
||||
<el-link type="primary">{{ row.UserId }}</el-link>
|
||||
</template>
|
||||
<div>用 户 ID :{{ row.UserId }}</div>
|
||||
<div>客户端ID:{{ row.ClientId }}</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="浏览器" prop="Browser" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
title="浏览器ua"
|
||||
:width="500"
|
||||
trigger="hover"
|
||||
:content="row.Ua"
|
||||
>
|
||||
<template #reference>
|
||||
<el-link type="primary">{{ row.Os }} / {{ row.Browser }}</el-link>
|
||||
</template>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="IP" prop="Ip" />
|
||||
<el-table-column label="区域">
|
||||
<template #default="{ row }">
|
||||
{{ row.Country }}{{ row.Province }}{{ row.City }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- <el-table-column label="省份" prop="Province" />
|
||||
<el-table-column label="城市" prop="City" /> -->
|
||||
<el-table-column label="运营商" prop="Operator" />
|
||||
|
||||
<el-table-column label="屏幕" prop="Width">
|
||||
<template #default="{ row }"> {{ row.Width }} * {{ row.Height }} </template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="屏幕高度" prop="Height" min-width="130" /> -->
|
||||
<el-table-column label="ua记录" prop="Ua" min-width="380" />
|
||||
<el-table-column label="创建时间" prop="CreateTime" min-width="130" />
|
||||
<!-- <el-table-column label="ua记录" prop="Ua" min-width="380" /> -->
|
||||
<el-table-column label="创建时间" prop="CreateTime" min-width="140" />
|
||||
|
||||
<el-table-column label="操作" width="80" fixed="right">
|
||||
<template #default="{ row }">
|
||||
|
@@ -105,7 +105,7 @@
|
||||
<el-table-column label="事件类型" prop="EventType" min-width="130" />
|
||||
<el-table-column label="URL地址" prop="Path" min-width="130" />
|
||||
<el-table-column label="错误消息" prop="Message" min-width="130" />
|
||||
<el-table-column label="错误堆栈" prop="Stack" min-width="130" />
|
||||
<!-- <el-table-column label="错误堆栈" prop="Stack" min-width="130" /> -->
|
||||
<el-table-column label="md5" prop="Md5" min-width="130" />
|
||||
<el-table-column label="创建时间" prop="CreateTime" min-width="130" />
|
||||
|
||||
|
@@ -37,13 +37,13 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="CreateTime" class="w-[280px]">
|
||||
<el-form-item label="创建时间" prop="CreateTime" class="w-[430px]">
|
||||
<daterange-picker
|
||||
v-model:startTime="queryParams.CreateTimeStart"
|
||||
v-model:endTime="queryParams.CreateTimeEnd"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="UpdateTime" class="w-[280px]">
|
||||
<el-form-item label="更新时间" prop="UpdateTime" class="w-[420px]">
|
||||
<daterange-picker
|
||||
v-model:startTime="queryParams.UpdateTimeStart"
|
||||
v-model:endTime="queryParams.UpdateTimeEnd"
|
||||
|
@@ -1,7 +1,10 @@
|
||||
package monitor_client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -135,14 +138,37 @@ func (hd *MonitorClientHandler) Detail(c *gin.Context) {
|
||||
// @Success 200 {object} response.Response "成功"
|
||||
// @Router /api/admin/monitor_client/add [post]
|
||||
func (hd *MonitorClientHandler) Add(c *gin.Context) {
|
||||
|
||||
var addReq MonitorClientAddReq
|
||||
if response.IsFailWithResp(c, util.VerifyUtil.VerifyJSON(c, &addReq)) {
|
||||
data, err := url.QueryUnescape(c.Query("data"))
|
||||
if err != nil {
|
||||
response.CheckAndRespWithData(c, 0, err)
|
||||
return
|
||||
}
|
||||
var addReq MonitorClientAddReq
|
||||
json.Unmarshal([]byte(data), &addReq)
|
||||
// if response.IsFailWithResp(c, util.VerifyUtil.VerifyJSON(c, &addReq)) {
|
||||
// return
|
||||
// }
|
||||
|
||||
lastClient, err := MonitorClientService.DetailByClientId(*addReq.ClientId)
|
||||
|
||||
uaStr := c.GetHeader("user-agent")
|
||||
ip := c.ClientIP()
|
||||
|
||||
// createFlag := true
|
||||
if err == nil {
|
||||
last := lastClient.UserId + lastClient.Width.String() + lastClient.Height.String() + lastClient.Ip + lastClient.Ua
|
||||
newStr := *addReq.UserId + addReq.Width.String() + addReq.Height.String() + ip + uaStr
|
||||
if last == newStr {
|
||||
// 前后数据一样,不用创建新的数据
|
||||
fmt.Println("前后数据一样,不用创建新的数据")
|
||||
response.CheckAndRespWithData(c, 0, nil)
|
||||
return
|
||||
} else {
|
||||
// 新建的话,需要清除lastClient对应的缓存
|
||||
cacheUtil.RemoveCache("ClientId:" + lastClient.ClientId)
|
||||
}
|
||||
}
|
||||
|
||||
if uaStr != "" {
|
||||
ua := core.UAParser.Parse(uaStr)
|
||||
addReq.Ua = &uaStr
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package monitor_client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"x_admin/core"
|
||||
"x_admin/core/request"
|
||||
"x_admin/core/response"
|
||||
@@ -111,34 +112,6 @@ func (service monitorClientService) List(page request.PageReq, listReq MonitorCl
|
||||
}, nil
|
||||
}
|
||||
|
||||
// List 监控-客户端信息列表
|
||||
// func (service monitorClientService) ListByErrorId(page request.PageReq, listReq MonitorClientListReq, errorId int) (res response.PageResp, e error) {
|
||||
// // 分页信息
|
||||
// limit := page.PageSize
|
||||
// offset := page.PageSize * (page.PageNo - 1)
|
||||
// dbModel := service.GetModel(listReq)
|
||||
// // 总数
|
||||
// var count int64
|
||||
// err := dbModel.Count(&count).Error
|
||||
// if e = response.CheckErr(err, "失败"); e != nil {
|
||||
// return
|
||||
// }
|
||||
// // 数据
|
||||
// var modelList []model.MonitorClient
|
||||
// err = dbModel.Limit(limit).Offset(offset).Order("id desc").Find(&modelList).Error
|
||||
// if e = response.CheckErr(err, "查询失败"); e != nil {
|
||||
// return
|
||||
// }
|
||||
// result := []MonitorClientResp{}
|
||||
// util.ConvertUtil.Copy(&result, modelList)
|
||||
// return response.PageResp{
|
||||
// PageNo: page.PageNo,
|
||||
// PageSize: page.PageSize,
|
||||
// Count: count,
|
||||
// Lists: result,
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
// ListAll 监控-客户端信息列表
|
||||
func (service monitorClientService) ListAll(listReq MonitorClientListReq) (res []MonitorClientResp, e error) {
|
||||
dbModel := service.GetModel(listReq)
|
||||
@@ -154,6 +127,9 @@ func (service monitorClientService) ListAll(listReq MonitorClientListReq) (res [
|
||||
}
|
||||
|
||||
func (service monitorClientService) DetailByClientId(ClientId string) (res MonitorClientResp, e error) {
|
||||
if ClientId == "" {
|
||||
return res, errors.New("ClientId不能为空")
|
||||
}
|
||||
var obj = model.MonitorClient{}
|
||||
err := cacheUtil.GetCache("ClientId:"+ClientId, &obj)
|
||||
if err != nil {
|
||||
@@ -164,6 +140,7 @@ func (service monitorClientService) DetailByClientId(ClientId string) (res Monit
|
||||
if e = response.CheckErr(err, "获取详情失败"); e != nil {
|
||||
return
|
||||
}
|
||||
cacheUtil.SetCache(obj.Id, obj)
|
||||
cacheUtil.SetCache("ClientId:"+obj.ClientId, obj)
|
||||
}
|
||||
|
||||
@@ -184,6 +161,7 @@ func (service monitorClientService) Detail(Id int) (res MonitorClientResp, e err
|
||||
return
|
||||
}
|
||||
cacheUtil.SetCache(obj.Id, obj)
|
||||
cacheUtil.SetCache("ClientId:"+obj.ClientId, obj)
|
||||
}
|
||||
|
||||
util.ConvertUtil.Copy(&res, obj)
|
||||
@@ -193,7 +171,7 @@ func (service monitorClientService) Detail(Id int) (res MonitorClientResp, e err
|
||||
// ErrorUser 监控-客户端信息详情
|
||||
func (service monitorClientService) ErrorUsers(error_id int) (res []MonitorClientResp, e error) {
|
||||
var obj = []model.MonitorClient{}
|
||||
service.db.Raw("SELECT client.* from x_monitor_error_list as list right join x_monitor_client as client on client.id = list.client_id where list.error_id = ? Order by list.id DESC", error_id).Scan(&obj)
|
||||
service.db.Raw("SELECT client.*,list.create_time AS create_time from x_monitor_error_list as list right join x_monitor_client as client on client.id = list.client_id where list.error_id = ? Order by list.id DESC LIMIT 0,20", error_id).Scan(&obj)
|
||||
|
||||
util.ConvertUtil.Copy(&res, obj)
|
||||
return
|
||||
@@ -209,6 +187,7 @@ func (service monitorClientService) Add(addReq MonitorClientAddReq) (createId in
|
||||
return 0, e
|
||||
}
|
||||
cacheUtil.SetCache(obj.Id, obj)
|
||||
cacheUtil.SetCache("ClientId:"+obj.ClientId, obj)
|
||||
createId = obj.Id
|
||||
return
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package monitor_error
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -103,12 +105,23 @@ func (hd *MonitorErrorHandler) Detail(c *gin.Context) {
|
||||
// @Success 200 {object} response.Response "成功"
|
||||
// @Router /api/admin/monitor_error/add [post]
|
||||
func (hd *MonitorErrorHandler) Add(c *gin.Context) {
|
||||
var addReq MonitorErrorAddReq
|
||||
if response.IsFailWithResp(c, util.VerifyUtil.VerifyJSON(c, &addReq)) {
|
||||
data, err := url.QueryUnescape(c.Query("data"))
|
||||
if err != nil {
|
||||
response.CheckAndRespWithData(c, 0, err)
|
||||
return
|
||||
}
|
||||
createId, e := MonitorErrorService.Add(addReq)
|
||||
response.CheckAndRespWithData(c, createId, e)
|
||||
|
||||
var addReq []MonitorErrorAddReq
|
||||
json.Unmarshal([]byte(data), &addReq)
|
||||
|
||||
// if response.IsFailWithResp(c, util.VerifyUtil.VerifyJSON(c, &addReq)) {
|
||||
// return
|
||||
// }
|
||||
for i := 0; i < len(addReq); i++ {
|
||||
MonitorErrorService.Add(addReq[i])
|
||||
}
|
||||
// createId, e := MonitorErrorService.Add(addReq)
|
||||
response.CheckAndRespWithData(c, nil, nil)
|
||||
}
|
||||
|
||||
// @Summary 监控-错误列删除
|
||||
|
@@ -151,7 +151,7 @@ func (service monitorErrorService) Add(addReq MonitorErrorAddReq) (createId int,
|
||||
var obj model.MonitorError
|
||||
util.ConvertUtil.StructToStruct(addReq, &obj)
|
||||
|
||||
Md5 := util.ToolsUtil.MakeMd5(obj.ProjectKey + obj.Path + obj.Stack)
|
||||
Md5 := util.ToolsUtil.MakeMd5(obj.ProjectKey + obj.EventType + obj.Message + obj.Path + obj.Stack)
|
||||
|
||||
errorDetails, e := service.DetailByMD5(Md5)
|
||||
if e != nil {
|
||||
|
@@ -45,6 +45,7 @@ INSERT INTO x_system_auth_menu (pid, menu_type, menu_name, perms,is_cache, is_sh
|
||||
// MonitorClientRoute(rg)
|
||||
func MonitorClientRoute(rg *gin.RouterGroup) {
|
||||
handle := monitor_client.MonitorClientHandler{}
|
||||
rg.GET("/monitor_client/add", middleware.RecordLog("监控-客户端信息新增"), handle.Add)
|
||||
|
||||
r := rg.Group("/", middleware.TokenAuth())
|
||||
r.GET("/monitor_client/list", handle.List)
|
||||
@@ -52,7 +53,6 @@ func MonitorClientRoute(rg *gin.RouterGroup) {
|
||||
r.GET("/monitor_client/detail", handle.Detail)
|
||||
r.GET("/monitor_client/errorUsers", handle.ErrorUsers)
|
||||
|
||||
r.POST("/monitor_client/add", middleware.RecordLog("监控-客户端信息新增"), handle.Add)
|
||||
// r.POST("/monitor_client/edit",middleware.RecordLog("监控-客户端信息编辑"), handle.Edit)
|
||||
|
||||
r.POST("/monitor_client/del", middleware.RecordLog("监控-客户端信息删除"), handle.Del)
|
||||
|
@@ -45,14 +45,13 @@ INSERT INTO x_system_auth_menu (pid, menu_type, menu_name, perms,is_cache, is_sh
|
||||
// MonitorErrorRoute(rg)
|
||||
func MonitorErrorRoute(rg *gin.RouterGroup) {
|
||||
handle := monitor_error.MonitorErrorHandler{}
|
||||
rg.GET("/monitor_error/add", middleware.RecordLog("监控-错误列新增"), handle.Add)
|
||||
|
||||
r := rg.Group("/", middleware.TokenAuth())
|
||||
r.GET("/monitor_error/list", handle.List)
|
||||
r.GET("/monitor_error/listAll", handle.ListAll)
|
||||
r.GET("/monitor_error/detail", handle.Detail)
|
||||
|
||||
r.POST("/monitor_error/add", middleware.RecordLog("监控-错误列新增"), handle.Add)
|
||||
|
||||
r.POST("/monitor_error/del", middleware.RecordLog("监控-错误列删除"), handle.Del)
|
||||
r.POST("/monitor_error/delBatch", middleware.RecordLog("监控-错误列删除-批量"), handle.DelBatch)
|
||||
|
||||
|
16
x_error_sdk/.gitignore
vendored
Normal file
16
x_error_sdk/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
pnpm-lock.yaml
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
25
x_error_sdk/package.json
Normal file
25
x_error_sdk/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@adtkcn/x-log",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"module": "./index.ts",
|
||||
"main": "./index.ts",
|
||||
"scripts": {
|
||||
"build": "npm run build:index&&npm run build:browser&&npm run build:ts",
|
||||
"build:index": "esbuild x-log.ts --bundle --sourcemap --outfile=dist/x-log.js --format=esm --target=es2015",
|
||||
"build:browser": "esbuild x-log-browser.ts --bundle --sourcemap --outfile=dist/x-log-browser.js --format=esm --target=es2015",
|
||||
"build:ts": "tsc --emitDeclarationOnly"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"typescript": "^5.6.3",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dcloudio/types": "^3.0.19",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"esbuild": "^0.15.14"
|
||||
}
|
||||
}
|
18
x_error_sdk/tsconfig.json
Normal file
18
x_error_sdk/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true, // 生成声明文件,开启后会自动生成声明文件
|
||||
"target": "esnext", // 最新的ES标准
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array"
|
||||
"strict": true, // 开启所有严格的类型检查
|
||||
"noImplicitAny": false, // 不允许隐式的any类型
|
||||
"esModuleInterop": true, // 允许export=导出,由import from 导入
|
||||
"moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
|
||||
"outDir": "dist", // 指定输出目录
|
||||
"types": [
|
||||
"@dcloudio/types"
|
||||
]
|
||||
}
|
||||
}
|
25
x_error_sdk/types.ts
Normal file
25
x_error_sdk/types.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
export type LogWithEnv = {
|
||||
Type: "env";
|
||||
ScreenHeight?: number;
|
||||
ScreenWidth?: number;
|
||||
};
|
||||
export type LogWithError = {
|
||||
Type: "error"|"event"|"resources";
|
||||
EventType: string;
|
||||
Path:string;
|
||||
Message: string;
|
||||
Stack: string;
|
||||
};
|
||||
|
||||
|
||||
export interface Platform {
|
||||
upload(url: string, data: Object);
|
||||
getEnvInfo(): LogWithEnv;
|
||||
listen(callback: ListenCallbackFn): void;
|
||||
getCache(key: string): any;
|
||||
setCache(key: string, info: any): void;
|
||||
delCache(key: string): void;
|
||||
}
|
||||
|
||||
export type ListenCallbackFn = (params: LogWithError) => void;
|
160
x_error_sdk/x-log-browser.ts
Normal file
160
x_error_sdk/x-log-browser.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import type { LogWithError, LogWithEnv, ListenCallbackFn } from "./types";
|
||||
export function upload(url: string, data: object[]) {
|
||||
return new Promise(() => {
|
||||
try {
|
||||
let h = new Image();
|
||||
h.onerror = function (event) {
|
||||
var e = event as Event;
|
||||
e.preventDefault();
|
||||
};
|
||||
h.onload = function () {};
|
||||
h.src = url + "?data=" + encodeURIComponent(JSON.stringify(data));
|
||||
} catch (error) {}
|
||||
});
|
||||
}
|
||||
export function setCache(key: string, info: any) {
|
||||
localStorage.setItem(key, JSON.stringify(info));
|
||||
}
|
||||
export function getCache(key: string): any {
|
||||
try {
|
||||
let list = localStorage.getItem(key);
|
||||
if (list) {
|
||||
return JSON.parse(list);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
export function delCache(key: string) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
// 获取环境信息
|
||||
export function getEnvInfo(): LogWithEnv {
|
||||
const env: LogWithEnv = {
|
||||
Type: "env",
|
||||
ScreenHeight: 0,
|
||||
ScreenWidth: 0,
|
||||
};
|
||||
|
||||
// navigator对象数据
|
||||
if (window ) {
|
||||
env.ScreenHeight = window.innerHeight || 0; // 获取显示屏信息
|
||||
env.ScreenWidth = window.innerWidth || 0;
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
export function listenError(callback: ListenCallbackFn) {
|
||||
window.addEventListener(
|
||||
"error",
|
||||
(err: any) => {
|
||||
console.error(err);
|
||||
let target = err.target;
|
||||
if (target?.localName) {
|
||||
if (target?.localName == "img" || target?.localName == "script") {
|
||||
// errorResources(target?.localName, target.src);
|
||||
callback({
|
||||
Type: "resources",
|
||||
EventType: target?.localName,
|
||||
Path: target.src,
|
||||
Message:'',
|
||||
Stack: "",
|
||||
});
|
||||
} else if (target?.localName == "link") {
|
||||
// errorResources(target?.localName, target.href);
|
||||
callback({
|
||||
Type: "resources",
|
||||
EventType: target?.localName,
|
||||
Path: target.href,
|
||||
Message:'',
|
||||
Stack: "",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// errorLog(err.type, err.message, err.error?.stack || "");
|
||||
callback({
|
||||
Type: "error",
|
||||
EventType: err.type,
|
||||
Path:window.location.href,
|
||||
Message: err.message,
|
||||
Stack: err.error?.stack || "",
|
||||
});
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
window.addEventListener("unhandledrejection", (err) => {
|
||||
if (typeof err.reason == "string") {
|
||||
// errorLog(err.type, err.reason, "");
|
||||
callback({
|
||||
Type: "error",
|
||||
EventType: err.type,
|
||||
Path:window.location.href,
|
||||
Message: err.reason,
|
||||
Stack: "",
|
||||
});
|
||||
} else if (typeof err.reason == "object") {
|
||||
// errorLog(err.type, err.reason?.message || "", err.reason?.stack || "");
|
||||
callback({
|
||||
Type: "error",
|
||||
EventType: err.type,
|
||||
Path:window.location.href,
|
||||
Message: err.reason?.message || "",
|
||||
Stack: err.reason?.stack || "",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// export function listenBehavior(eventLog: EventLog) {
|
||||
// window.addEventListener("click", (e) => {
|
||||
// let target = e.target as HTMLElement;
|
||||
// let tagName = target?.localName;
|
||||
// let name = [tagName];
|
||||
// if (target.id) {
|
||||
// name.push("#" + target.id);
|
||||
// }
|
||||
// target.classList.forEach((item) => {
|
||||
// name.push("." + item);
|
||||
// });
|
||||
// if (target.innerText) {
|
||||
// name.push(":" + target.innerText);
|
||||
// }
|
||||
// eventLog("click", name.join(""));
|
||||
// });
|
||||
// window.addEventListener("hashchange", (e) => {
|
||||
// eventLog("pageChange", e.newURL);
|
||||
// });
|
||||
|
||||
// eventLog("pageChange", window.location.href);
|
||||
// }
|
||||
|
||||
export function listen(callback: ListenCallbackFn) {
|
||||
listenError((params: LogWithError) => {
|
||||
|
||||
if (params.Stack) {
|
||||
let newStack: string[] = [];
|
||||
params.Stack.split("\n").map((item, index) => {
|
||||
if (index < 4) {
|
||||
newStack.push(item);
|
||||
}
|
||||
});
|
||||
params.Stack = newStack.join("\n");
|
||||
}
|
||||
|
||||
callback(params);
|
||||
});
|
||||
}
|
||||
export default {
|
||||
getEnvInfo,
|
||||
listenError,
|
||||
upload,
|
||||
getCache,
|
||||
setCache,
|
||||
delCache,
|
||||
listen,
|
||||
};
|
18
x_error_sdk/x-log-node.ts
Normal file
18
x_error_sdk/x-log-node.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// import http from "http";
|
||||
|
||||
// export function upload(url: string, data: Object) {
|
||||
// let h = new Image();
|
||||
// h.onerror = function () {};
|
||||
// h.src = url + "?log=" + encodeURIComponent(data.toString());
|
||||
|
||||
// const req = http.request(url, { method: "POST" }, (res: any) => {
|
||||
// res.setEncoding("utf8");
|
||||
// res.on("data", (chunk) => {});
|
||||
// // 监听接口请求完成, 除非error,最终都会执行end;
|
||||
// res.on("end", () => {});
|
||||
// });
|
||||
// req.on("error", (e) => {});
|
||||
// // 写入请求的数据
|
||||
// data && req.write(data);
|
||||
// req.end();
|
||||
// }
|
20
x_error_sdk/x-log-uniapp.ts
Normal file
20
x_error_sdk/x-log-uniapp.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export function upload(url: string, data: Object) {
|
||||
if (uni) {
|
||||
uni.request({
|
||||
url: url, //仅为示例,并非真实接口地址。
|
||||
data: {
|
||||
log: encodeURIComponent(JSON.stringify(data)),
|
||||
},
|
||||
method: "POST",
|
||||
header: {
|
||||
// xlog: "version", //自定义请求头信息
|
||||
},
|
||||
success: (res: any) => {
|
||||
console.log(res.data);
|
||||
},
|
||||
fail: (err: any) => {
|
||||
console.log(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
143
x_error_sdk/x-log.ts
Normal file
143
x_error_sdk/x-log.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { v1 as uuid_v1 } from "uuid";
|
||||
import type { LogWithError, Platform,LogWithEnv } from "./types";
|
||||
|
||||
type Uid = string | number;
|
||||
|
||||
type Props = {
|
||||
Dns: string; // 域名
|
||||
Pid: string; // 项目标识
|
||||
Uid?: Uid; //用户标识
|
||||
};
|
||||
|
||||
class XLog {
|
||||
private Dns: string = "";
|
||||
private client_id: string = "";
|
||||
private Pid: string = "";
|
||||
private Uid: Uid = "";
|
||||
|
||||
private platform: Platform;
|
||||
|
||||
MessageList: any[] = [];
|
||||
|
||||
constructor(props: Props, platform: Platform) {
|
||||
this.platform = platform;
|
||||
if (props) {
|
||||
props.Dns && (this.Dns = props.Dns);
|
||||
props.Pid && (this.Pid = props.Pid);
|
||||
props.Uid && (this.Uid = props.Uid);
|
||||
}
|
||||
this.SetClientID();
|
||||
this.getLocalMessage();
|
||||
this.initEnv();
|
||||
|
||||
|
||||
// 监听错误
|
||||
platform.listen((params: LogWithError) => {
|
||||
console.log('listen',params);
|
||||
|
||||
this.Push(params);
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
this.upload();
|
||||
}, 1000 * 10);
|
||||
}
|
||||
private initEnv() {
|
||||
let envInfo:LogWithEnv = this.platform.getEnvInfo();
|
||||
if (envInfo) {
|
||||
this.uploadInfo(envInfo);
|
||||
}
|
||||
}
|
||||
// 关联用户id
|
||||
public SetUid(uid: Uid) {
|
||||
this.Uid = uid;
|
||||
this.initEnv();
|
||||
}
|
||||
// 设置错误ID
|
||||
private SetClientID() {
|
||||
const client_id = this.platform.getCache("x_log_client_id");
|
||||
if (client_id) {
|
||||
this.client_id = client_id;
|
||||
} else {
|
||||
this.client_id = uuid_v1();
|
||||
this.platform.setCache("x_log_client_id", this.client_id);
|
||||
}
|
||||
}
|
||||
// 获取本地缓存消息
|
||||
private getLocalMessage() {
|
||||
let x_message_list = this.platform.getCache("x_message_list");
|
||||
if (x_message_list) {
|
||||
this.MessageList = x_message_list;
|
||||
} else {
|
||||
this.MessageList = [];
|
||||
}
|
||||
}
|
||||
// 记录错误,超出5条时立即上报,否则缓存到本地(等待定时器上报)
|
||||
public Push(data: LogWithError) {
|
||||
console.log(this);
|
||||
|
||||
this.MessageList.push({
|
||||
...data,
|
||||
ProjectKey:this.Pid,
|
||||
ClientId:this.client_id
|
||||
});
|
||||
|
||||
if (this.MessageList.length > 5) {
|
||||
this.upload(); //上传
|
||||
} else {
|
||||
this.platform.setCache(
|
||||
"x_message_list",
|
||||
this.MessageList
|
||||
);
|
||||
}
|
||||
}
|
||||
public uploadInfo(envInfo:LogWithEnv ) {
|
||||
if (!this.Dns) return; //未设置Dns服务器不上传
|
||||
|
||||
try {
|
||||
this.platform
|
||||
.upload(
|
||||
this.Dns +
|
||||
`/admin/monitor_client/add`,
|
||||
{
|
||||
ProjectKey:this.Pid,
|
||||
ClientId:this.client_id,
|
||||
UserId:this.Uid,
|
||||
Width: envInfo.ScreenWidth,
|
||||
Height:envInfo.ScreenHeight
|
||||
}
|
||||
)
|
||||
.catch((err: any) => {
|
||||
// 上传失败
|
||||
});
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
public upload() {
|
||||
if (!this.Dns) return; //未设置Dns服务器不上传
|
||||
if (!this.MessageList.length) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.platform
|
||||
.upload(
|
||||
this.Dns +
|
||||
`/admin/monitor_error/add`,
|
||||
this.MessageList
|
||||
)
|
||||
.catch((err: any) => {
|
||||
// 上传失败
|
||||
});
|
||||
this.MessageList = [];
|
||||
this.platform.delCache("x_message_list");
|
||||
} catch (error) {}
|
||||
}
|
||||
// public Vue2 = (app: any) => {
|
||||
// app.prototype.$xLog = this;
|
||||
// };
|
||||
// public Vue3 = (app: any) => {
|
||||
// app.config.globalProperties.$xLog = this;
|
||||
// };
|
||||
}
|
||||
export default XLog;
|
Reference in New Issue
Block a user