feat:新增endpoint最新指标过滤功能
This commit is contained in:
@@ -304,7 +304,7 @@ const en = {
|
|||||||
device: 'SN',//SN
|
device: 'SN',//SN
|
||||||
host: 'Host',//'Host'
|
host: 'Host',//'Host'
|
||||||
assetState: 'State',//'状态'
|
assetState: 'State',//'状态'
|
||||||
assetPing: 'PingStatus',
|
assetPing: 'Ping',
|
||||||
modules: 'Module',//'组件'
|
modules: 'Module',//'组件'
|
||||||
alerts: 'Alert information',//'告警信息'
|
alerts: 'Alert information',//'告警信息'
|
||||||
dataCenter: 'DC',//DC
|
dataCenter: 'DC',//DC
|
||||||
|
|||||||
@@ -90,9 +90,9 @@
|
|||||||
<div v-if="item.prop == 'pingStatus'">
|
<div v-if="item.prop == 'pingStatus'">
|
||||||
<el-popover
|
<el-popover
|
||||||
placement="right"
|
placement="right"
|
||||||
width="200"
|
width="50"
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
:content="scope.row.pingLastUpdate+'['+(scope.row.pingRtt ? scope.row.pingRtt:'unreachable')+']'">
|
:content="scope.row.pingRtt ? scope.row.pingRtt+'ms':'unreachable'">
|
||||||
<div slot="reference" style="width: 20px">
|
<div slot="reference" style="width: 20px">
|
||||||
<div :class="{'active-icon green':scope.row.pingState == 1,'active-icon red':scope.row.pingState == 0}"></div>
|
<div :class="{'active-icon green':scope.row.pingState == 1,'active-icon red':scope.row.pingState == 0}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
177
nezha-fronted/src/components/page/project/metricSearch.vue
Normal file
177
nezha-fronted/src/components/page/project/metricSearch.vue
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="componetStyle">
|
||||||
|
<el-popover placement="bottom"
|
||||||
|
width="200"
|
||||||
|
v-model="historyShow"
|
||||||
|
>
|
||||||
|
<ul>
|
||||||
|
<li v-for="(item,index) in queryHistory" class="history-li" @click="pickHistory(item)">{{item}}</li>
|
||||||
|
</ul>
|
||||||
|
<div slot="reference" @click.prevent.stop="" class="search-input-history" ><i class="el-icon-time"/></div>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
|
<div class="search-input">
|
||||||
|
<el-autocomplete
|
||||||
|
id="query-expression"
|
||||||
|
class="inline-input"
|
||||||
|
v-model="queryExpression"
|
||||||
|
size="mini"
|
||||||
|
width="300"
|
||||||
|
:fetch-suggestions="querySearch"
|
||||||
|
:placeholder="$t('project.endpoint.promExpr')"
|
||||||
|
@select="handleSelect"
|
||||||
|
ref="queryExpression"
|
||||||
|
>
|
||||||
|
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
||||||
|
<template slot-scope="{ item }">
|
||||||
|
<span>{{ item.key }}<!--:{{item.type}}--></span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete>
|
||||||
|
<button class="nz-btn nz-btn-size-normal nz-btn-style-light" @click="filterTabelData">GO</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import en from "../../common/language/en";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "metricSearch",
|
||||||
|
props:{
|
||||||
|
metrics:{type:Array},
|
||||||
|
labels:{type:Array},
|
||||||
|
componetStyle:{type:String}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
},
|
||||||
|
data:function(){
|
||||||
|
return {
|
||||||
|
queryExpression:'',//输入框显示的表达式
|
||||||
|
saveExpression:'',
|
||||||
|
filter:null, //filter
|
||||||
|
metricSet:false,//是否设置了metric
|
||||||
|
labelSet:false,//是否设置了label
|
||||||
|
suggestionArray:[], //提示下拉选的数组
|
||||||
|
inputElment:null,//输入框的html元素,
|
||||||
|
caretIndex:-1, //选中后光标的位置
|
||||||
|
localStorageHistoryKey:'endpoint_query_history',
|
||||||
|
queryHistory:[],
|
||||||
|
historyShow:false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
querySearch(queryString, cb) {
|
||||||
|
// 调用 callback 返回建议列表的数据
|
||||||
|
let results=queryString?this.suggestionArray.filter(this.filter(queryString)):this.suggestionArray;
|
||||||
|
cb(results)
|
||||||
|
},
|
||||||
|
handleSelect(item) {
|
||||||
|
let temp=this;
|
||||||
|
console.log(temp.queryExpression)
|
||||||
|
if(item.type==='metric'){
|
||||||
|
temp.queryExpression=item.key+'{}';
|
||||||
|
temp.saveExpression=temp.queryExpression;
|
||||||
|
temp.filter=this.labelFilter;
|
||||||
|
temp.metricSet=true;
|
||||||
|
temp.suggestionArray=temp.labels;
|
||||||
|
this.foucsAndMoveCaret();
|
||||||
|
}else if(item.type==='label' && this.metricSet){
|
||||||
|
temp.queryExpression=temp.saveExpression.substr(0,temp.caretIndex)+item.key+'='+temp.saveExpression.substr(temp.caretIndex,temp.saveExpression.length);
|
||||||
|
temp.saveExpression=temp.queryExpression;
|
||||||
|
temp.suggestionArray=[];
|
||||||
|
temp.foucsAndMoveCaret();
|
||||||
|
}else if(item.type==='label' && !this.metricSet && !temp.labelSet){
|
||||||
|
temp.queryExpression="{"+item.key+"=}";
|
||||||
|
temp.saveExpression=temp.queryExpression;
|
||||||
|
temp.suggestionArray=[];
|
||||||
|
temp.labelSet=true;
|
||||||
|
temp.foucsAndMoveCaret();
|
||||||
|
}else if(item.type=='label'&& !this.metricSet && temp.labelSet){
|
||||||
|
temp.queryExpression=temp.saveExpression.substr(0,temp.caretIndex)+item.key+'='+temp.saveExpression.substr(temp.caretIndex,temp.saveExpression.length);
|
||||||
|
temp.saveExpression=temp.queryExpression;
|
||||||
|
temp.suggestionArray=[];
|
||||||
|
temp.foucsAndMoveCaret();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initFilter(queryString) {
|
||||||
|
return (suggestion) => {
|
||||||
|
return (suggestion.key.toLowerCase().indexOf(queryString.toLowerCase()) != -1);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
labelFilter(queryString){
|
||||||
|
let caretPos = this.inputElment.selectionEnd;
|
||||||
|
queryString=queryString.substr(this.caretIndex,caretPos-this.caretIndex);
|
||||||
|
return (suggestion) => {
|
||||||
|
return (suggestion.key.toLowerCase().indexOf(queryString.toLowerCase()) != -1);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
foucsAndMoveCaret(){
|
||||||
|
let temp=this;
|
||||||
|
temp.$refs.queryExpression.focus();
|
||||||
|
setTimeout(function(){
|
||||||
|
temp.caretIndex=temp.queryExpression.length-1
|
||||||
|
temp.inputElment.setSelectionRange(temp.caretIndex,temp.caretIndex);
|
||||||
|
},100);
|
||||||
|
},
|
||||||
|
filterTabelData:function(){
|
||||||
|
this.$emit('expression-change',this.queryExpression);
|
||||||
|
this.queryHistory.unshift(this.queryExpression);
|
||||||
|
localStorage.setItem(this.localStorageHistoryKey,JSON.stringify(this.queryHistory));
|
||||||
|
},
|
||||||
|
pickHistory:function(expression){
|
||||||
|
this.queryExpression=expression;
|
||||||
|
this.filterTabelData();
|
||||||
|
this.historyShow=false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.filter=this.initFilter;
|
||||||
|
this.suggestionArray=this.labels.concat(this.metrics);
|
||||||
|
this.inputElment=document.getElementById('query-expression');
|
||||||
|
this.queryHistory=JSON.parse(localStorage.getItem(this.localStorageHistoryKey))?JSON.parse(localStorage.getItem(this.localStorageHistoryKey)):[];
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
queryExpression:function(n,o){
|
||||||
|
if(o.length<n.length){
|
||||||
|
if(n&&/\w*\{.*\}.*/i.test(n)){
|
||||||
|
let caretPos=this.inputElment.selectionEnd;
|
||||||
|
console.log('caretPos:'+caretPos+" lastOf:"+n.lastIndexOf('}'))
|
||||||
|
if(caretPos <= n.lastIndexOf('}')&&n.substr(caretPos-1,1)==','){
|
||||||
|
this.suggestionArray=this.labels;
|
||||||
|
this.saveExpression=n;
|
||||||
|
this.filter=this.labelFilter;
|
||||||
|
this.foucsAndMoveCaret();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
this.saveExpression=n;
|
||||||
|
this.caretIndex=this.inputElment.selectionEnd;
|
||||||
|
if(n.length==0){
|
||||||
|
this.filter=this.initFilter;
|
||||||
|
this.suggestionArray=this.labels.concat(this.metrics);
|
||||||
|
}
|
||||||
|
if(n.length>0 && n.indexOf('{')==-1){
|
||||||
|
this.filter=this.initFilter;
|
||||||
|
this.suggestionArray=this.metrics;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search-input-history{
|
||||||
|
display: inline-block;
|
||||||
|
border:1px solid #e5e5e5;
|
||||||
|
border-radius: 10%;
|
||||||
|
}
|
||||||
|
.search-input{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.history-li:hover{
|
||||||
|
background-color: #e5e5e5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -4,6 +4,9 @@
|
|||||||
height: 26px !important;
|
height: 26px !important;
|
||||||
border-color: #d8d8d8;
|
border-color: #d8d8d8;
|
||||||
}
|
}
|
||||||
|
.project .nz-table .el-table__row td:first-of-type {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<style scope>
|
<style scope>
|
||||||
.chart-bottom {
|
.chart-bottom {
|
||||||
@@ -169,6 +172,7 @@
|
|||||||
>
|
>
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
<button @click="changeTime(10)" class="nz-btn nz-btn-size-normal nz-btn-style-light margin-r-20"><i class="el-icon-d-arrow-right"></i></button>
|
<button @click="changeTime(10)" class="nz-btn nz-btn-size-normal nz-btn-style-light margin-r-20"><i class="el-icon-d-arrow-right"></i></button>
|
||||||
|
<metric-search :metrics="metricList" :labels="labelList" v-if="tableShow == 3" @expression-change="tableFilter"></metric-search>
|
||||||
<button @click="viewGraph" slot="reference" class="nz-btn nz-btn-size-normal nz-btn-style-normal nz-btn-min-width-120">
|
<button @click="viewGraph" slot="reference" class="nz-btn nz-btn-size-normal nz-btn-style-normal nz-btn-min-width-120">
|
||||||
<span class='top-tool-btn-txt'>{{$t('project.endpoint.addGraph')}}</span>
|
<span class='top-tool-btn-txt'>{{$t('project.endpoint.addGraph')}}</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -176,7 +180,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-table
|
<el-table
|
||||||
v-loading="queryEdpLoading"
|
v-loading="queryEdpLoading"
|
||||||
:data="endpointQueryTabData"
|
:data="showTableData"
|
||||||
border
|
border
|
||||||
class="nz-table"
|
class="nz-table"
|
||||||
:header-cell-class-name="cellClass"
|
:header-cell-class-name="cellClass"
|
||||||
@@ -253,11 +257,12 @@
|
|||||||
import echarts from 'echarts';
|
import echarts from 'echarts';
|
||||||
import chartBox from "../dashboard/chartBox";
|
import chartBox from "../dashboard/chartBox";
|
||||||
import bus from "../../../libs/bus";
|
import bus from "../../../libs/bus";
|
||||||
|
import metricSearch from "./metricSearch";
|
||||||
export default {
|
export default {
|
||||||
name: "project2",
|
name: "project2",
|
||||||
components: {
|
components: {
|
||||||
'chart-box': chartBox
|
'chart-box': chartBox,
|
||||||
|
'metric-search':metricSearch
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
let temp=this;
|
let temp=this;
|
||||||
@@ -372,8 +377,11 @@
|
|||||||
viewAssetState:false,
|
viewAssetState:false,
|
||||||
curEndpoint:null,
|
curEndpoint:null,
|
||||||
endpointQueryTabData:[],//endpoint 查询列表数据
|
endpointQueryTabData:[],//endpoint 查询列表数据
|
||||||
|
showTableData:[],
|
||||||
queryExpression:'',
|
queryExpression:'',
|
||||||
metricList: [], // metric列表
|
metricList: [], // metric列表
|
||||||
|
labelSet:new Set(),
|
||||||
|
labelList:[],
|
||||||
formatTime:'',//查询endpoint的时间,
|
formatTime:'',//查询endpoint的时间,
|
||||||
selectedEndpoints:[],//选中的metric{label='value'}
|
selectedEndpoints:[],//选中的metric{label='value'}
|
||||||
chartDatas:[],//从query_range查询到的数据
|
chartDatas:[],//从query_range查询到的数据
|
||||||
@@ -891,9 +899,11 @@
|
|||||||
this.formatTime='';
|
this.formatTime='';
|
||||||
}
|
}
|
||||||
this.endpointQueryTabData=[];
|
this.endpointQueryTabData=[];
|
||||||
|
this.showTableData=[];
|
||||||
this.$get("/prom/api/v1/query?query={job='ed_"+this.curEndpoint.id+"'}&time="+this.formatTime).then(response=>{
|
this.$get("/prom/api/v1/query?query={job='ed_"+this.curEndpoint.id+"'}&time="+this.formatTime).then(response=>{
|
||||||
if(response.status==="success"){
|
if(response.status==="success"){
|
||||||
let results=response.data.result;
|
let results=response.data.result;
|
||||||
|
this.endpointQueryTabData=JSON.parse(JSON.stringify(results));
|
||||||
for (let result of results){
|
for (let result of results){
|
||||||
// {"metric":{"instance":"192.168.40.126:9100","__name__":"scrape_duration_seconds","module":"node_exporter","project":"kafka","asset":"192.168.40.126","job":"ed_1","dc":"dc5"},"value":[1580782176.522,"0.000560761"]}
|
// {"metric":{"instance":"192.168.40.126:9100","__name__":"scrape_duration_seconds","module":"node_exporter","project":"kafka","asset":"192.168.40.126","job":"ed_1","dc":"dc5"},"value":[1580782176.522,"0.000560761"]}
|
||||||
let temp=result.metric.__name__;
|
let temp=result.metric.__name__;
|
||||||
@@ -901,12 +911,17 @@
|
|||||||
temp+="{";
|
temp+="{";
|
||||||
for (let key in result.metric){
|
for (let key in result.metric){
|
||||||
temp+=key +"='"+result.metric[key]+"',";
|
temp+=key +"='"+result.metric[key]+"',";
|
||||||
|
this.labelSet.add(key);
|
||||||
}
|
}
|
||||||
temp=temp.charAt(temp.length-1) == ","?temp.substr(0,temp.length-1):temp;
|
temp=temp.charAt(temp.length-1) == ","?temp.substr(0,temp.length-1):temp;
|
||||||
temp+="}";
|
temp+="}";
|
||||||
let edpQueryData={element:temp,value:result.value[1],type:(result.metric.type?result.metric.type:'2')};
|
let edpQueryData={element:temp,value:result.value[1],type:(result.metric.type?result.metric.type:'2')};
|
||||||
this.endpointQueryTabData.push(edpQueryData);
|
this.showTableData.push(edpQueryData);
|
||||||
|
this.labelList=Array.from(this.labelSet).map((item,index)=>{
|
||||||
|
return {key:item,type:'label'}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tableShow=3;
|
this.tableShow=3;
|
||||||
this.queryEdpLoading=false;
|
this.queryEdpLoading=false;
|
||||||
}
|
}
|
||||||
@@ -1062,6 +1077,20 @@
|
|||||||
return 'disabledCheck'
|
return 'disabledCheck'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 获取metric列表
|
||||||
|
getSuggestMetric() {
|
||||||
|
this.$get('metric', {pageNo: 1, pageSize: -1}).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.metricList = response.data.list.map((item,index)=>{
|
||||||
|
item.__label__='metric';
|
||||||
|
return {key:item.metric,type:'metric'};
|
||||||
|
});
|
||||||
|
}else {
|
||||||
|
this.metricList = [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
getPanelData() { //获取panel数据
|
getPanelData() { //获取panel数据
|
||||||
this.$get('panel?pageNo=1&pageSize=-1').then(response => {
|
this.$get('panel?pageNo=1&pageSize=-1').then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
@@ -1069,11 +1098,66 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
tableFilter:function(expression){
|
||||||
|
let metric='';
|
||||||
|
let labels=[];
|
||||||
|
if(/\w*\{.*\}.*/i.test(expression)){
|
||||||
|
metric=expression.substr(0,expression.indexOf('{'));
|
||||||
|
let labelStr=expression.substr(expression.indexOf('{')+1,expression.indexOf('}')-expression.indexOf('{')-1);
|
||||||
|
let labelArr=labelStr.split(',');
|
||||||
|
labels=labelArr.map((item,index)=>{
|
||||||
|
let temp=item.split('=');
|
||||||
|
let label=temp[0]&&temp[0]!=''?temp[0]:null;
|
||||||
|
let value=temp[1]&&temp[1]!=''?temp[1]:null;
|
||||||
|
return label?{label:label,value:value}:null;
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
metric=expression;
|
||||||
|
}
|
||||||
|
this.showTableData=[];
|
||||||
|
let sourceData=JSON.parse(JSON.stringify(this.endpointQueryTabData))
|
||||||
|
sourceData=sourceData.filter((item)=>{
|
||||||
|
let metricName=item.metric.__name__;
|
||||||
|
if(metricName.indexOf(metric)==-1){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(let i in labels){
|
||||||
|
let label=labels[i];
|
||||||
|
let value=item.metric[label.label];
|
||||||
|
if(!value || value != label.value){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
for(let i in sourceData){
|
||||||
|
let item=sourceData[i];
|
||||||
|
// {"metric":{"instance":"192.168.40.126:9100","__name__":"scrape_duration_seconds","module":"node_exporter","project":"kafka","asset":"192.168.40.126","job":"ed_1","dc":"dc5"},"value":[1580782176.522,"0.000560761"]}
|
||||||
|
let metricName=item.metric.__name__;
|
||||||
|
let temp=metricName;
|
||||||
|
delete item.metric.__name__;
|
||||||
|
temp+="{";
|
||||||
|
let hasLabel=true;
|
||||||
|
for (let key in item.metric){
|
||||||
|
let label=key;
|
||||||
|
let value=item.metric[label];
|
||||||
|
temp+=label +"='"+value+"',";
|
||||||
|
}
|
||||||
|
temp=temp.charAt(temp.length-1) == ","?temp.substr(0,temp.length-1):temp;
|
||||||
|
temp+="}";
|
||||||
|
if(hasLabel){
|
||||||
|
let edpQueryData={element:temp,value:item.value[1],type:(item.metric.type?item.metric.type:'2')};
|
||||||
|
this.showTableData.push(edpQueryData);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.currentProject = this.$store.state.currentProject;
|
this.currentProject = this.$store.state.currentProject;
|
||||||
this.getModuleList();
|
this.getModuleList();
|
||||||
this.getMetricsTableData();
|
this.getMetricsTableData();
|
||||||
|
this.getSuggestMetric()
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getPanelData();
|
this.getPanelData();
|
||||||
|
|||||||
Reference in New Issue
Block a user