Feat instance dependency (#233)
* feat: add popup for instance dependency
* feat: click dependency instance
* refactor: add detect point component
* feat: add instance detect point
* feat: add instance dependency query for metric
* feat: update Instance Dependency
* feat: add modes
* feat: add dependency sankey
* fix: rm dead code
* fix: dialog rerender
* feat: update modules
* feat: update cpm metric
diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts
index af107f5..96fae7d 100644
--- a/src/assets/lang/en.ts
+++ b/src/assets/lang/en.ts
@@ -104,6 +104,8 @@
nextService: 'Next Service',
object: 'Object',
metrics: 'Metrics',
+ ShowInstanceDependency: 'Show Instance Dependency',
+ InstanceDependencyTitle: 'Service Instance Dependency',
};
export default m;
diff --git a/src/assets/lang/zh.ts b/src/assets/lang/zh.ts
index 91a64fd..5b455ca 100644
--- a/src/assets/lang/zh.ts
+++ b/src/assets/lang/zh.ts
@@ -104,6 +104,8 @@
nextService: '下一个服务',
object: '粒度',
metrics: '指标',
+ ShowInstanceDependency: '展示实例依赖',
+ InstanceDependencyTitle: '实例依赖',
};
export default m;
diff --git a/src/components/rk-echarts.vue b/src/components/rk-echarts.vue
index c7daecf..74f7b13 100644
--- a/src/components/rk-echarts.vue
+++ b/src/components/rk-echarts.vue
@@ -27,6 +27,7 @@
@Component
export default class RkEcharts extends Vue {
@Prop() private option: any;
+ @Prop() private clickEvent: any;
@Prop({ default: false }) private uncombine!: boolean;
@Prop({ default: '100%' }) private height!: string;
@Prop({default: '100%' }) private width!: string;
@@ -59,6 +60,9 @@
const el: any = this.$el;
this.myChart = echarts.init(el, '');
this.myChart.setOption(this.option);
+ this.myChart.on('click', (params: any) => {
+ this.clickEvent(params);
+ });
}
}
</script>
diff --git a/src/event-bus.ts b/src/event-bus.ts
index a8e0068..99a098d 100644
--- a/src/event-bus.ts
+++ b/src/event-bus.ts
@@ -1,3 +1,20 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
import Vue, { VueConstructor } from 'vue';
type VueComponentVM = Vue & { _uid: string; };
diff --git a/src/graph/fragments/topology.ts b/src/graph/fragments/topology.ts
index 0f7b8a6..8a5dd0f 100644
--- a/src/graph/fragments/topology.ts
+++ b/src/graph/fragments/topology.ts
@@ -153,6 +153,166 @@
}
`};
+export const TopoInstanceClientInfo = {
+ variable: '$duration: Duration!, $id: ID!',
+ query: `
+ getResponseTimeTrend: getLinearIntValues(metric: {
+ name: "service_instance_relation_client_resp_time"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ getThroughputTrend: getLinearIntValues(metric: {
+ name: "service_instance_relation_client_cpm"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ getSLATrend: getLinearIntValues(metric: {
+ name: "service_instance_relation_client_call_sla"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p50: getLinearIntValues(metric: {
+ name: "service_instance_relation_client_p50"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p75: getLinearIntValues(metric: {
+ name: "service_instance_relation_client_p75"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p90: getLinearIntValues(metric: {
+ name: "service_instance_relation_client_p90"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p95: getLinearIntValues(metric: {
+ name: "service_instance_relation_client_p95"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p99: getLinearIntValues(metric: {
+ name: "service_instance_relation_client_p99"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+`};
+
+export const TopoInstanceServerInfo = {
+ variable: '$duration: Duration!, $id: ID!',
+ query: `
+ getResponseTimeTrend: getLinearIntValues(metric: {
+ name: "service_instance_relation_server_resp_time"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ getThroughputTrend: getLinearIntValues(metric: {
+ name: "service_instance_relation_server_cpm"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ getSLATrend: getLinearIntValues(metric: {
+ name: "service_instance_relation_server_call_sla"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p50: getLinearIntValues(metric: {
+ name: "service_instance_relation_server_p50"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p75: getLinearIntValues(metric: {
+ name: "service_instance_relation_server_p75"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p90: getLinearIntValues(metric: {
+ name: "service_instance_relation_server_p90"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p95: getLinearIntValues(metric: {
+ name: "service_instance_relation_server_p95"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ p99: getLinearIntValues(metric: {
+ name: "service_instance_relation_server_p99"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+`};
+
+export const TopoInstanceDependency = {
+ variable: '$clientServiceId: ID!, $serverServiceId: ID!, $duration: Duration!',
+ query: `
+ topo: getServiceInstanceTopology(clientServiceId: $clientServiceId,
+ serverServiceId: $serverServiceId, duration: $duration) {
+ nodes {
+ id
+ name
+ type
+ isReal
+ serviceName
+ serviceId
+ }
+ calls {
+ id
+ source
+ detectPoints
+ target
+ }
+ }
+`};
+
export const Topo = {
variable: '$duration: Duration!',
query: `
@@ -261,3 +421,28 @@
value
}
}`};
+
+export const DependencyInstanceServerMetric = {
+ variable: '$duration: Duration!, $idsC: [ID!]!',
+ query: `
+ cpmC: getValues(metric: {
+ name: "service_instance_relation_server_cpm"
+ ids: $idsC
+ }, duration: $duration) {
+ values {
+ id
+ value
+ }
+ }`};
+export const DependencyInstanceClientMetric = {
+ variable: '$duration: Duration!, $idsC: [ID!]!',
+ query: `
+ cpmC: getValues(metric: {
+ name: "service_instance_relation_client_cpm"
+ ids: $idsC
+ }, duration: $duration) {
+ values {
+ id
+ value
+ }
+ }`};
diff --git a/src/graph/query/topology.ts b/src/graph/query/topology.ts
index efce939..0588b70 100644
--- a/src/graph/query/topology.ts
+++ b/src/graph/query/topology.ts
@@ -15,8 +15,9 @@
* limitations under the License.
*/
-import { Topo, ServiceTopo, TopoMetric,
- TopoServiceMetric, TopoClientMetric, TopoServiceInfo, TopoClientInfo } from '../fragments/topology';
+import { Topo, ServiceTopo, TopoMetric, TopoInstanceDependency, TopoInstanceClientInfo, TopoInstanceServerInfo,
+ TopoServiceMetric, TopoClientMetric, TopoServiceInfo, TopoClientInfo,
+ DependencyInstanceServerMetric, DependencyInstanceClientMetric } from '../fragments/topology';
export const queryTopo =
`query queryTopo(${Topo.variable}) {${Topo.query}}`;
@@ -41,3 +42,18 @@
export const queryTopoClientInfo =
`query queryTopoClientInfo(${TopoClientInfo.variable}) {${TopoClientInfo.query}}`;
+
+export const queryTopoInstanceDependency =
+ `query queryTopoInstanceDependency(${TopoInstanceDependency.variable}) {${TopoInstanceDependency.query}}`;
+
+export const queryTopoInstanceServerInfo =
+ `query queryTopoInstanceServerInfo(${TopoInstanceServerInfo.variable}) {${TopoInstanceServerInfo.query}}`;
+
+export const queryTopoInstanceClientInfo =
+ `query queryTopoInstanceClientInfo(${TopoInstanceClientInfo.variable}) {${TopoInstanceClientInfo.query}}`;
+
+export const queryDependencyInstanceServerMetric =
+ `query queryDependencyInstanceServerMetric(${DependencyInstanceServerMetric.variable}) {${DependencyInstanceServerMetric.query}}`;
+
+export const queryDependencyInstanceClientMetric =
+ `query queryDependencyInstanceClientMetric(${DependencyInstanceClientMetric.variable}) {${DependencyInstanceClientMetric.query}}`;
diff --git a/src/store/modules/topology/index.ts b/src/store/modules/topology/index.ts
index 0cf662c..2c9e25d 100644
--- a/src/store/modules/topology/index.ts
+++ b/src/store/modules/topology/index.ts
@@ -19,6 +19,7 @@
import graph from '@/graph';
import * as types from '../../mutation-types';
import { AxiosResponse } from 'axios';
+
interface Option {
key: string;
label: string;
@@ -27,8 +28,10 @@
avgResponseTime: number;
cpm: number;
isAlert: boolean;
- source: string;
- target: string;
+ source: string | any;
+ target: string | any;
+ id: string;
+ detectPoints: string[];
}
interface Node {
apdex: number;
@@ -49,7 +52,7 @@
calls: Call[];
nodes: Node[];
detectPoints: string[];
- selectedCallId: string;
+ selectedServiceCall: Call | null;
currentNode: any;
current: Option;
mode: boolean;
@@ -66,13 +69,20 @@
showTraceDialog: boolean;
showInstancesDialog: boolean;
showEndpointDialog: boolean;
+ instanceDependency: {
+ calls: Call[];
+ nodes: Node[];
+ };
+ selectedInstanceCall: Call | null;
+ instanceDependencyMetrics: {[key: string]: any};
+ queryInstanceMetricsType: string;
}
const initState: State = {
callback: '',
mode: true,
detectPoints: [],
- selectedCallId: '',
+ selectedServiceCall: null,
calls: [],
nodes: [],
currentNode: {},
@@ -93,6 +103,13 @@
showTraceDialog: false,
showInstancesDialog: false,
showEndpointDialog: false,
+ instanceDependency: {
+ calls: [],
+ nodes: [],
+ },
+ selectedInstanceCall: null,
+ instanceDependencyMetrics: {},
+ queryInstanceMetricsType: '',
};
// getters
@@ -135,8 +152,10 @@
state.calls = data.calls;
state.nodes = data.nodes;
},
+ [types.SET_SELECTED_CALL](state: State, data: any) {
+ state.selectedServiceCall = data;
+ },
[types.SET_TOPO_RELATION](state: State, data: any) {
- state.selectedCallId = data.id;
state.getResponseTimeTrend = data.getResponseTimeTrend ?
data.getResponseTimeTrend.values.map((i: any) => i.value) : [];
state.getSLATrend = data.getSLATrend ? data.getSLATrend.values.map((i: any) => i.value) : [];
@@ -147,6 +166,28 @@
state.p95 = data.p95 ? data.p95.values.map((i: any) => i.value) : [];
state.p99 = data.p99 ? data.p99.values.map((i: any) => i.value) : [];
},
+ [types.SET_INSTANCE_DEPENDENCY](state: State, data: any) {
+ state.instanceDependency = data;
+ },
+ [types.SET_SELECTED_INSTANCE_CALL](state: State, data: Call) {
+ state.selectedInstanceCall = data;
+ },
+ [types.SET_INSTANCE_DEPEDENCE_METRICS](state: State, data: any) {
+ state.instanceDependencyMetrics.getResponseTimeTrend = data.getResponseTimeTrend ?
+ data.getResponseTimeTrend.values.map((i: any) => i.value) : [];
+ state.instanceDependencyMetrics.getSLATrend = data.getSLATrend ?
+ data.getSLATrend.values.map((i: any) => i.value) : [];
+ state.instanceDependencyMetrics.getThroughputTrend = data.getThroughputTrend ?
+ data.getThroughputTrend.values.map((i: any) => i.value) : [];
+ state.instanceDependencyMetrics.p50 = data.p50 ? data.p50.values.map((i: any) => i.value) : [];
+ state.instanceDependencyMetrics.p75 = data.p75 ? data.p75.values.map((i: any) => i.value) : [];
+ state.instanceDependencyMetrics.p90 = data.p90 ? data.p90.values.map((i: any) => i.value) : [];
+ state.instanceDependencyMetrics.p95 = data.p95 ? data.p95.values.map((i: any) => i.value) : [];
+ state.instanceDependencyMetrics.p99 = data.p99 ? data.p99.values.map((i: any) => i.value) : [];
+ },
+ [types.SET_INSTANCE_DEPEDENCE_TYPE](state: State, data: string) {
+ state.queryInstanceMetricsType = data;
+ },
};
// actions
@@ -156,13 +197,30 @@
},
CLEAR_TOPO_INFO(context: { commit: Commit; state: State; }) {
context.commit(types.SET_TOPO_RELATION, {});
+ context.commit(types.SET_SELECTED_CALL, null);
+ },
+ GET_INSTANCE_DEPENDENCY_METRICS(
+ context: { commit: Commit; state: State, dispatch: Dispatch, getters: any}, params: any,
+ ) {
+ if (params.mode === 'SERVER') {
+ params.queryType = 'queryTopoInstanceServerInfo';
+ context.dispatch('INSTANCE_RELATION_INFO', params);
+ }
+ if (params.mode === 'CLIENT') {
+ params.queryType = 'queryTopoInstanceClientInfo';
+ context.dispatch('INSTANCE_RELATION_INFO', params);
+ }
},
GET_TOPO_SERVICE_INFO(context: { commit: Commit; state: State; }, params: any) {
return graph
.query('queryTopoServiceInfo')
- .params(params)
+ .params({
+ id: params.id,
+ duration: params.duration,
+ })
.then((res: AxiosResponse) => {
- context.commit('SET_TOPO_RELATION', Object.assign(res.data.data, { id: params.id }));
+ context.commit('SET_TOPO_RELATION', res.data.data);
+ context.commit(types.SET_SELECTED_CALL, params);
});
},
GET_TOPO_CLIENT_INFO(context: { commit: Commit; state: State; }, params: any) {
@@ -170,7 +228,8 @@
.query('queryTopoClientInfo')
.params(params)
.then((res: AxiosResponse) => {
- context.commit('SET_TOPO_RELATION', Object.assign(res.data.data, { id: params.id }));
+ context.commit('SET_TOPO_RELATION', res.data.data);
+ context.commit(types.SET_SELECTED_CALL, params);
});
},
GET_TOPO(context: { commit: Commit; state: State; }, params: any) {
@@ -244,6 +303,77 @@
});
});
},
+ async GET_TOPO_INSTANCE_DEPENDENCY(context: { commit: Commit; state: State; }, params: {
+ clientServiceId: string, serverServiceId: string, duration: string}) {
+
+ graph.query('queryTopoInstanceDependency').params(params)
+ .then((res: AxiosResponse) => {
+ if (!(res.data && res.data.data)) {
+ return;
+ }
+ const serverIdsC = [] as string[];
+ const clientIdsC = [] as string[];
+ const topoCalls = res.data.data.topo.calls;
+ for (const call of topoCalls) {
+ if (call.detectPoints.includes('CLIENT')) {
+ clientIdsC.push(call.id);
+ } else {
+ serverIdsC.push(call.id);
+ }
+ }
+ graph.query('queryDependencyInstanceClientMetric').params({
+ idsC: clientIdsC,
+ duration: params.duration,
+ }).then((json: AxiosResponse) => {
+ const clientCalls = [] as string[];
+ for (const call of topoCalls) {
+ for (const cpm of json.data.data.cpmC.values) {
+ if (cpm.id === call.id) {
+ clientCalls.push({
+ ...call,
+ ...cpm,
+ });
+ }
+ }
+ }
+ graph.query('queryDependencyInstanceServerMetric').params({
+ idsC: serverIdsC,
+ duration: params.duration,
+ }).then((jsonResp: AxiosResponse) => {
+ const serverCalls = [] as string[];
+ for (const call of topoCalls) {
+ for (const cpm of jsonResp.data.data.cpmC.values) {
+ if (cpm.id === call.id) {
+ serverCalls.push({
+ ...call,
+ ...cpm,
+ });
+ }
+ }
+ }
+ const data = {
+ nodes: res.data.data.topo.nodes,
+ calls: [...serverCalls, ...clientCalls],
+ };
+ context.commit(types.SET_INSTANCE_DEPENDENCY, data);
+ });
+ });
+ });
+ },
+ INSTANCE_RELATION_INFO(context: { commit: Commit; state: State; }, params: Call &
+ {mode: string; queryType: string; durationTime: string}) {
+ graph.query(params.queryType).params({
+ id: params.id,
+ duration: params.durationTime,
+ }).then((res: AxiosResponse) => {
+ if (!(res.data && res.data.data)) {
+ return;
+ }
+ context.commit(types.SET_SELECTED_INSTANCE_CALL, params);
+ context.commit(types.SET_INSTANCE_DEPEDENCE_TYPE, params.mode);
+ context.commit(types.SET_INSTANCE_DEPEDENCE_METRICS, res.data.data);
+ });
+ },
};
export default {
diff --git a/src/store/mutation-types.ts b/src/store/mutation-types.ts
index 889b13f..39b76fa 100644
--- a/src/store/mutation-types.ts
+++ b/src/store/mutation-types.ts
@@ -84,6 +84,7 @@
export const SET_SHOW_TRACE_DIALOG = 'SET_SHOW_TRACE_DIALOG';
export const SET_SHOW_INSTANCES_DIALOG = 'SET_SHOW_INSTANCES_DIALOG';
export const SET_SHOW_ENDPOINT_DIALOG = 'SET_SHOW_ENDPOINT_DIALOG';
+export const SET_INSTANCE_DEPENDENCY = 'SET_INSTANCE_DEPENDENCY';
// comparison
export const SET_CHARTVAL = 'SET_CHARTVAL';
@@ -97,3 +98,7 @@
export const SET_CONFIG = 'SET_CONFIG';
export const SET_SERVICE_TOPOLOGY = 'GET_SERVICE_TOPOLOGY';
export const SET_METRICS = 'SET_METRICS';
+export const SET_SELECTED_INSTANCE_CALL = 'SET_SELECTED_INSTANCE_CALL';
+export const SET_INSTANCE_DEPEDENCE_METRICS = 'SET_INSTANCE_DEPEDENCE_METRICS';
+export const SET_INSTANCE_DEPEDENCE_TYPE = 'SET_INSTANCE_DEPEDENCE_TYPE';
+export const SET_SELECTED_CALL = 'SET_SELECTED_CALL';
diff --git a/src/views/components/topology/dependency-sankey.vue b/src/views/components/topology/dependency-sankey.vue
new file mode 100644
index 0000000..239853d
--- /dev/null
+++ b/src/views/components/topology/dependency-sankey.vue
@@ -0,0 +1,81 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+<template>
+ <RkEcharts height="100%" :option="optionConfigs" :clickEvent="clickLinks"/>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import { Component, Prop } from 'vue-property-decorator';
+import { State, Action, Getter, Mutation } from 'vuex-class';
+import echarts from 'echarts/lib/echarts';
+
+@Component
+export default class DependencySankey extends Vue {
+ @Prop() private data: any;
+ @Getter('durationTime') private durationTime: any;
+ @Action('rocketTopo/GET_INSTANCE_DEPENDENCY_METRICS') private GET_INSTANCE_DEPENDENCY_METRICS: any;
+
+ get optionConfigs() {
+ return {
+ series: {
+ type: 'sankey',
+ focusNodeAdjacency: 'allEdges',
+ data: this.data.nodes,
+ links: this.data.calls,
+ label: {
+ color: '#fff',
+ formatter: ((param: any) => param.data.name),
+ },
+ color: [
+ '#bf99f8',
+ '#3fe1da',
+ '#6be6c1',
+ '#3fcfdc',
+ '#626c91',
+ '#3fbcde',
+ '#a0a7e6',
+ '#3fa9e1',
+ '#96dee8',
+ ],
+ itemStyle: {
+ normal: {
+ borderWidth: 0,
+ },
+ },
+ lineStyle: {
+ normal: {
+ color: 'source',
+ opacity: 0.12,
+ },
+ },
+ },
+ };
+ }
+
+ private clickLinks(params: any) {
+ if (params.dataType === 'edge' && params.data) {
+ this.GET_INSTANCE_DEPENDENCY_METRICS({
+ ...params.data,
+ durationTime: this.durationTime,
+ mode: params.data.detectPoints[0],
+ });
+ }
+ }
+}
+</script>
diff --git a/src/views/components/topology/topo-aside.vue b/src/views/components/topology/topo-aside.vue
index b7ffc0b..b67a246 100644
--- a/src/views/components/topology/topo-aside.vue
+++ b/src/views/components/topology/topo-aside.vue
@@ -22,13 +22,13 @@
<use xlink:href="#issues"/>
</svg>
<svg v-if="showServerInfo" class="link-topo-aside-btn icon cp lg"
- @click="show = !show"
- :style="`position:absolute;left:290px;transform: rotate(${show?0 : 180}deg);top:50px;`">
+ @click="show = !show"
+ :style="`position:absolute;left:290px;transform: rotate(${show?0 : 180}deg);top:50px;`">
<use xlink:href="#chevron-left"/>
</svg>
<TopoService/>
<div v-if="show">
- <div class="link-topo-aside-box" style="top:50px" v-if="!stateTopo.selectedCallId && showServerInfo">
+ <div class="link-topo-aside-box" style="top:50px" v-if="!stateTopo.selectedServiceCall && showServerInfo">
<div class="mb-20">
<span class="b dib mr-20">{{$t('serviceDetail')}}</span>
</div>
@@ -42,104 +42,23 @@
</div>
<div>
<TopoChart
- v-if="rocketDashboard.serviceApdexScore.ApdexScore.length"
- :data="rocketDashboard.serviceApdexScore.ApdexScore"
- :intervalTime="intervalTime"
- title="Service ApdexScore"
- unit=""
+ v-if="rocketDashboard.serviceApdexScore.ApdexScore.length"
+ :data="rocketDashboard.serviceApdexScore.ApdexScore"
+ :intervalTime="intervalTime"
+ title="Service ApdexScore"
+ unit=""
/>
<TopoChart
- v-if="rocketDashboard.serviceSLA.SLA.length"
- :data="rocketDashboard.serviceSLA.SLA"
- :intervalTime="intervalTime"
- :precent="true"
- title="Service SLA"
- unit="%"
+ v-if="rocketDashboard.serviceSLA.SLA.length"
+ :data="rocketDashboard.serviceSLA.SLA"
+ :intervalTime="intervalTime"
+ :precent="true"
+ title="Service SLA"
+ unit="%"
/>
</div>
</div>
- <div v-if="stateTopo.selectedCallId || showServerInfo"
- :class="`link-topo-aside-box link-topo-aside-box-${isMini?'min':'max'}`"
- :style="`top:80px;position: fixed;right: 30px;${showInfoCount === 0 ? 'animation: unset;': ''}`">
- <svg
- :style="`position:absolute;left:-48px;top:0;transform: rotate(${isMini?0 : 180}deg);`"
- class="link-topo-aside-btn icon cp lg"
- @click="setShowInfo"
- >
- <use xlink:href="#chevron-left"/>
- </svg>
- <div class="mb-5 clear">
- <span v-if="stateTopo.selectedCallId" class="b dib mr-20 vm">{{ $t('detectPoint') }}</span>
- <span v-else-if="showServerInfo" class="b dib mr-20 vm">{{ $t('serviceDetail') }}</span>
- <span
- v-if="stateTopo.detectPoints.indexOf('CLIENT') !== -1"
- :class="{'active':!stateTopo.mode}"
- class="link-topo-aside-box-btn tc r sm cp b"
- @click="setMode(false)"
- >{{ this.$t('client') }}</span>
- <span
- v-if="stateTopo.detectPoints.indexOf('SERVER') !== -1"
- :class="{'active':stateTopo.mode}"
- class="link-topo-aside-box-btn tc r sm cp b"
- @click="setMode(true)"
- >{{ this.$t('server') }}</span>
- </div>
- <div v-if="showInfo">
- <div v-if="stateTopo.selectedCallId">
- <TopoChart
- v-if="stateTopo.getResponseTimeTrend.length"
- :data="stateTopo.getResponseTimeTrend"
- :intervalTime="intervalTime"
- :title="$t('avgResponseTime')"
- unit="ms"
- />
- <TopoChart
- v-if="stateTopo.getThroughputTrend.length"
- :data="stateTopo.getThroughputTrend"
- :intervalTime="intervalTime"
- :title="$t('avgThroughput')"
- unit="cpm"
- />
- <TopoChart
- v-if="stateTopo.getSLATrend.length"
- :data="stateTopo.getSLATrend"
- :intervalTime="intervalTime"
- :precent="true"
- :title="$t('avgSLA')"
- unit="%"
- />
- <ChartResponse
- v-if="stateTopo.p50.length"
- :data="stateTopo"
- :intervalTime="intervalTime"
- :title="$t('percentResponse')"
- />
- </div>
- <div v-else-if="showServerInfo">
- <TopoChart
- v-if="rocketDashboard.serviceResponseTime.ResponseTime.length"
- :data="rocketDashboard.serviceResponseTime.ResponseTime"
- :intervalTime="intervalTime"
- title="Service ResponseTime"
- unit="ms"
- />
- <TopoChart
- v-if="rocketDashboard.serviceThroughput.Throughput.length"
- :data="rocketDashboard.serviceThroughput.Throughput"
- :intervalTime="intervalTime"
- title="Service Throughput"
- unit="cpm"
- />
- <ChartResponse
- v-if="rocketDashboard.servicePercent.p50.length"
- :data="rocketDashboard.servicePercent"
- :intervalTime="intervalTime"
- title="Service Response Time Percentile"
- unit="ms"
- />
- </div>
- </div>
- </div>
+ <TopoDetectPoint />
</div>
<el-drawer
v-if="stateTopo.showAlarmDialog"
@@ -150,9 +69,9 @@
:modal-append-to-body="false"
>
<alarm-containers :style="`height: ${drawerMainBodyHeight}`"
- :alarmScope="{label: 'Service', key: 'Service'}"
- inTopo
- :keyword="stateTopo.honeycombNode.name"
+ :alarmScope="{label: 'Service', key: 'Service'}"
+ inTopo
+ :keyword="stateTopo.honeycombNode.name"
/>
</el-drawer>
<el-drawer
@@ -164,19 +83,19 @@
:modal-append-to-body="false"
>
<trace-containers :style="`height: ${drawerMainBodyHeight}`"
- :service="{label: stateTopo.honeycombNode.name, key: stateTopo.honeycombNode.id}"
- inTopo
+ :service="{label: stateTopo.honeycombNode.name, key: stateTopo.honeycombNode.id}"
+ inTopo
/>
</el-drawer>
<instances-survey-window
- v-if="stateTopo.showInstancesDialog"
- :is-show.sync="stateTopo.showInstancesDialog"
- :instances="stateDashboardOption.instances"
+ v-if="stateTopo.showInstancesDialog"
+ :is-show.sync="stateTopo.showInstancesDialog"
+ :instances="stateDashboardOption.instances"
/>
<endpoint-survey-window
- v-if="stateTopo.showEndpointDialog"
- :is-show.sync="stateTopo.showEndpointDialog"
- :endpoints="stateDashboardOption.endpoints"
+ v-if="stateTopo.showEndpointDialog"
+ :is-show.sync="stateTopo.showEndpointDialog"
+ :endpoints="stateDashboardOption.endpoints"
/>
</aside>
</template>
@@ -193,11 +112,14 @@
import TopoChart from './topo-chart.vue';
import ChartResponse from './topo-response.vue';
import TopoService from './topo-services.vue';
+ import TopoInstanceDependency from './topo-instance-dependency.vue';
+ import TopoDetectPoint from './topo-detect-point.vue';
@Component({
components: {
TopoChart, TopoService, ChartResponse, Radial, AlarmContainers,
TraceContainers, InstancesSurveyWindow, EndpointSurveyWindow,
+ TopoInstanceDependency, TopoDetectPoint,
},
})
export default class TopoAside extends Vue {
@@ -208,23 +130,17 @@
@Action('rocketTopo/GET_TOPO') private GET_TOPO: any;
@Action('rocketTopo/CLEAR_TOPO') private CLEAR_TOPO: any;
@Action('SELECT_SERVICE') private SELECT_SERVICE: any;
- @Action('GET_QUERY') private GET_QUERY: any;
- @Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
- @Action('MIXHANDLE_GET_OPTION') private MIXHANDLE_GET_OPTION: any;
@Mutation('rocketTopo/SET_MODE_STATUS') private SET_MODE_STATUS: any;
@Action('rocketTopo/CLEAR_TOPO_INFO') private CLEAR_TOPO_INFO: any;
@Mutation('SET_COMPS_TREE') private SET_COMPS_TREE: any;
@Mutation('SET_EVENTS') private SET_EVENTS: any;
@State('rocketDashboard') private rocketDashboard: any;
-
+ private dialogTopoVisible = false;
private drawerMainBodyHeight = '100%';
private initState = initState;
private radioStatus: boolean = false;
private show: boolean = true;
- private showInfo: boolean = false;
- private isMini: boolean = true;
- private showInfoCount: number = 0;
private get showServerInfo() {
return this.stateTopo.currentNode.name && this.stateTopo.currentNode.isReal;
@@ -272,55 +188,9 @@
this.getTopo();
}
- @Watch('stateTopo.selectedCallId')
- private watchDetectPointNodeId(newValue: string) {
- if (newValue || this.stateTopo.currentNode.isReal) {
- this.showInfo = true;
- } else {
- this.showInfo = false;
- this.isMini = true;
- }
- }
-
- @Watch('stateTopo.currentNode.name')
- private watchCurrentNodeIsReal(newValue: boolean) {
- const service = this.stateTopo.currentNode;
- if (this.stateTopo.currentNode.isReal) {
- this.MIXHANDLE_CHANGE_GROUP_WITH_CURRENT({index: 0, current: 1});
- this.MIXHANDLE_GET_OPTION({compType: 'service', duration: this.durationTime})
- .then(() => {
- this.GET_QUERY({
- serviceId: service.id || '',
- duration: this.durationTime,
- });
- });
- }
- if (newValue || this.stateTopo.selectedCallId) {
- this.showInfo = true;
- } else {
- this.showInfo = false;
- this.isMini = true;
- }
- }
-
-
private showRadial() {
this.radioStatus = !this.radioStatus;
}
-
- private setMode(mode: boolean) {
- this.SET_MODE_STATUS(mode);
- this.stateTopo.callback();
- }
-
- private setShowInfo() {
- this.showInfo = false;
- this.showInfoCount = 1;
- this.isMini = !this.isMini;
- setTimeout(() => {
- this.showInfo = true;
- }, 550);
- }
}
</script>
<style lang="scss">
@@ -337,18 +207,6 @@
}
}
- .link-topo-aside-box-btn {
- color: #626977;
- border: 1px solid;
- padding: 0px 3px;
- width: 45px;
- display: inline-block;
-
- &.active {
- color: #448dfe;
- }
- }
-
.link-topo-aside-btn {
display: block;
background: #252a2f9a;
diff --git a/src/views/components/topology/topo-detect-point.vue b/src/views/components/topology/topo-detect-point.vue
new file mode 100644
index 0000000..ab0ac40
--- /dev/null
+++ b/src/views/components/topology/topo-detect-point.vue
@@ -0,0 +1,283 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+<template>
+ <div v-if="stateTopo.selectedServiceCall || showServerInfo"
+ :class="`link-topo-aside-box link-topo-aside-box-${isMini?'min':'max'}`"
+ :style="`top:80px;position: fixed;right: 30px;${showInfoCount === 0 ? 'animation: unset;': ''}`">
+ <svg
+ :style="`position:absolute;left:-48px;top:0;transform: rotate(${isMini?0 : 180}deg);`"
+ class="link-topo-aside-btn icon cp lg"
+ @click="setShowInfo"
+ >
+ <use xlink:href="#chevron-left"/>
+ </svg>
+ <div class="mb-5 clear">
+ <span v-if="stateTopo.selectedServiceCall" class="b dib mr-20 vm">{{ $t('detectPoint') }}</span>
+ <span v-else-if="showServerInfo" class="b dib mr-20 vm">{{ $t('serviceDetail') }}</span>
+ <span
+ v-if="stateTopo.detectPoints.indexOf('CLIENT') !== -1"
+ :class="{'active':!stateTopo.mode}"
+ class="link-topo-aside-box-btn tc r sm cp b"
+ @click="setMode(false)"
+ >{{ this.$t('client') }}</span>
+ <span
+ v-if="stateTopo.detectPoints.indexOf('SERVER') !== -1"
+ :class="{'active':stateTopo.mode}"
+ class="link-topo-aside-box-btn tc r sm cp b"
+ @click="setMode(true)"
+ >{{ this.$t('server') }}</span>
+ </div>
+ <div v-if="showInfo">
+ <div v-if="stateTopo.selectedServiceCall">
+ <TopoChart
+ v-if="stateTopo.getResponseTimeTrend.length"
+ :data="stateTopo.getResponseTimeTrend"
+ :intervalTime="intervalTime"
+ :title="$t('avgResponseTime')"
+ unit="ms"
+ />
+ <TopoChart
+ v-if="stateTopo.getThroughputTrend.length"
+ :data="stateTopo.getThroughputTrend"
+ :intervalTime="intervalTime"
+ :title="$t('avgThroughput')"
+ unit="cpm"
+ />
+ <TopoChart
+ v-if="stateTopo.getSLATrend.length"
+ :data="stateTopo.getSLATrend"
+ :intervalTime="intervalTime"
+ :precent="true"
+ :title="$t('avgSLA')"
+ unit="%"
+ />
+ <ChartResponse
+ v-if="stateTopo.p50.length"
+ :data="stateTopo"
+ :intervalTime="intervalTime"
+ :title="$t('percentResponse')"
+ />
+ </div>
+ <div v-else-if="showServerInfo">
+ <TopoChart
+ v-if="rocketDashboard.serviceResponseTime.ResponseTime.length"
+ :data="rocketDashboard.serviceResponseTime.ResponseTime"
+ :intervalTime="intervalTime"
+ title="Service ResponseTime"
+ unit="ms"
+ />
+ <TopoChart
+ v-if="rocketDashboard.serviceThroughput.Throughput.length"
+ :data="rocketDashboard.serviceThroughput.Throughput"
+ :intervalTime="intervalTime"
+ title="Service Throughput"
+ unit="cpm"
+ />
+ <ChartResponse
+ v-if="rocketDashboard.servicePercent.p50.length"
+ :data="rocketDashboard.servicePercent"
+ :intervalTime="intervalTime"
+ title="Service Response Time Percentile"
+ unit="ms"
+ />
+ </div>
+ </div>
+ <div class="show-dependency" v-if="stateTopo.selectedServiceCall">
+ <a class="rk-btn lg" @click="openInstanceModal">{{$t('ShowInstanceDependency')}}</a>
+ <el-dialog
+ class="instance-dependency"
+ :width="'90%'"
+ :title="`${stateTopo.selectedServiceCall.source.name} -> ${stateTopo.selectedServiceCall.target.name} Instance Dependency`"
+ :visible.sync="dialogTopoVisible"
+ :modal-append-to-body="false"
+ :close-on-click-modal="false"
+ :destroy-on-close="true"
+ :before-close="clearInstance"
+ >
+ <TopoInstanceDependency />
+ </el-dialog>
+ </div>
+ </div>
+</template>
+<script lang="ts">
+
+ import { Component, Vue, Watch, Prop } from 'vue-property-decorator';
+ import { Action, Getter, Mutation, State } from 'vuex-class';
+ import topo, { State as topoState } from '@/store/modules/topology';
+ import TopoInstanceDependency from './topo-instance-dependency.vue';
+ import TopoChart from './topo-chart.vue';
+ import ChartResponse from './topo-response.vue';
+
+ @Component({
+ components: {
+ TopoInstanceDependency, ChartResponse, TopoChart,
+ },
+ })
+ export default class TopoDetectPoint extends Vue {
+ @State('rocketTopo') private stateTopo!: topoState;
+ @Getter('intervalTime') private intervalTime: any;
+ @Getter('durationTime') private durationTime: any;
+ @Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
+ @Action('MIXHANDLE_GET_OPTION') private MIXHANDLE_GET_OPTION: any;
+ @Action('GET_QUERY') private GET_QUERY: any;
+ @Mutation('rocketTopo/SET_MODE_STATUS') private SET_MODE_STATUS: any;
+ @State('rocketDashboard') private rocketDashboard: any;
+ @Mutation('rocketTopo/SET_SELECTED_INSTANCE_CALL') private SET_SELECTED_INSTANCE_CALL: any;
+ @Mutation('rocketTopo/SET_INSTANCE_DEPENDENCY') private SET_INSTANCE_DEPENDENCY: any;
+ @Action('rocketTopo/CLEAR_TOPO_INFO') private CLEAR_TOPO_INFO: any;
+ @Action('rocketTopo/GET_TOPO_INSTANCE_DEPENDENCY') private GET_INSTANCE_DEPENDENCY: any;
+
+ private isMini: boolean = true;
+ private showInfoCount: number = 0;
+ private showInfo: boolean = false;
+ private dialogTopoVisible = false;
+
+ private get showServerInfo() {
+ return this.stateTopo.currentNode.name && this.stateTopo.currentNode.isReal;
+ }
+
+ @Watch('stateTopo.selectedServiceCall')
+ private watchDetectPointNodeId(newValue: string) {
+ if (newValue || this.stateTopo.currentNode.isReal) {
+ this.showInfo = true;
+ } else {
+ this.showInfo = false;
+ this.isMini = true;
+ }
+ }
+
+ @Watch('stateTopo.currentNode.name')
+ private watchCurrentNodeIsReal(newValue: boolean) {
+ const service = this.stateTopo.currentNode;
+ if (this.stateTopo.currentNode.isReal) {
+ this.MIXHANDLE_CHANGE_GROUP_WITH_CURRENT({index: 0, current: 1});
+ this.MIXHANDLE_GET_OPTION({compType: 'service', duration: this.durationTime})
+ .then(() => {
+ this.GET_QUERY({
+ serviceId: service.id || '',
+ duration: this.durationTime,
+ });
+ });
+ }
+ if (newValue || this.stateTopo.selectedServiceCall) {
+ this.showInfo = true;
+ } else {
+ this.showInfo = false;
+ this.isMini = true;
+ }
+ }
+
+ private setShowInfo() {
+ this.showInfo = false;
+ this.showInfoCount = 1;
+ this.isMini = !this.isMini;
+ setTimeout(() => {
+ this.showInfo = true;
+ }, 550);
+ }
+
+ private setMode(mode: boolean) {
+ this.SET_MODE_STATUS(mode);
+ this.stateTopo.callback();
+ }
+
+ private clearInstance() {
+ this.dialogTopoVisible = false;
+ this.SET_SELECTED_INSTANCE_CALL(null);
+ }
+
+ private openInstanceModal() {
+ this.dialogTopoVisible = true;
+ if (!(this.stateTopo.selectedServiceCall && this.stateTopo.selectedServiceCall.source)) {
+ return;
+ }
+ this.GET_INSTANCE_DEPENDENCY({
+ serverServiceId: this.stateTopo.selectedServiceCall.source.id,
+ clientServiceId: this.stateTopo.selectedServiceCall.target.id,
+ duration: this.durationTime,
+ });
+ }
+ }
+
+</script>
+<style lang="scss">
+ .link-topo-aside-box-btn {
+ color: #626977;
+ border: 1px solid;
+ padding: 0px 3px;
+ width: 45px;
+ display: inline-block;
+ &.active {
+ color: #448dfe;
+ }
+ }
+ .show-dependency {
+ margin: 20px 0;
+ .rk-btn {
+ display: block;
+ text-align: center;
+ }
+ .el-dialog__header{
+ background: #333;
+ .el-dialog__title{
+ color: #efeff1;
+ }
+ }
+ .el-dialog__body{
+ background: #333;
+ color: #efeff1;
+ height: 650px;
+ padding: 10px 20px;
+ }
+ }
+ .link-topo-aside-box {
+ border-radius: 4px;
+ position: absolute;
+ width: 280px;
+ z-index: 101;
+ color: #ddd;
+ background-color: #333;
+ padding: 15px 20px 10px;
+
+ .label {
+ display: inline-block;
+ width: 40%;
+ }
+
+ .content {
+ vertical-align: top;
+ display: inline-block;
+ width: 60%;
+ }
+
+ .circle {
+ width: 8px;
+ height: 8px;
+ border-radius: 4px;
+ background-color: #EE5B5B;
+ margin-top: 6px;
+ }
+ }
+
+ .link-topo-aside-box {
+ p {
+ margin-block-start: auto !important;
+ margin-block-end: auto !important;
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/src/views/components/topology/topo-instance-dependency.vue b/src/views/components/topology/topo-instance-dependency.vue
new file mode 100644
index 0000000..dc455dd
--- /dev/null
+++ b/src/views/components/topology/topo-instance-dependency.vue
@@ -0,0 +1,129 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+<template>
+ <div class="rk-topo-instance-dependency">
+ <div class="rk-dependency-chart">
+ <DependencySankey :data="stateTopo.instanceDependency" />
+ </div>
+ <div v-if="!stateTopo.instanceDependency.nodes.length">No Instance Dependency</div>
+ <div v-if="stateTopo.selectedInstanceCall" class="rk-instance-dependency-metrics">
+ <div class="mb-5 clear">
+ <span class="b dib mr-20 vm">{{ $t('detectPoint') }}</span>
+ <span
+ v-if="stateTopo.selectedInstanceCall.detectPoints.includes('CLIENT')"
+ class="link-topo-aside-box-btn tc r sm cp b"
+ :class="{'active':stateTopo.queryInstanceMetricsType==='CLIENT'}"
+ @click="setMode('CLIENT')"
+ >{{ this.$t('client') }}</span>
+ <span
+ v-if="stateTopo.selectedInstanceCall.detectPoints.includes('SERVER')"
+ class="link-topo-aside-box-btn tc r sm cp b"
+ :class="{'active':stateTopo.queryInstanceMetricsType==='SERVER' }"
+ @click="setMode('SERVER')"
+ >{{ this.$t('server') }}</span>
+ </div>
+ <div v-if="stateTopo.selectedInstanceCall">
+ <TopoChart
+ v-if="stateTopo.instanceDependencyMetrics.getResponseTimeTrend"
+ :data="stateTopo.instanceDependencyMetrics.getResponseTimeTrend"
+ :intervalTime="intervalTime"
+ :title="$t('avgResponseTime')"
+ unit="ms"
+ />
+ <TopoChart
+ v-if="stateTopo.instanceDependencyMetrics.getThroughputTrend"
+ :data="stateTopo.instanceDependencyMetrics.getThroughputTrend"
+ :intervalTime="intervalTime"
+ :title="$t('avgThroughput')"
+ unit="cpm"
+ />
+ <TopoChart
+ v-if="stateTopo.instanceDependencyMetrics.getSLATrend"
+ :data="stateTopo.instanceDependencyMetrics.getSLATrend"
+ :intervalTime="intervalTime"
+ :precent="true"
+ :title="$t('avgSLA')"
+ unit="%"
+ />
+ <ChartResponse
+ v-if="stateTopo.instanceDependencyMetrics.p50"
+ :data="stateTopo.instanceDependencyMetrics"
+ :intervalTime="intervalTime"
+ :title="$t('percentResponse')"
+ />
+ </div>
+ </div>
+ </div>
+</template>
+<script lang="ts">
+import { Vue, Component, Prop } from 'vue-property-decorator';
+import { State, Action, Getter, Mutation } from 'vuex-class';
+
+import { State as topoState} from '@/store/modules/topology';
+import Topo from './topo.vue';
+import TopoChart from './topo-chart.vue';
+import ChartResponse from './topo-response.vue';
+import DependencySankey from './dependency-sankey.vue';
+
+@Component({
+ components: {
+ Topo, ChartResponse, TopoChart, DependencySankey,
+ },
+})
+export default class TopoInstanceDependency extends Vue {
+ @Getter('durationTime') private durationTime: any;
+ @State('rocketTopo') private stateTopo!: topoState;
+ @State('rocketDashboard') private rocketDashboard: any;
+ @Getter('intervalTime') private intervalTime: any;
+ @Mutation('rocketTopo/SET_INSTANCE_DEPEDENCE_TYPE') private SET_MODE_STATUS: any;
+ @Action('rocketTopo/GET_INSTANCE_DEPENDENCY_METRICS') private GET_INSTANCE_DEPENDENCY_METRICS: any;
+
+ private showInfo: boolean = true;
+
+ private setMode(mode: string) {
+ this.GET_INSTANCE_DEPENDENCY_METRICS({
+ ...this.stateTopo.selectedInstanceCall,
+ durationTime: this.durationTime,
+ mode,
+ });
+ }
+}
+
+</script>
+<style lang="scss">
+ .rk-topo-instance-dependency {
+ height: 100%;
+ display: flex;
+ flex-direction: row;
+ &>:first-child {
+ line-height: 400px;
+ text-align: center;
+ width: 100%;
+ }
+ .rk-instance-dependency-metrics {
+ width: 320px;
+ height: 100%;
+ background: #111;
+ padding: 10px 20px;
+ }
+ .rk-dependency-chart {
+ width: 850px;
+ height: 100%;
+ }
+ }
+</style>
diff --git a/src/views/components/topology/topo-services.vue b/src/views/components/topology/topo-services.vue
index 8011746..73d4899 100644
--- a/src/views/components/topology/topo-services.vue
+++ b/src/views/components/topology/topo-services.vue
@@ -16,7 +16,7 @@
*/
<template>
- <div class="link-topo-aside-box" style="padding:0;z-index:102;">
+ <div class="link-topo-aside-box" style="padding:0;">
<TopoSelect :current="service" :data="services" @onChoose="handleChange"/>
</div>
</template>
diff --git a/src/views/components/topology/topo.vue b/src/views/components/topology/topo.vue
index 2a7fb8f..66b3061 100644
--- a/src/views/components/topology/topo.vue
+++ b/src/views/components/topology/topo.vue
@@ -19,16 +19,16 @@
<div class="micro-topo-chart"></div>
</template>
<script lang="js">
- import CssHelper from '@/utils/cssHelper';
- import * as d3 from 'd3';
- import d3tip from 'd3-tip';
+ import CssHelper from '@/utils/cssHelper';
+ import * as d3 from 'd3';
+ import d3tip from 'd3-tip';
/* tslint:disable */
-const diagonal = d3.linkHorizontal()
- .x(function (d) { return d.x })
- .y(function (d) { return d.y });
-const diagonalvertical = d3.linkVertical()
- .x(function (d) { return d.x })
- .y(function (d) { return d.y });
+ const diagonal = d3.linkHorizontal()
+ .x(function (d) { return d.x })
+ .y(function (d) { return d.y });
+ const diagonalvertical = d3.linkVertical()
+ .x(function (d) { return d.x })
+ .y(function (d) { return d.y });
export default {
props: {
@@ -141,26 +141,26 @@
'datas.nodes': 'draw',
},
methods: {
- removeHoneycomb(that) {
- const appGovernTopoHoneycombFrames = d3.select('#app-govern-topo-honeycomb-frames');
- appGovernTopoHoneycombFrames.nodes().forEach((node) => {
- const childrenArray = Array.from(node.children).reverse();
- _.forEach(childrenArray, (ele, index) => {
- ele.classList.toggle('reverse');
+ removeHoneycomb(that) {
+ const appGovernTopoHoneycombFrames = d3.select('#app-govern-topo-honeycomb-frames');
+ appGovernTopoHoneycombFrames.nodes().forEach((node) => {
+ const childrenArray = Array.from(node.children).reverse();
+ _.forEach(childrenArray, (ele, index) => {
+ ele.classList.toggle('reverse');
+ setTimeout(() => {
+ ele.remove();
+ }, 130 * index);
+ });
+ });
setTimeout(() => {
- ele.remove();
- }, 130 * index);
- });
- });
- setTimeout(() => {
- appGovernTopoHoneycombFrames.remove();
- }, 780);
- },
+ appGovernTopoHoneycombFrames.remove();
+ }, 780);
+ },
draw(value, oldValue) {
// Avoid unnecessary repetitive rendering
const diffNodes = _.difference(_.sortBy(value, 'id'), _.sortBy(oldValue, 'id'));
- if(value && value.length > 0 && diffNodes && diffNodes.length <=0){
- return null;
+ if(value && value.length > 0 && diffNodes && diffNodes.length <=0) {
+ return;
}
const codeId = this.datas.nodes.map(i => i.id);
for (let i = 0; i < this.datas.calls.length; i += 1) {
@@ -169,7 +169,7 @@
this.datas.calls[i].target = this.datas.calls[i].source;
}
}
- this.svg.select('.graph').remove();
+ this.svg.select(`.graph_${this.datas.type || ''}`).remove();
this.force = d3
.forceSimulation(this.datas.nodes)
.force('collide', d3.forceCollide().radius(() => 65))
@@ -180,7 +180,7 @@
.force('center', d3.forceCenter(window.innerWidth / 2 + 100, this.height / 2))
.on('tick', this.tick)
.stop();
- this.graph = this.svg.append('g').attr('class', 'graph');
+ this.graph = this.svg.append('g').attr('class', `graph_${this.datas.type || ''}`);
this.svg.call(this.getZoomBehavior(this.graph));
this.graph.call(this.tip);
this.graph.call(this.tipName);
@@ -226,57 +226,11 @@
})
.on('click', function(d, i) {
event.stopPropagation();
- that.tip.hide({}, this);
- that.node.attr('class', '');
- d3.select(this).attr('class', 'node-active');
- const copyD = JSON.parse(JSON.stringify(d));
- delete copyD.x;
- delete copyD.y;
- delete copyD.vx;
- delete copyD.vy;
- delete copyD.fx;
- delete copyD.fy;
- delete copyD.index;
- that.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
- that.$store.commit('rocketTopo/SET_NODE', copyD);
- that.toggleNode(that.node, d, true);
- that.toggleLine(that.line, d, true);
- that.toggleLine(that.lineNode, d, true);
+ // active selected nodes and disable another nodes of non-relations
+ that.clickNodesToUpdate(d, this);
if (d.isReal) {
- const honeycombFrames = d3.select('#honeycomb-selector_honeycomb-frames');
- const appGovernTopoHoneycombFrames = that.graph.append('g')
- .attr('id', 'app-govern-topo-honeycomb-frames')
- .attr('style', honeycombFrames.attr('style'))
- .attr('stroke', honeycombFrames.attr('stroke')).html(honeycombFrames.html())
- .on('mouseleave', function () {
- that.removeHoneycomb(that);
- });
- const nodeTranslate = CssHelper.translateSerialization(this.getAttribute('transform'));
- const appGovernTopoHoneycombFramesTranslate = CssHelper.matrixSerialization(honeycombFrames.attr('transform'));
- appGovernTopoHoneycombFramesTranslate.tx = nodeTranslate.x - 83;
- appGovernTopoHoneycombFramesTranslate.ty = nodeTranslate.y + 72;
- appGovernTopoHoneycombFrames.attr('transform', CssHelper.matrixDeserialization(appGovernTopoHoneycombFramesTranslate));
-
- that.$store.commit('rocketTopo/SET_HONEYCOMB_NODE', d);
-
- d3.selectAll('#honeycomb-selector_honeycomb-group-top-right').on('click', () => {
- that.$store.commit('rocketTopo/SET_SHOW_ALARM_DIALOG', true);
- that.removeHoneycomb(that);
- });
- d3.selectAll('#honeycomb-selector_honeycomb-group-below-right').on('click', () => {
- that.$store.commit('rocketTopo/SET_SHOW_TRACE_DIALOG', true);
- that.removeHoneycomb(that);
- });
- d3.selectAll('#honeycomb-selector_honeycomb-group-below-left').on('click', () => {
- that.$store.commit('SET_CURRENT_SERVICE', { key: d.id, label: d.name });
- that.$store.commit('rocketTopo/SET_SHOW_INSTANCES_DIALOG', true);
- that.removeHoneycomb(that);
- });
- d3.selectAll('#honeycomb-selector_honeycomb-group-top-left').on('click', () => {
- that.$store.commit('SET_CURRENT_SERVICE', { key: d.id, label: d.name });
- that.$store.commit('rocketTopo/SET_SHOW_ENDPOINT_DIALOG', true);
- that.removeHoneycomb(that);
- });
+ // show some entrance icons for service nodes, such as alarm, instance, endpoint
+ that.dashboardEntranceIcons(d, this);
}
});
this.node
@@ -319,7 +273,8 @@
.attr('text-anchor', 'middle')
.attr('x', 22)
.attr('y', 70)
- .text(d => d.name.length > 12 ? `${d.name.substring(0,12)}...`: d.name)
+ // .text(d => d.name)
+ .text(d => d.name.length > 20 ? `${d.name.substring(0, 20)}...`: d.name)
this.glink = this.graph.append('g').selectAll('.link');
this.link = this.glink.data(this.datas.calls).enter();
this.line = this.link.append('path').attr('class', 'link')
@@ -329,24 +284,13 @@
that.tip.hide({}, this);
}
this.lineNode = this.link.append('rect').attr('class', 'link-node cp')
- .attr('width', 6)
- .attr('height', 6)
+ .attr('width', 10)
+ .attr('height', 10)
.attr('rx', 3)
.attr('ry', 3)
.attr('fill', d => d.cpm ? '#217EF299' : '#6a6d7799')
.on('click', function(d, i) {
- that.$store.commit('rocketTopo/SET_NODE', {});
- that.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
- that.$store.commit('rocketTopo/SET_MODE', d.detectPoints);
- event.stopPropagation();
- that.tip.hide({}, this);
- that.tip.show(d, this);
- that.$store.dispatch(that.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' : 'rocketTopo/GET_TOPO_CLIENT_INFO', {id:d.id,duration: that.$store.getters.durationTime});
- that.$store.commit('rocketTopo/SET_CALLBACK', function() {
- that.tip.hide({}, this);
- that.tip.show(d, this);
- that.$store.dispatch(that.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' : 'rocketTopo/GET_TOPO_CLIENT_INFO', {id:d.id,duration: that.$store.getters.durationTime});
- })
+ that.clickLinkNodes(d, this);
});
d3.timeout(() => {
for (
@@ -362,8 +306,77 @@
this.tick();
}
});
- },
- isLinkNode(currNode, node) {
+ },
+ clickLinkNodes(d, that) {
+ this.$store.commit('rocketTopo/SET_NODE', {});
+ this.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
+ this.$store.commit('rocketTopo/SET_MODE', d.detectPoints);
+ event.stopPropagation();
+ this.tip.hide({}, that);
+ this.tip.show(d, that);
+ this.$store.dispatch(this.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' : 'rocketTopo/GET_TOPO_CLIENT_INFO', {...d,duration: this.$store.getters.durationTime});
+ this.$store.commit('rocketTopo/SET_CALLBACK', () => {
+ this.tip.hide({}, that);
+ this.tip.show(d, that);
+ this.$store.dispatch(this.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' : 'rocketTopo/GET_TOPO_CLIENT_INFO', {...d,duration: this.$store.getters.durationTime});
+ })
+ },
+ clickNodesToUpdate(d, that) {
+ this.tip.hide({}, that);
+ this.node.attr('class', '');
+ d3.select(that).attr('class', 'node-active');
+ const copyD = JSON.parse(JSON.stringify(d));
+ delete copyD.x;
+ delete copyD.y;
+ delete copyD.vx;
+ delete copyD.vy;
+ delete copyD.fx;
+ delete copyD.fy;
+ delete copyD.index;
+ this.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
+ this.$store.commit('rocketTopo/SET_NODE', copyD);
+ this.toggleNode(this.node, d, true);
+ this.toggleLine(this.line, d, true);
+ this.toggleLine(this.lineNode, d, true);
+ },
+ dashboardEntranceIcons(d, context) {
+ const that = this;
+ const honeycombFrames = d3.select('#honeycomb-selector_honeycomb-frames');
+ const appGovernTopoHoneycombFrames = this.graph.append('g')
+ .attr('id', 'app-govern-topo-honeycomb-frames')
+ .attr('style', honeycombFrames.attr('style'))
+ .attr('stroke', honeycombFrames.attr('stroke')).html(honeycombFrames.html())
+ .on('mouseleave', function () {
+ that.removeHoneycomb(that);
+ });
+ const nodeTranslate = CssHelper.translateSerialization(context.getAttribute('transform'));
+ const appGovernTopoHoneycombFramesTranslate = CssHelper.matrixSerialization(honeycombFrames.attr('transform'));
+ appGovernTopoHoneycombFramesTranslate.tx = nodeTranslate.x - 83;
+ appGovernTopoHoneycombFramesTranslate.ty = nodeTranslate.y + 72;
+ appGovernTopoHoneycombFrames.attr('transform', CssHelper.matrixDeserialization(appGovernTopoHoneycombFramesTranslate));
+
+ that.$store.commit('rocketTopo/SET_HONEYCOMB_NODE', d);
+
+ d3.selectAll('#honeycomb-selector_honeycomb-group-top-right').on('click', () => {
+ that.$store.commit('rocketTopo/SET_SHOW_ALARM_DIALOG', true);
+ that.removeHoneycomb(that);
+ });
+ d3.selectAll('#honeycomb-selector_honeycomb-group-below-right').on('click', () => {
+ this.$store.commit('rocketTopo/SET_SHOW_TRACE_DIALOG', true);
+ that.removeHoneycomb(that);
+ });
+ d3.selectAll('#honeycomb-selector_honeycomb-group-below-left').on('click', () => {
+ that.$store.commit('SET_CURRENT_SERVICE', { key: d.id, label: d.name });
+ that.$store.commit('rocketTopo/SET_SHOW_INSTANCES_DIALOG', true);
+ that.removeHoneycomb(that);
+ });
+ d3.selectAll('#honeycomb-selector_honeycomb-group-top-left').on('click', () => {
+ that.$store.commit('SET_CURRENT_SERVICE', { key: d.id, label: d.name });
+ that.$store.commit('rocketTopo/SET_SHOW_ENDPOINT_DIALOG', true);
+ that.removeHoneycomb(that);
+ });
+ },
+ isLinkNode(currNode, node) {
if (currNode.id === node.id) {
return true;
}
@@ -372,7 +385,7 @@
(i.source.id === node.id || i.target.id === node.id)
).length;
},
- toggleNode(nodeCircle, currNode, isHover) {
+ toggleNode(nodeCircle, currNode, isHover) {
if (isHover) {
nodeCircle.sort((a, b) => a.id === currNode.id ? 1 : -1);
nodeCircle