完善监控sdk

This commit is contained in:
xiangheng
2024-10-31 13:11:34 +08:00
parent daeea359bc
commit e65b9f6af8
11 changed files with 424 additions and 205 deletions

View File

@@ -1,14 +1,18 @@
<script setup lang="ts">
import { onUnmounted } from 'vue'
import XLog from '../../../../x_error_sdk/x-log'
import x_log_browser from '../../../../x_error_sdk/x-log-browser'
const xlog = new XLog(
const xLog = new XLog(
{
Dns: 'http://localhost:5174/api',
Pid: 'e19e3be20de94f49b68fafb4c30668bc',
Uid: '10'
},
x_log_browser
new x_log_browser()
)
onUnmounted(() => {
xLog.unListen()
})
defineProps<{ msg: string }>()
function error() {
throw new Error('主动抛error')

View File

@@ -4,13 +4,13 @@
ref="popupRef"
:title="popupTitle"
:async="true"
width="90%"
width="98%"
:clickModalClose="true"
@confirm="handleSubmit"
@close="handleClose"
>
<el-row :gutter="20">
<el-col :span="10">
<el-row :gutter="10">
<el-col :span="18">
<el-card>
<template #header>
<div class="card-header">
@@ -44,7 +44,7 @@
<!-- <template #footer>Footer content</template> -->
</el-card>
</el-col>
<el-col :span="14">
<el-col :span="6">
<el-card>
<template #header>
<div class="card-header">

View File

@@ -38,7 +38,7 @@
</el-card>
<el-card class="!border-none mt-4" shadow="never">
<div class="text-right">
<el-button
<!-- <el-button
v-perms="['admin:monitor_error:add']"
type="primary"
@click="handleAdd()"
@@ -47,8 +47,8 @@
<icon name="el-icon-Plus" />
</template>
新增
</el-button>
<upload
</el-button> -->
<!-- <upload
v-perms="['admin:monitor_error:ImportFile']"
class="ml-3 mr-3"
:url="monitor_error_import_file"
@@ -63,8 +63,8 @@
</template>
导入
</el-button>
</upload>
<el-button
</upload> -->
<!-- <el-button
v-perms="['admin:monitor_error:ExportFile']"
type="primary"
@click="exportFile"
@@ -73,7 +73,7 @@
<icon name="el-icon-Download" />
</template>
导出
</el-button>
</el-button> -->
<el-button
v-perms="['admin:monitor_error:delBatch']"
type="danger"
@@ -154,9 +154,7 @@ import { ref, reactive, shallowRef, nextTick } from 'vue'
import {
monitor_error_delete,
monitor_error_delete_batch,
monitor_error_list,
monitor_error_import_file,
monitor_error_export_file
monitor_error_list
} from '@/api/monitor/error'
import type { type_monitor_error, type_monitor_error_query } from '@/api/monitor/error'
@@ -197,11 +195,6 @@ const { listAllData } = useListAllData<{
})
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
const handleAdd = async () => {
showEdit.value = true
await nextTick()
editRef.value?.open('add')
}
const multipleSelection = ref<type_monitor_error[]>([])
const handleSelectionChange = (val: type_monitor_error[]) => {
@@ -240,11 +233,5 @@ const deleteBatch = async () => {
} catch (error) {}
}
const exportFile = async () => {
try {
await feedback.confirm('确定要导出?')
await monitor_error_export_file(queryParams)
} catch (error) {}
}
getLists()
</script>

View File

@@ -229,18 +229,34 @@ func (service monitorClientService) Del(Id int) (e error) {
err = service.db.Delete(&obj).Error
e = response.CheckErr(err, "删除失败")
cacheUtil.RemoveCache(obj.Id)
cacheUtil.RemoveCache("ClientId:" + obj.ClientId)
return
}
// DelBatch 用户协议-批量删除
func (service monitorClientService) DelBatch(Ids []string) (e error) {
var obj model.MonitorClient
err := service.db.Where("id in (?)", Ids).Delete(&obj).Error
var obj []model.MonitorClient
// 查询Ids对应的数据
err := service.db.Where("id in (?)", Ids).Find(&obj).Error
if err != nil {
return err
}
if len(obj) == 0 {
return errors.New("数据不存在")
}
err = service.db.Where("id in (?)", Ids).Delete(model.MonitorClient{}).Error
if err != nil {
return err
}
// md5集合
var Clients []string
for _, v := range obj {
Clients = append(Clients, "ClientId:"+v.ClientId)
}
// 删除缓存
cacheUtil.RemoveCache(Ids)
cacheUtil.RemoveCache(Clients)
return nil
}

View File

@@ -1,6 +1,7 @@
package monitor_error
import (
"errors"
"strconv"
"x_admin/admin/monitor_client"
"x_admin/admin/monitor_error_list"
@@ -146,41 +147,42 @@ func (service monitorErrorService) DetailByMD5(md5 string) (res MonitorErrorResp
}
// Add 监控-错误列新增
func (service monitorErrorService) Add(addReq MonitorErrorAddReq) (createId int, e error) {
func (service monitorErrorService) Add(addReq MonitorErrorAddReq) (createId int, err error) {
var obj model.MonitorError
util.ConvertUtil.StructToStruct(addReq, &obj)
Md5 := util.ToolsUtil.MakeMd5(obj.ProjectKey + obj.EventType + obj.Message + obj.Path + obj.Stack)
errorDetails, e := service.DetailByMD5(Md5)
if e != nil {
errorDetails, err := service.DetailByMD5(Md5)
if err != nil {
obj.Md5 = Md5
err := service.db.Create(&obj).Error
e = response.CheckMysqlErr(err)
if e != nil {
return 0, e
err = response.CheckMysqlErr(err)
if err != nil {
return 0, err
}
createId = obj.Id
cacheUtil.SetCache(createId, obj)
cacheUtil.SetCache("md5:"+Md5, obj)
} else {
createId = errorDetails.Id
}
client, err := monitor_client.MonitorClientService.DetailByClientId(addReq.ClientId)
if err != nil {
return
return 0, err
}
monitor_error_list.MonitorErrorListService.Add(monitor_error_list.MonitorErrorListAddReq{
ErrorId: strconv.Itoa(createId),
ClientId: strconv.Itoa(client.Id),
_, err = monitor_error_list.MonitorErrorListService.Add(monitor_error_list.MonitorErrorListAddReq{
Eid: strconv.Itoa(createId),
Cid: strconv.Itoa(client.Id),
// ClientId: addReq.ClientId,
ProjectKey: addReq.ProjectKey,
// ProjectKey: addReq.ProjectKey,
})
return
return createId, err
}
// Del 监控-错误列删除
@@ -198,18 +200,33 @@ func (service monitorErrorService) Del(Id int) (e error) {
err = service.db.Delete(&obj).Error
e = response.CheckErr(err, "删除失败")
cacheUtil.RemoveCache(obj.Id)
cacheUtil.RemoveCache("md5:" + obj.Md5)
return
}
// DelBatch 用户协议-批量删除
func (service monitorErrorService) DelBatch(Ids []string) (e error) {
var obj model.MonitorError
err := service.db.Where("id in (?)", Ids).Delete(&obj).Error
var obj []model.MonitorError
// 查询Ids对应的数据
err := service.db.Where("id in (?)", Ids).Find(&obj).Error
if err != nil {
return err
}
if len(obj) == 0 {
return errors.New("数据不存在")
}
err = service.db.Where("id in (?)", Ids).Delete(model.MonitorError{}).Error
if err != nil {
return err
}
// md5集合
var md5s []string
for _, v := range obj {
md5s = append(md5s, "md5:"+v.Md5)
}
// 删除缓存
cacheUtil.RemoveCache(Ids)
cacheUtil.RemoveCache(md5s)
return nil
}

View File

@@ -6,16 +6,16 @@ import (
// MonitorErrorListAddReq 错误对应的用户记录新增参数
type MonitorErrorListAddReq struct {
ErrorId string // 错误id
ClientId string // 客户端id
ProjectKey string // 项目id
Eid string // 错误id
Cid string // 客户端id
// ProjectKey string // 项目id
}
// MonitorErrorListResp 错误对应的用户记录返回信息
type MonitorErrorListResp struct {
Id int // 项目id
ErrorId string // 错误id
ClientId string // 客户端id
ProjectKey string // 项目id
Id int // 项目id
Eid string // 错误id
Cid string // 客户端id
// ProjectKey string // 项目id
CreateTime core.NullTime // 创建时间
}

View File

@@ -6,9 +6,8 @@ import (
// MonitorErrorList 错误对应的用户记录实体
type MonitorErrorList struct {
Id int `gorm:"primarykey;comment:'项目id'"` // 项目id
ErrorId string `gorm:"comment:'错误id'"` // 错误id
ClientId string `gorm:"comment:'客户端id'"` // 客户端id
ProjectKey string `gorm:"comment:'项目key'"` // 项目id
Id int `gorm:"primarykey;comment:'id'"`
Eid string `gorm:"comment:'错误id'"` // 错误id
Cid string `gorm:"comment:'客户端id'"` // 客户端id
CreateTime core.NullTime `gorm:"autoCreateTime;comment:'创建时间'"` // 创建时间
}

View File

@@ -5,7 +5,7 @@ export type LogWithEnv = {
ScreenWidth?: number;
};
export type LogWithError = {
Type: "error"|"event"|"resources";
Type: "error"|"event"|"resources"|'click'|'historyChange'|'hashChange';
EventType: string;
Path:string;
Message: string;
@@ -13,13 +13,14 @@ export type LogWithError = {
};
export interface Platform {
upload(url: string, data: Object);
// 扩展必须实现的接口
export interface IErrorEvent {
upload(url: string, data: object): Promise<void>;
setCache(key: string, info: any): void;
getCache(key: string): any;
delCache(key: string): void;
getEnvInfo(): LogWithEnv;
listen(callback: ListenCallbackFn): void;
getCache(key: string): any;
setCache(key: string, info: any): void;
delCache(key: string): void;
unListen(): void;
}
export type ListenCallbackFn = (params: LogWithError) => void;

View File

@@ -0,0 +1,170 @@
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;
}
function errorCallback(err:Event) {
}
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 function unListen() {
window.removeEventListener("error", () => {});
window.removeEventListener("unhandledrejection", () => {});
}
export default {
getEnvInfo,
listenError,
upload,
getCache,
setCache,
delCache,
listen,
};

View File

@@ -1,160 +1,180 @@
import type { LogWithError, LogWithEnv, ListenCallbackFn } from "./types";
export function upload(url: string, data: object[]) {
return new Promise(() => {
import type {
LogWithError,
LogWithEnv,
ListenCallbackFn,
IErrorEvent,
} from "./types";
class Logger implements IErrorEvent {
public upload(url: string, data: object): Promise<void> {
return new Promise((resolve) => {
try {
let h = new Image();
h.onload = function (event) {
var e = event as Event;
e.preventDefault();
};
h.onerror = function () {};
h.src = url + "?data=" + encodeURIComponent(JSON.stringify(data));
resolve();
} catch (error) {
resolve();
}
});
}
public setCache(key: string, info: any): void {
localStorage.setItem(key, JSON.stringify(info));
}
public getCache(key: string): any {
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 {
let list = localStorage.getItem(key);
if (list) {
return JSON.parse(list);
} else {
return null;
}
} catch (error) {
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;
}
public delCache(key: string): void {
localStorage.removeItem(key);
}
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 || "",
public getEnvInfo(): LogWithEnv {
const env: LogWithEnv = {
Type: "env",
ScreenHeight: 0,
ScreenWidth: 0,
};
if (window) {
env.ScreenHeight = window.innerHeight || 0; // 获取显示屏信息
env.ScreenWidth = window.innerWidth || 0;
}
return env;
}
private callback(err: LogWithError): void {}
private listenError = (err: any) => {
console.error(err);
let target = err.target;
if (target?.localName) {
if (target?.localName === "img" || target?.localName === "script") {
this.callback({
Type: "resources",
EventType: target?.localName,
Path: target.src,
Message: "",
Stack: "",
});
} else if (target?.localName === "link") {
this.callback({
Type: "resources",
EventType: target?.localName,
Path: target.href,
Message: "",
Stack: "",
});
}
},
true
);
window.addEventListener("unhandledrejection", (err) => {
if (typeof err.reason == "string") {
// errorLog(err.type, err.reason, "");
callback({
} else {
this.callback({
Type: "error",
EventType: err.type,
Path:window.location.href,
Path: window.location.href,
Message: err.message,
Stack: this.handleStack(err.error?.stack || ""),
});
}
};
private unhandledrejection = (err: any): void => {
if (err && typeof err.reason === "string") {
this.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({
} else if (err && typeof err.reason === "object") {
this.callback({
Type: "error",
EventType: err.type,
Path:window.location.href,
Path: window.location.href,
Message: err.reason?.message || "",
Stack: err.reason?.stack || "",
Stack: this.handleStack(err.reason?.stack || ""),
});
}
});
};
listenClick = (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.slice(0, 20));
}
this.callback({
Type: "click",
EventType: "click",
Path: window.location.href,
Message: name.join(""),
Stack: "",
});
};
listenHistoryRouterChange = () => {
this.callback({
Type: "historyChange",
EventType: "popstate",
Path: window.location.href,
Message: "",
Stack: "",
});
};
// 监听hash
private listenHashRouterChange = (e) => {
this.callback({
Type: "hashChange",
EventType: "hashChange",
Path: window.location.href,
Message: "",
Stack: "",
});
};
private handleStack(stack: string): string {
let newStack: string[] = [];
if (stack) {
stack.split("\n").map((item, index) => {
if (index < 4) {
newStack.push(item);
}
});
}
return newStack.join("\n");
}
public listen(callback: ListenCallbackFn): void {
this.callback = callback;
window.addEventListener("unhandledrejection", this.unhandledrejection);
window.addEventListener("error", this.listenError, true);
// window.addEventListener("click", this.listenClick);
window.addEventListener("hashchange", this.listenHashRouterChange);
window.addEventListener("popstate",this.listenHistoryRouterChange);
}
public unListen(): void {
this.callback = () => {};
window.removeEventListener("error", this.listenError);
window.removeEventListener("unhandledrejection", this.unhandledrejection);
window.removeEventListener("click", this.listenClick);
window.removeEventListener("hashchange", this.listenHashRouterChange);
window.removeEventListener("popstate",this.listenHistoryRouterChange);
}
}
// 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,
};
export default Logger;

View File

@@ -1,5 +1,5 @@
import { v1 as uuid_v1 } from "uuid";
import type { LogWithError, Platform,LogWithEnv } from "./types";
import type { LogWithError, IErrorEvent,LogWithEnv } from "./types";
type Uid = string | number;
@@ -15,11 +15,12 @@ class XLog {
private Pid: string = "";
private Uid: Uid = "";
private platform: Platform;
private platform: IErrorEvent;
MessageList: any[] = [];
timer: number;
constructor(props: Props, platform: Platform) {
constructor(props: Props, platform: IErrorEvent) {
this.platform = platform;
if (props) {
props.Dns && (this.Dns = props.Dns);
@@ -33,12 +34,12 @@ class XLog {
// 监听错误
platform.listen((params: LogWithError) => {
console.log('listen',params);
console.log('listenCallback',params);
this.Push(params);
});
setInterval(() => {
this.timer=setInterval(() => {
this.upload();
}, 1000 * 10);
}
@@ -133,6 +134,10 @@ class XLog {
this.platform.delCache("x_message_list");
} catch (error) {}
}
public unListen(){
clearInterval(this.timer)
this.platform.unListen()
}
// public Vue2 = (app: any) => {
// app.prototype.$xLog = this;
// };