前端监控完成
This commit is contained in:
@@ -1,15 +1,21 @@
|
||||
<template>
|
||||
<section class="AppApiMonitor">
|
||||
<ai-list>
|
||||
<ai-title slot="title" :title="$options.label" isShowBottomBorder/>
|
||||
<ai-title slot="title" :title="$options.label" isShowBottomBorder>
|
||||
<template #rightBtn>
|
||||
<el-button @click="page.current=1,getStaData(),getTableData()">刷新</el-button>
|
||||
</template>
|
||||
</ai-title>
|
||||
<template #blank>
|
||||
<el-row type="flex">
|
||||
<ai-card title="接口异常分布">
|
||||
<ai-echart/>
|
||||
<el-row type="flex" class="topRow">
|
||||
<ai-card title="接口异常分布" headerPanel>
|
||||
<ai-echart class="sta" type="pie" :data="sta.distribution"/>
|
||||
</ai-card>
|
||||
<ai-card class="fill mar-l16" title="接口异常TOP10" headerPanel>
|
||||
<ai-table :tableData="sta.top10" :colConfigs="top10Columns" :isShowPagination="false" tableSize="mini"/>
|
||||
</ai-card>
|
||||
<ai-card class="fill mar-l16" title="接口异常TOP10"></ai-card>
|
||||
</el-row>
|
||||
<ai-card panel>
|
||||
<ai-card panel class="mar-t16">
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<ai-select placeholder="状态码" v-model="search.status" :selectList="networkStatus" @change="page.current=1,getTableData()"/>
|
||||
@@ -18,11 +24,19 @@
|
||||
<el-input v-model="search.name" size="small" placeholder="搜索接口" clearable @change="page.current=1,getTableData()"/>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table :tableData="tableData" :colConfigs="columns" :pageConfig.sync="page" @getList="getTableData" :dict="dict"/>
|
||||
<ai-table :tableData="tableData" :colConfigs="columns" :current.sync="page.current" :size.sync="page.size" :total="page.total"
|
||||
@getList="getTableData" :dict="dict">
|
||||
<el-table-column slot="expand" type="expand">
|
||||
<template slot-scope="{row}">
|
||||
<ai-wrapper>
|
||||
<ai-info-item labelWidth="100px" v-for="op in desConfigs" :key="op.prop" :value="row[op.prop]" v-bind="op"/>
|
||||
</ai-wrapper>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</ai-card>
|
||||
</template>
|
||||
</ai-list>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -44,15 +58,24 @@ export default {
|
||||
tableData: [],
|
||||
page: {current: 1},
|
||||
search: {},
|
||||
columns: [
|
||||
{label: "状态码", prop: "status"},
|
||||
sta: {top10: [], distribution: []},
|
||||
top10Columns: [
|
||||
{label: "接口地址", prop: "path"},
|
||||
{label: "次数", prop: "total", width: 80, align: 'center'},
|
||||
{label: "创建时间", prop: "createTime", width: 160},
|
||||
],
|
||||
columns: [
|
||||
{slot: 'expand'},
|
||||
{label: "接口地址", prop: "path"},
|
||||
{label: "状态码", prop: "status", width: 100, align: 'center'},
|
||||
{label: "创建时间", prop: "createTime", width: 160},
|
||||
{label: "错误信息", prop: "error"},
|
||||
],
|
||||
desConfigs: [
|
||||
{label: "终端", prop: "device"},
|
||||
{label: "页面", prop: "url"},
|
||||
{label: "用户", prop: "userName"},
|
||||
{label: "环境", prop: "nodeProcess"},
|
||||
{label: "创建时间", prop: "createTime"},
|
||||
{label: "页面", prop: "url", isLine: true},
|
||||
{label: "错误信息", prop: "error", isLine: true},
|
||||
],
|
||||
networkStatus: [
|
||||
{dictValue: 200, dictName: '200:成功'},
|
||||
@@ -74,15 +97,28 @@ export default {
|
||||
this.page.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
getStaData() {
|
||||
this.instance.post("/node/monitorApi/sta").then(res => {
|
||||
if (res?.data) {
|
||||
this.sta = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTableData()
|
||||
this.getStaData()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AppApiMonitor {
|
||||
|
||||
.sta {
|
||||
width: 400px;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,38 +1,67 @@
|
||||
/**
|
||||
* 获取符合要求的请求
|
||||
* @param entries 监测的请求对象
|
||||
* @param type 设置满足条件的请求类型
|
||||
* @returns {PerformanceEntry[]}
|
||||
*/
|
||||
const getRequests = (entries = performance.getEntriesByType('resource'), type = ['xmlhttprequest']) =>
|
||||
entries?.filter(e => type.includes(e.initiatorType)) || []
|
||||
import http from "./request"
|
||||
|
||||
/**
|
||||
* 观察者工具对象,用于前端接口监测
|
||||
*/
|
||||
|
||||
class Observer {
|
||||
constructor() {
|
||||
this.saveLogs(getRequests())
|
||||
this.ins = new PerformanceObserver((list, ob) => {
|
||||
const watchLogs = getRequests(list.getEntriesByType("resource"))
|
||||
this.saveLogs(watchLogs)
|
||||
})
|
||||
this.ins.observe({entryTypes: ["resource"]})
|
||||
this.initXHRObserver()
|
||||
}
|
||||
|
||||
saveLogs(list = []) {
|
||||
list.map(e => {
|
||||
if (!/sockjs/.test(e.name)) {
|
||||
const api = {
|
||||
status: e.responseStatus,
|
||||
path: e.name,
|
||||
url: location.href,
|
||||
nodeProcess: process.env.NODE_ENV,
|
||||
}
|
||||
console.log(api)
|
||||
// http.post("/node/monitorApi/addOrUpdate", api)
|
||||
static saveLog(item = {}) {
|
||||
const api = {
|
||||
method: item.method,
|
||||
path: item.url,
|
||||
url: location.href,
|
||||
nodeProcess: process.env.NODE_ENV,
|
||||
status: item.status,
|
||||
code: item.response?.code,
|
||||
error: item.response?.code != 0 ? item.response?.data : null,
|
||||
device: navigator.userAgentData.platform
|
||||
}
|
||||
if (!/(sockjs-node|hot-update|monitorApi)/.test(api.path)) {
|
||||
if (!!this.timer) {
|
||||
clearTimeout(this.timer)
|
||||
}
|
||||
})
|
||||
this.pending = [this.pending, api].flat().filter(Boolean)
|
||||
this.timer = setTimeout(() => {
|
||||
http.post("/admin/apiForward", this.pending, {
|
||||
withoutToken: true,
|
||||
params: {url: "http://dvcp.cunwuyun.cn/ns/node/monitorApi/addOrUpdate"}
|
||||
}).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.pending = []
|
||||
}
|
||||
})
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
initXHRObserver() {
|
||||
const origin = XMLHttpRequest.prototype.open;
|
||||
XMLHttpRequest.prototype.open = function (...args) {
|
||||
let send = this.send;
|
||||
let _this = this
|
||||
let post_data = []
|
||||
this.send = function (...data) {
|
||||
post_data = data;
|
||||
return send.apply(_this, data)
|
||||
}
|
||||
this.addEventListener("readystatechange", function () {
|
||||
if (this.readyState === 4) {
|
||||
// 请求后拦截
|
||||
Observer.saveLog({
|
||||
url: args[1],
|
||||
status: this.status,
|
||||
method: args[0],
|
||||
data: post_data,
|
||||
response: JSON.parse(this.response || null)
|
||||
})
|
||||
}
|
||||
}, false)
|
||||
return origin.apply(this, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,11 +149,11 @@ export default {
|
||||
checkAll: v => v.chooseList.length == v.tableData.length && v.tableData !== 0,
|
||||
page() {
|
||||
return {
|
||||
...this.pageConfig,
|
||||
current: this.current,
|
||||
size: this.size,
|
||||
total: this.total,
|
||||
pagerCount: this.pagerCount
|
||||
pagerCount: this.pagerCount,
|
||||
...this.pageConfig
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<section class="ai-card" :class="{panel}">
|
||||
<section class="ai-card" :class="{panel,headerPanel}">
|
||||
<ai-bar v-if="!hideHeader" :title="title" v-bind="$attrs">
|
||||
<template #title>
|
||||
<slot name="title"></slot>
|
||||
@@ -23,7 +23,8 @@ export default {
|
||||
type: String
|
||||
},
|
||||
hideTitle: Boolean,
|
||||
panel: Boolean
|
||||
panel: Boolean,
|
||||
headerPanel: Boolean
|
||||
},
|
||||
computed: {
|
||||
hideHeader: v => v.hideTitle || v.panel
|
||||
@@ -43,12 +44,16 @@ export default {
|
||||
padding: 12px 40px 22px;
|
||||
}
|
||||
|
||||
&.panel {
|
||||
&.panel, &.headerPanel {
|
||||
margin-bottom: 0;
|
||||
|
||||
.ai-card__body {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.aibar {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
colors () {
|
||||
colors() {
|
||||
if (this.theme === '0') {
|
||||
return ['#2896FF', '#09DBFE', '#61FDB9', '#FFBB69', '#8429FF', '#ea7ccc']
|
||||
}
|
||||
@@ -113,7 +113,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
theme () {
|
||||
theme() {
|
||||
this.refresh()
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user