diff --git a/nezha-fronted/src/components/charts/chart-alert-list.vue b/nezha-fronted/src/components/charts/chart-alert-list.vue index 0b2088e9b..768834a2e 100644 --- a/nezha-fronted/src/components/charts/chart-alert-list.vue +++ b/nezha-fronted/src/components/charts/chart-alert-list.vue @@ -60,12 +60,13 @@ :id="'tableContainer'+chartIndex" :tableData="storedTableData" :loading="loadingTable" - :height="'calc(100% - 0px)'" + :height="'calc(100% - 46px)'" :customTableTitle="tableTitle" :tableId="''" :projectAlertId="'tableContainer'+chartIndex" @tableDataSort="tableDataSort" @del="deleteMessage" + @showText="showText" @messageDetail="messageDetail" ref="alertListTable" :form="'chartList'" @@ -88,7 +89,7 @@ :id="'tableContainer'+chartIndex" :tableData="storedScreanTableData" :loading="loadingTable" - :height="'calc(100% - 0px)'" + :height="'calc(100% - 20px)'" :customTableTitle="tableTitle" :tableId="''" :projectAlertId="'tableContainer'+chartIndex" @@ -129,10 +130,28 @@
{{$t("project.endpoint.dialogTitle")}}
- + +
+
+
+ + +
+ + + +
+ {{$t('alert.config.trbShot')}} +
+
+
+ +
+
-
@@ -296,7 +315,12 @@ export default { chartUnit: 5, deleteBox: { show: false, ids: '', remark: '', state: 2 }, isPreview: false, - ps: null + ps: null, + resultType: '', + logData: [], + chartLoading: false, + dialogShowText: false, + dialogText: '' } }, computed: { @@ -389,7 +413,7 @@ export default { this.currentMsg = obj this.chartUnit = obj.alertRule.unit ? obj.alertRule.unit : 5 this.$nextTick(() => { - this.queryChartDate() + this.queryDate() }) }, dialogClose () { @@ -433,7 +457,7 @@ export default { chartUnitChange (unit) { this.chartUnit = unit this.$nextTick(() => { - this.queryChartDate() + this.queryDate() }) }, messageDetail (row) { @@ -441,7 +465,7 @@ export default { this.currentMsg = { ...row, alertRule: { ...res.data } } this.graphShow = true this.$nextTick(() => { - this.queryChartDate() + this.queryDate() }) }) }, @@ -471,6 +495,7 @@ export default { this.legend = [] this.chartDatas = [] axios.all(axiosArr).then(res => { + this.chartLoading = false try { res.forEach((response, promIndex) => { if (response.status == 200) { @@ -891,7 +916,168 @@ export default { this.$set(this.searchLabel, 'orderBy', orderBy) this.getAlertList() }, - + queryDate () { + this.chartLoading = true + if (this.currentMsg.alertRule.type === 1) { + this.resultType = 'matrix' + this.$nextTick(() => { + this.queryChartDate() + }) + } else if (this.currentMsg.alertRule.type === 2) { + this.queryLogData(1000) + } + }, + exportLog ({ limit, descending }) { + const start = this.searchTimeDialog[0] ? this.searchTimeDialog[0] : bus.computeTimezoneTime(new Date().getTime() - 1 * 60 * 60 * 1000) + const end = this.searchTimeDialog[1] ? this.searchTimeDialog[1] : bus.computeTimezoneTime(new Date().getTime()) + const params = { + logql: this.expressions, + start: start, + end: end, + direction: descending ? 'backward' : 'forward', + limit + } + axios.get('/logs/loki/export', { responseType: 'blob', params: params }).then(res => { + if (window.navigator.msSaveOrOpenBlob) { + // 兼容ie11 + const blobObject = new Blob([res.data]) + window.navigator.msSaveOrOpenBlob(blobObject, 'log') + } else { + const url = URL.createObjectURL(new Blob([res.data])) + const a = document.createElement('a') + document.body.appendChild(a) // 此处增加了将创建的添加到body当中 + a.href = url + a.download = 'log' + a.target = '_blank' + a.click() + a.remove() // 将a标签移除 + } + }, error => { + const $self = this + const reader = new FileReader() + reader.onload = function (event) { + const responseText = reader.result + const exception = JSON.parse(responseText) + if (exception.message) { + $self.$message.error(exception.message) + } else { + console.error(error) + } + } + reader.readAsText(error.response.data) + }) + }, + queryLogData (limit) { // log的chart和table是一个请求 + if (!limit) { + limit = 1000 + } + const start = this.searchTimeDialog[0] ? this.searchTimeDialog[0] : bus.computeTimezoneTime(new Date().getTime() - 1 * 60 * 60 * 1000) + const end = this.searchTimeDialog[1] ? this.searchTimeDialog[1] : bus.computeTimezoneTime(new Date().getTime()) + this.expressions = [this.currentMsg.alertRule.expr] + this.$get('/logs/loki/api/v1/query_range?format=1&query=' + this.currentMsg.alertRule.expr + '&start=' + this.$stringTimeParseToUnix(start) + '&end=' + this.$stringTimeParseToUnix(end) + '&limit=' + limit).then(res => { + this.chartLoading = false + const logData = [res.data] + this.resultType = res.data.resultType + this.$nextTick(() => { + if (this.$refs.logDetail) { + this.$refs.logDetail.time = this.chartData.param.time + this.$refs.logDetail.wrapLines = this.chartData.param.wrapLines + this.$refs.logDetail.operations.descending = this.chartData.param.descending + } + // logData.forEach((item, index) => { + // item.result.forEach(result => { + // result.elements = this.expressions[index] + // }) + // }) + this.logData = logData + this.resultType === 'matrix' && this.loadLogGraph() + }) + }) + }, + loadLogGraph () { + const graphData = this.logData.filter(l => l.resultType === 'matrix') + if (graphData && graphData.length > 0) { + this.$refs.messageChart.startLoading() + const queryExpression = [] + let series = [] + const legend = [] + this.expressions.forEach((item, index) => { + if (item !== '') { + queryExpression.push(item) + } + }) + this.logData.forEach((response, index) => { + if (response.resultType === 'matrix') { + const data = response.result + if (!data || data.length < 1) { + return + } + data.forEach((result, i) => { + const seriesItem = { + name: '', + symbol: 'emptyCircle', // 去掉点 + symbolSize: [2, 2], + showSymbol: false, + smooth: 0.2, // 曲线变平滑 + data: [], + lineStyle: { + width: 1, + opacity: 0.9 + }, + type: 'line' + } + seriesItem.data = result.values.map((item) => { + return [item[0] * 1000, item[1]] + }) + let host = ''// up, + let alias = '' + if (result.metric && Object.keys(result.metric).length > 0) { + const metric = Object.keys(result.metric) + if (metric.__name__) { + host = `${metric.__name__}{`// up, + } + metric.forEach((tag, i) => { + if (tag !== '__name__') { + host += `${tag}="${result.metric[tag]}",` + } + }) + if (host.endsWith(',')) { + host = host.substr(0, host.length - 1) + } + if (metric.__name__) { + host += '}' + } + // 处理legend别名 + // alias = this.dealLegendAlias(host, this.chartData.elements[index].legend) + if (!alias || alias === '') { + alias = host + } + } else { + alias = queryExpression[index] + } + seriesItem.name = alias + '-' + index + series.push(seriesItem) + legend.push({ name: seriesItem.name, alias: alias, isGray: false }) + }) + } + }) + this.$refs.messageChart.setLegend(legend) + this.$refs.messageChart.setRandomColors(series.length) + if (!series.length) { + series = '' + } + this.$refs.messageChart.setSeries(series) + this.defaultChartVisible = true + this.$nextTick(() => { + this.$refs.messageChart.endLoading() + this.$refs.messageChart.resize() + }) + } + }, + showText (row) { + this.dialogShowText = true + this.dialogText = row.alertRule.trbShot + } }, created () { // 是否存在分页缓存 diff --git a/nezha-fronted/src/components/common/bottomBox/tabs/alertMessageTabNew.vue b/nezha-fronted/src/components/common/bottomBox/tabs/alertMessageTabNew.vue index 53e9237d5..4f6da10ff 100644 --- a/nezha-fronted/src/components/common/bottomBox/tabs/alertMessageTabNew.vue +++ b/nezha-fronted/src/components/common/bottomBox/tabs/alertMessageTabNew.vue @@ -54,10 +54,13 @@
{{$t("project.endpoint.dialogTitle")}}
- +
- +
+ + +
@@ -177,7 +180,12 @@ export default { chartUnit: 5, requestIndex: 0, viewAssetState: false, - nowTime: '' + nowTime: '', + resultType: '', + logData: [], + chartLoading: false, + dialogShowText: false, + dialogText: '' } }, methods: { @@ -199,7 +207,7 @@ export default { chartUnitChange (unit) { this.chartUnit = unit this.$nextTick(() => { - this.queryChartDate() + this.queryDate() }) }, queryChartDate () { @@ -228,6 +236,7 @@ export default { this.legend = [] this.chartDatas = [] axios.all(axiosArr).then(res => { + this.chartLoading = false try { res.forEach((response, promIndex) => { if (response.status === 200) { @@ -430,7 +439,7 @@ export default { this.graphShow = true this.$nextTick(() => { this.searchTime = [bus.computeTimezoneTime(new Date().getTime() - 1 * 60 * 60 * 1000), bus.computeTimezoneTime(new Date().getTime())] - this.queryChartDate() + this.queryDate() }) }) }, @@ -613,6 +622,168 @@ export default { return [{}, { yAxis: this.currentMsg.alertRule.threshold }] } } + }, + queryDate () { + this.chartLoading = true + if (this.currentMsg.alertRule.type === 1) { + this.resultType = 'matrix' + this.$nextTick(() => { + this.queryChartDate() + }) + } else if (this.currentMsg.alertRule.type === 2) { + this.queryLogData(1000) + } + }, + exportLog ({ limit, descending }) { + const start = this.searchTimeDialog[0] ? this.searchTimeDialog[0] : bus.computeTimezoneTime(new Date().getTime() - 1 * 60 * 60 * 1000) + const end = this.searchTimeDialog[1] ? this.searchTimeDialog[1] : bus.computeTimezoneTime(new Date().getTime()) + const params = { + logql: this.expressions, + start: start, + end: end, + direction: descending ? 'backward' : 'forward', + limit + } + axios.get('/logs/loki/export', { responseType: 'blob', params: params }).then(res => { + if (window.navigator.msSaveOrOpenBlob) { + // 兼容ie11 + const blobObject = new Blob([res.data]) + window.navigator.msSaveOrOpenBlob(blobObject, 'log') + } else { + const url = URL.createObjectURL(new Blob([res.data])) + const a = document.createElement('a') + document.body.appendChild(a) // 此处增加了将创建的添加到body当中 + a.href = url + a.download = 'log' + a.target = '_blank' + a.click() + a.remove() // 将a标签移除 + } + }, error => { + const $self = this + const reader = new FileReader() + reader.onload = function (event) { + const responseText = reader.result + const exception = JSON.parse(responseText) + if (exception.message) { + $self.$message.error(exception.message) + } else { + console.error(error) + } + } + reader.readAsText(error.response.data) + }) + }, + queryLogData (limit) { // log的chart和table是一个请求 + if (!limit) { + limit = 1000 + } + const start = this.searchTimeDialog[0] ? this.searchTimeDialog[0] : bus.computeTimezoneTime(new Date().getTime() - 1 * 60 * 60 * 1000) + const end = this.searchTimeDialog[1] ? this.searchTimeDialog[1] : bus.computeTimezoneTime(new Date().getTime()) + this.expressions = [this.currentMsg.alertRule.expr] + this.$get('/logs/loki/api/v1/query_range?format=1&query=' + this.currentMsg.alertRule.expr + '&start=' + this.$stringTimeParseToUnix(start) + '&end=' + this.$stringTimeParseToUnix(end) + '&limit=' + limit).then(res => { + this.chartLoading = false + const logData = [res.data] + this.resultType = res.data.resultType + this.$nextTick(() => { + if (this.$refs.logDetail) { + this.$refs.logDetail.time = this.chartData.param.time + this.$refs.logDetail.wrapLines = this.chartData.param.wrapLines + this.$refs.logDetail.operations.descending = this.chartData.param.descending + } + // logData.forEach((item, index) => { + // item.result.forEach(result => { + // result.elements = this.expressions[index] + // }) + // }) + this.logData = logData + this.resultType === 'matrix' && this.loadLogGraph() + }) + }) + }, + loadLogGraph () { + const graphData = this.logData.filter(l => l.resultType === 'matrix') + if (graphData && graphData.length > 0) { + this.$refs.messageChart.startLoading() + const queryExpression = [] + let series = [] + const legend = [] + this.expressions.forEach((item, index) => { + if (item !== '') { + queryExpression.push(item) + } + }) + this.logData.forEach((response, index) => { + if (response.resultType === 'matrix') { + const data = response.result + if (!data || data.length < 1) { + return + } + data.forEach((result, i) => { + const seriesItem = { + name: '', + symbol: 'emptyCircle', // 去掉点 + symbolSize: [2, 2], + showSymbol: false, + smooth: 0.2, // 曲线变平滑 + data: [], + lineStyle: { + width: 1, + opacity: 0.9 + }, + type: 'line' + } + seriesItem.data = result.values.map((item) => { + return [item[0] * 1000, item[1]] + }) + let host = ''// up, + let alias = '' + if (result.metric && Object.keys(result.metric).length > 0) { + const metric = Object.keys(result.metric) + if (metric.__name__) { + host = `${metric.__name__}{`// up, + } + metric.forEach((tag, i) => { + if (tag !== '__name__') { + host += `${tag}="${result.metric[tag]}",` + } + }) + if (host.endsWith(',')) { + host = host.substr(0, host.length - 1) + } + if (metric.__name__) { + host += '}' + } + // 处理legend别名 + // alias = this.dealLegendAlias(host, this.chartData.elements[index].legend) + if (!alias || alias === '') { + alias = host + } + } else { + alias = queryExpression[index] + } + seriesItem.name = alias + '-' + index + series.push(seriesItem) + legend.push({ name: seriesItem.name, alias: alias, isGray: false }) + }) + } + }) + this.$refs.messageChart.setLegend(legend) + this.$refs.messageChart.setRandomColors(series.length) + if (!series.length) { + series = '' + } + this.$refs.messageChart.setSeries(series) + this.defaultChartVisible = true + this.$nextTick(() => { + this.$refs.messageChart.endLoading() + this.$refs.messageChart.resize() + }) + } + }, + showText (row) { + this.dialogShowText = true + this.dialogText = row.alertRule.trbShot } } } diff --git a/nezha-fronted/src/components/common/rightBox/alertRuleBox.vue b/nezha-fronted/src/components/common/rightBox/alertRuleBox.vue index 5529e3e2c..31cf28fb5 100644 --- a/nezha-fronted/src/components/common/rightBox/alertRuleBox.vue +++ b/nezha-fronted/src/components/common/rightBox/alertRuleBox.vue @@ -602,6 +602,9 @@ export default { } }, selectAlertRuleMetric (val) { + if (!this.editAlertRule.operator) { + this.editAlertRule.operator = '>' + } if (val === 1) { this.showSnmpTrap = true // showSnmpTrap 为 true 时显示 expr,threshold,unit this.$refs.alertRuleForm.clearValidate('expr') // 移除from表单的 expr 验证 @@ -612,6 +615,10 @@ export default { } else if (val === 3) { this.showSnmpTrap = false // showSnmpTrap 为 false 时,展示 OID this.editAlertRule.inr = '' + this.editAlertRule.last = '' + this.editAlertRule.unit = 0 + this.editAlertRule.operator = '' + this.editAlertRule.threshold = '' } }, afterInitRich () {