feat:新增endpoint最新指标过滤功能

This commit is contained in:
wangwenrui
2020-02-11 19:55:20 +08:00
parent cba799f6da
commit 0c84b1f6a4
4 changed files with 268 additions and 7 deletions

View File

@@ -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

View File

@@ -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>

View 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>

View File

@@ -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();