mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-07-18 14:45:25 +00:00
feat: inspect nodes
This commit is contained in:
parent
413646261d
commit
4badfffac8
17
src/assets/icons/keyboard_backspace.svg
Normal file
17
src/assets/icons/keyboard_backspace.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<path d="M21 11.016v1.969h-14.156l3.563 3.609-1.406 1.406-6-6 6-6 1.406 1.406-3.563 3.609h14.156z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 977 B |
@ -14,26 +14,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
export const ServiceTopology = {
|
|
||||||
variable: "$duration: Duration!, $serviceId: ID!",
|
|
||||||
query: `
|
|
||||||
topology: getServiceTopology(duration: $duration, serviceId: $serviceId) {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
type
|
|
||||||
isReal
|
|
||||||
}
|
|
||||||
calls {
|
|
||||||
id
|
|
||||||
source
|
|
||||||
detectPoints
|
|
||||||
target
|
|
||||||
sourceComponents
|
|
||||||
targetComponents
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
};
|
|
||||||
export const GlobalTopology = {
|
export const GlobalTopology = {
|
||||||
variable: "$duration: Duration!",
|
variable: "$duration: Duration!",
|
||||||
query: `
|
query: `
|
||||||
|
@ -17,13 +17,11 @@
|
|||||||
import {
|
import {
|
||||||
InstanceTopology,
|
InstanceTopology,
|
||||||
EndpointTopology,
|
EndpointTopology,
|
||||||
ServiceTopology,
|
|
||||||
GlobalTopology,
|
GlobalTopology,
|
||||||
ServicesTopology,
|
ServicesTopology,
|
||||||
} from "../fragments/topology";
|
} from "../fragments/topology";
|
||||||
|
|
||||||
export const getGlobalTopology = `query queryData(${GlobalTopology.variable}) {${GlobalTopology.query}}`;
|
export const getGlobalTopology = `query queryData(${GlobalTopology.variable}) {${GlobalTopology.query}}`;
|
||||||
export const getInstanceTopology = `query queryData(${InstanceTopology.variable}) {${InstanceTopology.query}}`;
|
export const getInstanceTopology = `query queryData(${InstanceTopology.variable}) {${InstanceTopology.query}}`;
|
||||||
export const getServiceTopology = `query queryData(${ServiceTopology.variable}) {${ServiceTopology.query}}`;
|
|
||||||
export const getEndpointTopology = `query queryData(${EndpointTopology.variable}) {${EndpointTopology.query}}`;
|
export const getEndpointTopology = `query queryData(${EndpointTopology.variable}) {${EndpointTopology.query}}`;
|
||||||
export const getServicesTopology = `query queryData(${ServicesTopology.variable}) {${ServicesTopology.query}}`;
|
export const getServicesTopology = `query queryData(${ServicesTopology.variable}) {${ServicesTopology.query}}`;
|
||||||
|
@ -68,13 +68,13 @@ export const topologyStore = defineStore({
|
|||||||
setLinkClientMetrics(m: { id: string; value: unknown }[]) {
|
setLinkClientMetrics(m: { id: string; value: unknown }[]) {
|
||||||
this.linkClientMetrics = m;
|
this.linkClientMetrics = m;
|
||||||
},
|
},
|
||||||
async getServiceTopology() {
|
async getServiceTopology(id: string) {
|
||||||
const serviceId = useSelectorStore().currentService.id;
|
const serviceIds = [id];
|
||||||
const duration = useAppStoreWithOut().durationTime;
|
const duration = useAppStoreWithOut().durationTime;
|
||||||
const res: AxiosResponse = await graphql
|
const res: AxiosResponse = await graphql
|
||||||
.query("getServiceTopology")
|
.query("getServicesTopology")
|
||||||
.params({
|
.params({
|
||||||
serviceId,
|
serviceIds,
|
||||||
duration,
|
duration,
|
||||||
});
|
});
|
||||||
if (!res.data.errors) {
|
if (!res.data.errors) {
|
||||||
|
@ -25,13 +25,11 @@ limitations under the License. -->
|
|||||||
v-show="data.widget?.tips"
|
v-show="data.widget?.tips"
|
||||||
/>
|
/>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-popover
|
<el-popover placement="bottom" trigger="click" :width="100">
|
||||||
placement="bottom"
|
|
||||||
trigger="click"
|
|
||||||
:style="{ width: '100px' }"
|
|
||||||
>
|
|
||||||
<template #reference>
|
<template #reference>
|
||||||
|
<span>
|
||||||
<Icon iconName="ellipsis_v" size="middle" class="operation" />
|
<Icon iconName="ellipsis_v" size="middle" class="operation" />
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<div class="tools" @click="editConfig">
|
<div class="tools" @click="editConfig">
|
||||||
<span>{{ t("edit") }}</span>
|
<span>{{ t("edit") }}</span>
|
||||||
|
@ -22,12 +22,18 @@ limitations under the License. -->
|
|||||||
<div class="setting" v-show="showSetting">
|
<div class="setting" v-show="showSetting">
|
||||||
<Settings @update="updateSettings" />
|
<Settings @update="updateSettings" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tool">
|
||||||
|
<span class="switch-icon ml-5" title="Settings">
|
||||||
|
<Icon @click="setConfig" size="middle" iconName="settings" />
|
||||||
|
</span>
|
||||||
|
<span class="switch-icon ml-5" title="Back to overview topology">
|
||||||
<Icon
|
<Icon
|
||||||
@click="setConfig"
|
@click="backToTopology"
|
||||||
class="switch-icon"
|
|
||||||
size="middle"
|
size="middle"
|
||||||
iconName="settings"
|
iconName="keyboard_backspace"
|
||||||
/>
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="operations-list"
|
class="operations-list"
|
||||||
v-if="topologyStore.node && topologyStore.node.isReal"
|
v-if="topologyStore.node && topologyStore.node.isReal"
|
||||||
@ -43,8 +49,8 @@ limitations under the License. -->
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onBeforeUnmount, reactive } from "vue";
|
import { ref, onMounted, onBeforeUnmount, reactive, watch } from "vue";
|
||||||
// import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import d3tip from "d3-tip";
|
import d3tip from "d3-tip";
|
||||||
import zoom from "./utils/zoom";
|
import zoom from "./utils/zoom";
|
||||||
@ -53,6 +59,7 @@ import nodeElement from "./utils/nodeElement";
|
|||||||
import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement";
|
import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement";
|
||||||
import topoLegend from "./utils/legend";
|
import topoLegend from "./utils/legend";
|
||||||
import { Node, Call } from "@/types/topology";
|
import { Node, Call } from "@/types/topology";
|
||||||
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
import { useTopologyStore } from "@/store/modules/topology";
|
import { useTopologyStore } from "@/store/modules/topology";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { EntityType } from "../../data";
|
import { EntityType } from "../../data";
|
||||||
@ -61,7 +68,8 @@ import { ElMessage } from "element-plus";
|
|||||||
import Settings from "./Settings.vue";
|
import Settings from "./Settings.vue";
|
||||||
|
|
||||||
/*global Nullable */
|
/*global Nullable */
|
||||||
// const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const selectorStore = useSelectorStore();
|
||||||
const topologyStore = useTopologyStore();
|
const topologyStore = useTopologyStore();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
const height = ref<number>(document.body.clientHeight - 90);
|
const height = ref<number>(document.body.clientHeight - 90);
|
||||||
@ -80,7 +88,10 @@ const legend = ref<any>(null);
|
|||||||
const showSetting = ref<boolean>(false);
|
const showSetting = ref<boolean>(false);
|
||||||
const settings = ref<any>({});
|
const settings = ref<any>({});
|
||||||
const operationsPos = reactive<{ x: number; y: number }>({ x: NaN, y: NaN });
|
const operationsPos = reactive<{ x: number; y: number }>({ x: NaN, y: NaN });
|
||||||
const items = ref([{ id: "alarm", title: "Alarm", func: handleGoAlarm }]);
|
const items = ref([
|
||||||
|
{ id: "inspect", title: "Inspect", func: handleInspect },
|
||||||
|
{ id: "alarm", title: "Alarm", func: handleGoAlarm },
|
||||||
|
]);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@ -96,6 +107,10 @@ onMounted(async () => {
|
|||||||
.attr("class", "topo-svg")
|
.attr("class", "topo-svg")
|
||||||
.attr("height", height.value)
|
.attr("height", height.value)
|
||||||
.attr("width", width.value);
|
.attr("width", width.value);
|
||||||
|
await init();
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
async function init() {
|
||||||
tip.value = (d3tip as any)().attr("class", "d3-tip").offset([-8, 0]);
|
tip.value = (d3tip as any)().attr("class", "d3-tip").offset([-8, 0]);
|
||||||
graph.value = svg.value.append("g").attr("class", "topo-svg-graph");
|
graph.value = svg.value.append("g").attr("class", "topo-svg-graph");
|
||||||
graph.value.call(tip.value);
|
graph.value.call(tip.value);
|
||||||
@ -117,10 +132,10 @@ onMounted(async () => {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
topologyStore.setNode(null);
|
topologyStore.setNode(null);
|
||||||
|
topologyStore.setLink(null);
|
||||||
// showSetting.value = false;
|
// showSetting.value = false;
|
||||||
});
|
});
|
||||||
update();
|
}
|
||||||
});
|
|
||||||
function ticked() {
|
function ticked() {
|
||||||
link.value.attr(
|
link.value.attr(
|
||||||
"d",
|
"d",
|
||||||
@ -196,10 +211,7 @@ function update() {
|
|||||||
dragended: dragended,
|
dragended: dragended,
|
||||||
handleNodeClick: handleNodeClick,
|
handleNodeClick: handleNodeClick,
|
||||||
tipHtml: (data: Node) => {
|
tipHtml: (data: Node) => {
|
||||||
const nodeMetrics: string[] = settings.value.nodeMetrics;
|
const nodeMetrics: string[] = settings.value.nodeMetrics || [];
|
||||||
if (!nodeMetrics) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const html = nodeMetrics.map((m) => {
|
const html = nodeMetrics.map((m) => {
|
||||||
const metric =
|
const metric =
|
||||||
topologyStore.nodeMetrics[m].values.filter(
|
topologyStore.nodeMetrics[m].values.filter(
|
||||||
@ -247,7 +259,13 @@ function update() {
|
|||||||
return ` <div class="mb-5"><span class="grey">${m}: </span>${metric.value}</div>`;
|
return ` <div class="mb-5"><span class="grey">${m}: </span>${metric.value}</div>`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const html = [...htmlServer, ...htmlClient].join(" ");
|
const html = [
|
||||||
|
...htmlServer,
|
||||||
|
...htmlClient,
|
||||||
|
`<div><span class="grey">${t(
|
||||||
|
"detectPoint"
|
||||||
|
)}:</span>${data.detectPoints.join(" | ")}</div>`,
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
},
|
},
|
||||||
@ -285,6 +303,18 @@ function update() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function handleInspect() {
|
||||||
|
svg.value.selectAll(".topo-svg-graph").remove();
|
||||||
|
const resp = await topologyStore.getServiceTopology(topologyStore.node.id);
|
||||||
|
|
||||||
|
if (resp.errors) {
|
||||||
|
ElMessage.error(resp.errors);
|
||||||
|
}
|
||||||
|
topologyStore.setNode(null);
|
||||||
|
topologyStore.setLink(null);
|
||||||
|
await init();
|
||||||
|
update();
|
||||||
|
}
|
||||||
function handleGoEndpoint() {
|
function handleGoEndpoint() {
|
||||||
const path = `/dashboard/${dashboardStore.layerId}/Endpoint/${topologyStore.node.id}/${settings.value.endpointDashboard}`;
|
const path = `/dashboard/${dashboardStore.layerId}/Endpoint/${topologyStore.node.id}/${settings.value.endpointDashboard}`;
|
||||||
const routeUrl = router.resolve({ path });
|
const routeUrl = router.resolve({ path });
|
||||||
@ -309,11 +339,25 @@ function handleGoAlarm() {
|
|||||||
|
|
||||||
window.open(routeUrl.href, "_blank");
|
window.open(routeUrl.href, "_blank");
|
||||||
}
|
}
|
||||||
|
async function backToTopology() {
|
||||||
|
svg.value.selectAll(".topo-svg-graph").remove();
|
||||||
|
const resp = await topologyStore.getServicesTopology();
|
||||||
|
|
||||||
|
if (resp.errors) {
|
||||||
|
ElMessage.error(resp.errors);
|
||||||
|
}
|
||||||
|
await init();
|
||||||
|
update();
|
||||||
|
topologyStore.setNode(null);
|
||||||
|
topologyStore.setLink(null);
|
||||||
|
}
|
||||||
async function getTopology() {
|
async function getTopology() {
|
||||||
let resp;
|
let resp;
|
||||||
switch (dashboardStore.entity) {
|
switch (dashboardStore.entity) {
|
||||||
case EntityType[0].value:
|
case EntityType[0].value:
|
||||||
resp = await topologyStore.getServiceTopology();
|
resp = await topologyStore.getServiceTopology(
|
||||||
|
selectorStore.currentService.id
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case EntityType[1].value:
|
case EntityType[1].value:
|
||||||
resp = await topologyStore.getServicesTopology();
|
resp = await topologyStore.getServicesTopology();
|
||||||
@ -336,7 +380,10 @@ function resize() {
|
|||||||
svg.value.attr("height", height.value).attr("width", width.value);
|
svg.value.attr("height", height.value).attr("width", width.value);
|
||||||
}
|
}
|
||||||
function updateSettings(config: any) {
|
function updateSettings(config: any) {
|
||||||
items.value = [{ id: "alarm", title: "Alarm", func: handleGoAlarm }];
|
items.value = [
|
||||||
|
{ id: "inspect", title: "Inspect", func: handleInspect },
|
||||||
|
{ id: "alarm", title: "Alarm", func: handleGoAlarm },
|
||||||
|
];
|
||||||
settings.value = config;
|
settings.value = config;
|
||||||
if (config.nodeDashboard) {
|
if (config.nodeDashboard) {
|
||||||
items.value.push({
|
items.value.push({
|
||||||
@ -363,6 +410,12 @@ function updateSettings(config: any) {
|
|||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener("resize", resize);
|
window.removeEventListener("resize", resize);
|
||||||
});
|
});
|
||||||
|
// watch(
|
||||||
|
// () => [topologyStore.nodes, topologyStore.calls],
|
||||||
|
// () => {
|
||||||
|
// update();
|
||||||
|
// }
|
||||||
|
// );
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.micro-topo-chart {
|
.micro-topo-chart {
|
||||||
@ -372,7 +425,7 @@ onBeforeUnmount(() => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 20px;
|
top: 20px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
width: 350px;
|
width: 360px;
|
||||||
height: 700px;
|
height: 700px;
|
||||||
background-color: #2b3037;
|
background-color: #2b3037;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@ -399,17 +452,25 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
span:hover {
|
span:hover {
|
||||||
color: #217ef2;
|
color: #409eff;
|
||||||
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch-icon {
|
.tool {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 22px;
|
top: 22px;
|
||||||
right: 0;
|
right: 0;
|
||||||
color: #ccc;
|
}
|
||||||
|
|
||||||
|
.switch-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.5ms linear;
|
transition: all 0.5ms linear;
|
||||||
|
background-color: #252a2f99;
|
||||||
|
color: #ddd;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 5px 8px 8px;
|
||||||
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topo-svg {
|
.topo-svg {
|
||||||
|
Loading…
Reference in New Issue
Block a user