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
|
||||
* 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 = {
|
||||
variable: "$duration: Duration!",
|
||||
query: `
|
||||
|
@ -17,13 +17,11 @@
|
||||
import {
|
||||
InstanceTopology,
|
||||
EndpointTopology,
|
||||
ServiceTopology,
|
||||
GlobalTopology,
|
||||
ServicesTopology,
|
||||
} from "../fragments/topology";
|
||||
|
||||
export const getGlobalTopology = `query queryData(${GlobalTopology.variable}) {${GlobalTopology.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 getServicesTopology = `query queryData(${ServicesTopology.variable}) {${ServicesTopology.query}}`;
|
||||
|
@ -68,13 +68,13 @@ export const topologyStore = defineStore({
|
||||
setLinkClientMetrics(m: { id: string; value: unknown }[]) {
|
||||
this.linkClientMetrics = m;
|
||||
},
|
||||
async getServiceTopology() {
|
||||
const serviceId = useSelectorStore().currentService.id;
|
||||
async getServiceTopology(id: string) {
|
||||
const serviceIds = [id];
|
||||
const duration = useAppStoreWithOut().durationTime;
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("getServiceTopology")
|
||||
.query("getServicesTopology")
|
||||
.params({
|
||||
serviceId,
|
||||
serviceIds,
|
||||
duration,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
|
@ -25,13 +25,11 @@ limitations under the License. -->
|
||||
v-show="data.widget?.tips"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
:style="{ width: '100px' }"
|
||||
>
|
||||
<el-popover placement="bottom" trigger="click" :width="100">
|
||||
<template #reference>
|
||||
<Icon iconName="ellipsis_v" size="middle" class="operation" />
|
||||
<span>
|
||||
<Icon iconName="ellipsis_v" size="middle" class="operation" />
|
||||
</span>
|
||||
</template>
|
||||
<div class="tools" @click="editConfig">
|
||||
<span>{{ t("edit") }}</span>
|
||||
|
@ -22,12 +22,18 @@ limitations under the License. -->
|
||||
<div class="setting" v-show="showSetting">
|
||||
<Settings @update="updateSettings" />
|
||||
</div>
|
||||
<Icon
|
||||
@click="setConfig"
|
||||
class="switch-icon"
|
||||
size="middle"
|
||||
iconName="settings"
|
||||
/>
|
||||
<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
|
||||
@click="backToTopology"
|
||||
size="middle"
|
||||
iconName="keyboard_backspace"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="operations-list"
|
||||
v-if="topologyStore.node && topologyStore.node.isReal"
|
||||
@ -43,8 +49,8 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onBeforeUnmount, reactive } from "vue";
|
||||
// import { useI18n } from "vue-i18n";
|
||||
import { ref, onMounted, onBeforeUnmount, reactive, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import * as d3 from "d3";
|
||||
import d3tip from "d3-tip";
|
||||
import zoom from "./utils/zoom";
|
||||
@ -53,6 +59,7 @@ import nodeElement from "./utils/nodeElement";
|
||||
import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement";
|
||||
import topoLegend from "./utils/legend";
|
||||
import { Node, Call } from "@/types/topology";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { useTopologyStore } from "@/store/modules/topology";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { EntityType } from "../../data";
|
||||
@ -61,7 +68,8 @@ import { ElMessage } from "element-plus";
|
||||
import Settings from "./Settings.vue";
|
||||
|
||||
/*global Nullable */
|
||||
// const { t } = useI18n();
|
||||
const { t } = useI18n();
|
||||
const selectorStore = useSelectorStore();
|
||||
const topologyStore = useTopologyStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const height = ref<number>(document.body.clientHeight - 90);
|
||||
@ -80,7 +88,10 @@ const legend = ref<any>(null);
|
||||
const showSetting = ref<boolean>(false);
|
||||
const settings = ref<any>({});
|
||||
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 () => {
|
||||
loading.value = true;
|
||||
@ -96,6 +107,10 @@ onMounted(async () => {
|
||||
.attr("class", "topo-svg")
|
||||
.attr("height", height.value)
|
||||
.attr("width", width.value);
|
||||
await init();
|
||||
update();
|
||||
});
|
||||
async function init() {
|
||||
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.call(tip.value);
|
||||
@ -117,10 +132,10 @@ onMounted(async () => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
topologyStore.setNode(null);
|
||||
topologyStore.setLink(null);
|
||||
// showSetting.value = false;
|
||||
});
|
||||
update();
|
||||
});
|
||||
}
|
||||
function ticked() {
|
||||
link.value.attr(
|
||||
"d",
|
||||
@ -196,10 +211,7 @@ function update() {
|
||||
dragended: dragended,
|
||||
handleNodeClick: handleNodeClick,
|
||||
tipHtml: (data: Node) => {
|
||||
const nodeMetrics: string[] = settings.value.nodeMetrics;
|
||||
if (!nodeMetrics) {
|
||||
return;
|
||||
}
|
||||
const nodeMetrics: string[] = settings.value.nodeMetrics || [];
|
||||
const html = nodeMetrics.map((m) => {
|
||||
const metric =
|
||||
topologyStore.nodeMetrics[m].values.filter(
|
||||
@ -247,7 +259,13 @@ function update() {
|
||||
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;
|
||||
},
|
||||
@ -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() {
|
||||
const path = `/dashboard/${dashboardStore.layerId}/Endpoint/${topologyStore.node.id}/${settings.value.endpointDashboard}`;
|
||||
const routeUrl = router.resolve({ path });
|
||||
@ -309,11 +339,25 @@ function handleGoAlarm() {
|
||||
|
||||
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() {
|
||||
let resp;
|
||||
switch (dashboardStore.entity) {
|
||||
case EntityType[0].value:
|
||||
resp = await topologyStore.getServiceTopology();
|
||||
resp = await topologyStore.getServiceTopology(
|
||||
selectorStore.currentService.id
|
||||
);
|
||||
break;
|
||||
case EntityType[1].value:
|
||||
resp = await topologyStore.getServicesTopology();
|
||||
@ -336,7 +380,10 @@ function resize() {
|
||||
svg.value.attr("height", height.value).attr("width", width.value);
|
||||
}
|
||||
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;
|
||||
if (config.nodeDashboard) {
|
||||
items.value.push({
|
||||
@ -363,6 +410,12 @@ function updateSettings(config: any) {
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("resize", resize);
|
||||
});
|
||||
// watch(
|
||||
// () => [topologyStore.nodes, topologyStore.calls],
|
||||
// () => {
|
||||
// update();
|
||||
// }
|
||||
// );
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.micro-topo-chart {
|
||||
@ -372,7 +425,7 @@ onBeforeUnmount(() => {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
width: 350px;
|
||||
width: 360px;
|
||||
height: 700px;
|
||||
background-color: #2b3037;
|
||||
overflow: auto;
|
||||
@ -399,17 +452,25 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
span:hover {
|
||||
color: #217ef2;
|
||||
color: #409eff;
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-icon {
|
||||
.tool {
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
right: 0;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.switch-icon {
|
||||
cursor: pointer;
|
||||
transition: all 0.5ms linear;
|
||||
background-color: #252a2f99;
|
||||
color: #ddd;
|
||||
display: inline-block;
|
||||
padding: 5px 8px 8px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.topo-svg {
|
||||
|
Loading…
Reference in New Issue
Block a user