mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-07 18:52:54 +00:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0c2cfa5630 | ||
![]() |
5e6e5aa737 | ||
![]() |
a4cd265d45 | ||
![]() |
0ef6b57cae | ||
![]() |
687ae07bb0 | ||
![]() |
0775bf0034 | ||
![]() |
5c322d960f | ||
![]() |
df2d07f508 | ||
![]() |
105450071e | ||
![]() |
39b4626317 | ||
![]() |
0ea8335fee |
553
package-lock.json
generated
553
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,6 @@
|
||||
"check-components-types": "if (! git diff --quiet -U0 ./src/types); then echo 'type files are not updated correctly'; git diff -U0 ./src/types; exit 1; fi"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.8.2",
|
||||
"d3": "^7.3.0",
|
||||
"d3-flame-graph": "^4.1.3",
|
||||
"d3-tip": "^0.9.1",
|
||||
@ -72,7 +71,7 @@
|
||||
"typescript": "^5.7.3",
|
||||
"unplugin-auto-import": "^0.18.2",
|
||||
"unplugin-vue-components": "^0.27.3",
|
||||
"vite": "^6.1.0",
|
||||
"vite": "^6.3.4",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vitest": "^3.0.5",
|
||||
|
38
src/assets/icons/data_processing_engine.svg
Normal file
38
src/assets/icons/data_processing_engine.svg
Normal file
@ -0,0 +1,38 @@
|
||||
<!-- 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 width="2400" height="2400" viewBox="0 0 200 100" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<defs>
|
||||
<marker id="arrowhead" markerWidth="8" markerHeight="8" refX="2" refY="2.5" orient="auto">
|
||||
<polygon points="0 0, 3 2.5, 0 5" fill="white" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<line x1="0" y1="20" x2="42" y2="20" stroke="white" stroke-width="7" marker-end="url(#arrowhead)" />
|
||||
<line x1="0" y1="50" x2="42" y2="50" stroke="white" stroke-width="7" marker-end="url(#arrowhead)" />
|
||||
<line x1="0" y1="80" x2="42" y2="80" stroke="white" stroke-width="7" marker-end="url(#arrowhead)" />
|
||||
|
||||
|
||||
<line x1="49" y1="10" x2="139" y2="10" stroke="white" stroke-width="7" />
|
||||
<line x1="49" y1="90" x2="139" y2="90" stroke="white" stroke-width="7" />
|
||||
<line x1="49" y1="10" x2="50" y2="90" stroke="white" stroke-width="7" />
|
||||
|
||||
<ellipse cx="140" cy="50" rx="10" ry="40" fill="none" stroke="white" stroke-width="7" />
|
||||
|
||||
<line x1="147" y1="20" x2="190" y2="20" stroke="white" stroke-width="7" marker-end="url(#arrowhead)" />
|
||||
<line x1="149" y1="50" x2="190" y2="50" stroke="white" stroke-width="7" marker-end="url(#arrowhead)" />
|
||||
<line x1="147" y1="80" x2="190" y2="80" stroke="white" stroke-width="7" marker-end="url(#arrowhead)" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
75
src/graphql/base.ts
Normal file
75
src/graphql/base.ts
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Licensed to 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. Apache Software Foundation (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.
|
||||
*/
|
||||
|
||||
const Timeout = 2 * 60 * 1000;
|
||||
export let globalAbortController = new AbortController();
|
||||
export function abortRequestsAndUpdate() {
|
||||
globalAbortController.abort(`Request timeout ${Timeout}ms`);
|
||||
globalAbortController = new AbortController();
|
||||
}
|
||||
class HTTPError extends Error {
|
||||
response;
|
||||
|
||||
constructor(response: Response, detailText = "") {
|
||||
super(detailText || response.statusText);
|
||||
|
||||
this.name = "HTTPError";
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
|
||||
const BasePath = `/graphql`;
|
||||
|
||||
export async function httpQuery({
|
||||
path = "",
|
||||
method = "GET",
|
||||
json,
|
||||
headers = {},
|
||||
}: {
|
||||
path?: string;
|
||||
method: string;
|
||||
json: unknown;
|
||||
headers: Recordable;
|
||||
}) {
|
||||
const timeoutId = setTimeout(() => {
|
||||
abortRequestsAndUpdate();
|
||||
}, Timeout);
|
||||
const url = `${BasePath}${path}`;
|
||||
const response: Response = await fetch(url, {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
accept: "application/json",
|
||||
...headers,
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
signal: globalAbortController.signal,
|
||||
})
|
||||
.catch((error) => {
|
||||
throw new HTTPError(error);
|
||||
})
|
||||
.finally(() => {
|
||||
clearTimeout(timeoutId);
|
||||
});
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
console.error(new HTTPError(response));
|
||||
}
|
||||
}
|
@ -14,20 +14,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import type { AxiosResponse } from "axios";
|
||||
import axios from "axios";
|
||||
import { cancelToken } from "@/utils/cancelToken";
|
||||
import { httpQuery } from "./base";
|
||||
|
||||
async function query(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
|
||||
const res: AxiosResponse = await axios.post(
|
||||
"/graphql",
|
||||
{ query: param.queryStr, variables: { ...param.conditions } },
|
||||
{ cancelToken: cancelToken() },
|
||||
);
|
||||
if (res.data.errors) {
|
||||
res.data.errors = res.data.errors.map((e: { message: string }) => e.message).join(" ");
|
||||
async function fetchQuery(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
|
||||
const response = await httpQuery({
|
||||
method: "post",
|
||||
json: { query: param.queryStr, variables: { ...param.conditions } },
|
||||
headers: {},
|
||||
});
|
||||
if (response.errors) {
|
||||
response.errors = response.errors.map((e: { message: string }) => e.message).join(" ");
|
||||
}
|
||||
return res;
|
||||
return response;
|
||||
}
|
||||
|
||||
export default query;
|
||||
export default fetchQuery;
|
||||
|
@ -14,22 +14,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export const TypeOfMetrics = {
|
||||
variable: "$name: String!",
|
||||
query: `typeOfMetrics(name: $name)`,
|
||||
};
|
||||
|
||||
export const listMetrics = {
|
||||
variable: "$regex: String",
|
||||
query: `
|
||||
metrics: listMetrics(regex: $regex) {
|
||||
value: name
|
||||
label: name
|
||||
type
|
||||
catalog
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export const getAllTemplates = {
|
||||
query: `
|
||||
|
@ -14,9 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import type { AxiosPromise, AxiosResponse } from "axios";
|
||||
import axios from "axios";
|
||||
import { cancelToken } from "@/utils/cancelToken";
|
||||
import { httpQuery } from "./base";
|
||||
import * as app from "./query/app";
|
||||
import * as selector from "./query/selector";
|
||||
import * as dashboard from "./query/dashboard";
|
||||
@ -45,30 +43,24 @@ const query: { [key: string]: string } = {
|
||||
...asyncProfile,
|
||||
};
|
||||
class Graphql {
|
||||
private queryData = "";
|
||||
public query(queryData: string) {
|
||||
this.queryData = queryData;
|
||||
queryData = "";
|
||||
query(data: string) {
|
||||
this.queryData = data;
|
||||
return this;
|
||||
}
|
||||
public params(variablesData: unknown): AxiosPromise<void> {
|
||||
return axios
|
||||
.post(
|
||||
"/graphql",
|
||||
{
|
||||
async params(variables: unknown) {
|
||||
const response = await httpQuery({
|
||||
method: "post",
|
||||
headers: {},
|
||||
json: {
|
||||
query: query[this.queryData],
|
||||
variables: variablesData,
|
||||
variables,
|
||||
},
|
||||
{ cancelToken: cancelToken() },
|
||||
)
|
||||
.then((res: AxiosResponse) => {
|
||||
if (res.data.errors) {
|
||||
res.data.errors = res.data.errors.map((e: { message: string }) => e.message).join(" ");
|
||||
}
|
||||
return res;
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
throw err;
|
||||
});
|
||||
if (response.errors) {
|
||||
response.errors = response.errors.map((e: { message: string }) => e.message).join(" ");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,18 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
TypeOfMetrics,
|
||||
listMetrics,
|
||||
getAllTemplates,
|
||||
addTemplate,
|
||||
changeTemplate,
|
||||
deleteTemplate,
|
||||
} from "../fragments/dashboard";
|
||||
|
||||
export const queryTypeOfMetrics = `query typeOfMetrics(${TypeOfMetrics.variable}) {${TypeOfMetrics.query}}`;
|
||||
|
||||
export const queryMetrics = `query queryData(${listMetrics.variable}) {${listMetrics.query}}`;
|
||||
import { getAllTemplates, addTemplate, changeTemplate, deleteTemplate } from "../fragments/dashboard";
|
||||
|
||||
export const addNewTemplate = `mutation template(${addTemplate.variable}) {${addTemplate.query}}`;
|
||||
|
||||
|
@ -109,12 +109,12 @@ export async function useDashboardQueryProcessor(configList: Indexable[]) {
|
||||
return { source: {}, tips: [], typesOfMQE: [] };
|
||||
}
|
||||
const tips: string[] = [];
|
||||
const source: { [key: string]: unknown } = {};
|
||||
const source: Indexable<unknown> = {};
|
||||
const keys = Object.keys(resp.data);
|
||||
const typesOfMQE: string[] = [];
|
||||
|
||||
for (let i = 0; i < config.metrics.length; i++) {
|
||||
const c: MetricConfigOpt = (config.metricConfig && config.metricConfig[i]) || {};
|
||||
const metricConfig: MetricConfigOpt = (config.metricConfig && config.metricConfig[i]) || {};
|
||||
const obj = resp.data[keys[i]] || {};
|
||||
const results = obj.results || [];
|
||||
const name = config.metrics[i];
|
||||
@ -125,15 +125,15 @@ export async function useDashboardQueryProcessor(configList: Indexable[]) {
|
||||
if (!obj.error) {
|
||||
if ([ExpressionResultType.SINGLE_VALUE, ExpressionResultType.TIME_SERIES_VALUES].includes(type)) {
|
||||
for (const item of results) {
|
||||
const label =
|
||||
let label =
|
||||
item.metric &&
|
||||
item.metric.labels.map((d: { key: string; value: string }) => `${d.key}=${d.value}`).join(",");
|
||||
const values = item.values.map((d: { value: unknown }) => d.value) || [];
|
||||
if (results.length === 1) {
|
||||
source[label || c.label || name] = values;
|
||||
} else {
|
||||
source[label] = values;
|
||||
// If the metrics label does not exist, use the configuration label or expression
|
||||
label = label ? `${metricConfig.label || name}, ${label}` : metricConfig.label || name;
|
||||
}
|
||||
source[label] = values;
|
||||
}
|
||||
}
|
||||
if (([ExpressionResultType.RECORD_LIST, ExpressionResultType.SORTED_LIST] as string[]).includes(type)) {
|
||||
@ -148,7 +148,7 @@ export async function useDashboardQueryProcessor(configList: Indexable[]) {
|
||||
const appStore = useAppStoreWithOut();
|
||||
const variables: string[] = [`$duration: Duration!`];
|
||||
let fragments = "";
|
||||
let conditions: Recordable = {
|
||||
let conditions: Recordable<unknown> = {
|
||||
duration: appStore.durationTime,
|
||||
};
|
||||
for (let i = 0; i < configArr.length; i++) {
|
||||
@ -465,7 +465,7 @@ export function useQueryTopologyExpressionsProcessor(metrics: string[], instance
|
||||
return { queryStr, conditions };
|
||||
}
|
||||
function handleExpressionValues(partMetrics: string[], resp: { [key: string]: any }) {
|
||||
const obj: any = {};
|
||||
const obj: Indexable = {};
|
||||
for (let idx = 0; idx < instances.length; idx++) {
|
||||
for (let index = 0; index < partMetrics.length; index++) {
|
||||
const k = "expression" + idx + index;
|
||||
|
@ -136,6 +136,12 @@ const titles = {
|
||||
"Cilium is a CNI plugin for Kubernetes that provides eBPF-based networking, security, and load balancing.",
|
||||
cilium_service: "Cilium Service",
|
||||
cilium_service_desc: "Observe Service status and resources from Cilium Hubble.",
|
||||
data_processing_engine: "Data Processing Engine",
|
||||
data_processing_engine_desc:
|
||||
"A data processing engine is a system designed to efficiently process, transform, and analyze large-scale data in real time or batch mode.",
|
||||
data_processing_engine_flink: "Flink",
|
||||
data_processing_engine_flink_desc:
|
||||
"Apache Flink is a framework and distributed processing engine for stateful computations over unbounded and bounded data streams. Flink has been designed to run in all common cluster environments, perform computations at in-memory speed and at any scale.",
|
||||
};
|
||||
|
||||
export default titles;
|
||||
|
@ -137,6 +137,12 @@ const titles = {
|
||||
"Cilium es un complemento CNI para Kubernetes que proporciona redes, seguridad y equilibrio de carga basados en eBPF.",
|
||||
cilium_service: "Cilium Service",
|
||||
cilium_service_desc: "Observe el estado del servicio y los recursos de Cilium Hubble.",
|
||||
data_processing_engine: "Data Processing Engine",
|
||||
data_processing_engine_desc:
|
||||
"A data processing engine is a system designed to efficiently process, transform, and analyze large-scale data in real time or batch mode.",
|
||||
data_processing_engine_flink: "Flink",
|
||||
data_processing_engine_flink_desc:
|
||||
"Apache Flink is a framework and distributed processing engine for stateful computations over unbounded and bounded data streams. Flink has been designed to run in all common cluster environments, perform computations at in-memory speed and at any scale.",
|
||||
};
|
||||
|
||||
export default titles;
|
||||
|
@ -119,6 +119,11 @@ const titles = {
|
||||
cilium_desc: "Cilium是Kubernetes上的CNI插件,提供基于eBPF的网络、安全和负载均衡。",
|
||||
cilium_service: "Cilium服务",
|
||||
cilium_service_desc: "通过Cilium Hubble收集的遥测数据观察服务。",
|
||||
data_processing_engine: "数据处理引擎",
|
||||
data_processing_engine_desc: "数据处理引擎是一个用于高效地在实时或批处理模式下处理、转换和分析大规模数据的系统。",
|
||||
data_processing_engine_flink: "Flink",
|
||||
data_processing_engine_flink_desc:
|
||||
"Apache Flink 是一个框架和分布式处理引擎,用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算。",
|
||||
};
|
||||
|
||||
export default titles;
|
||||
|
@ -37,16 +37,8 @@ const router = createRouter({
|
||||
routes,
|
||||
});
|
||||
|
||||
(window as any).axiosCancel = [];
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
router.beforeEach((to, _, next) => {
|
||||
// const token = window.localStorage.getItem("skywalking-authority");
|
||||
if ((window as any).axiosCancel.length !== 0) {
|
||||
for (const func of (window as any).axiosCancel) {
|
||||
setTimeout(func(), 0);
|
||||
}
|
||||
(window as any).axiosCancel = [];
|
||||
}
|
||||
|
||||
if (to.path === "/") {
|
||||
let defaultPath = "";
|
||||
|
@ -17,7 +17,6 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import type { Alarm } from "@/types/alarm";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
|
||||
@ -37,30 +36,22 @@ export const alarmStore = defineStore({
|
||||
actions: {
|
||||
async getAlarms(params: Recordable) {
|
||||
this.loading = true;
|
||||
const res: AxiosResponse = await graphql.query("queryAlarms").params(params);
|
||||
const res = await graphql.query("queryAlarms").params(params);
|
||||
this.loading = false;
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
if (res.data.data.getAlarm.items) {
|
||||
this.alarms = res.data.data.getAlarm.items;
|
||||
this.total = res.data.data.getAlarm.total;
|
||||
if (res.data.getAlarm.items) {
|
||||
this.alarms = res.data.getAlarm.items;
|
||||
this.total = res.data.getAlarm.total;
|
||||
}
|
||||
return res.data;
|
||||
},
|
||||
async getAlarmTagKeys() {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryAlarmTagKeys")
|
||||
.params({ duration: useAppStoreWithOut().durationTime });
|
||||
|
||||
return res.data;
|
||||
return await graphql.query("queryAlarmTagKeys").params({ duration: useAppStoreWithOut().durationTime });
|
||||
},
|
||||
async getAlarmTagValues(tagKey: string) {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryAlarmTagValues")
|
||||
.params({ tagKey, duration: useAppStoreWithOut().durationTime });
|
||||
|
||||
return res.data;
|
||||
return await graphql.query("queryAlarmTagValues").params({ tagKey, duration: useAppStoreWithOut().durationTime });
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -19,7 +19,6 @@ import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { Duration, DurationTime } from "@/types/app";
|
||||
import getLocalTime from "@/utils/localtime";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import dateFormatStep, { dateFormatTime } from "@/utils/dateFormat";
|
||||
import { TimeType } from "@/constants/data";
|
||||
import type { MenuOptions, SubItem } from "@/types/app";
|
||||
@ -118,12 +117,6 @@ export const appStore = defineStore({
|
||||
actions: {
|
||||
setDuration(data: Duration): void {
|
||||
this.durationRow = data;
|
||||
if ((window as any).axiosCancel.length !== 0) {
|
||||
for (const event of (window as any).axiosCancel) {
|
||||
setTimeout(event(), 0);
|
||||
}
|
||||
(window as any).axiosCancel = [];
|
||||
}
|
||||
this.runEventStack();
|
||||
},
|
||||
updateDurationRow(data: Duration) {
|
||||
@ -185,11 +178,11 @@ export const appStore = defineStore({
|
||||
});
|
||||
},
|
||||
async queryOAPTimeInfo() {
|
||||
const res: AxiosResponse = await graphql.query("queryOAPTimeInfo").params({});
|
||||
if (res.data.errors) {
|
||||
const res = await graphql.query("queryOAPTimeInfo").params({});
|
||||
if (res.errors) {
|
||||
this.utc = -(new Date().getTimezoneOffset() / 60) + ":0";
|
||||
} else {
|
||||
this.utc = res.data.data.getTimeInfo.timezone / 100 + ":0";
|
||||
this.utc = res.data.getTimeInfo.timezone / 100 + ":0";
|
||||
}
|
||||
const utcArr = this.utc.split(":");
|
||||
this.utcHour = isNaN(Number(utcArr[0])) ? 0 : Number(utcArr[0]);
|
||||
@ -197,21 +190,21 @@ export const appStore = defineStore({
|
||||
|
||||
return res.data;
|
||||
},
|
||||
async fetchVersion(): Promise<void> {
|
||||
const res: AxiosResponse = await graphql.query("queryOAPVersion").params({});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
async fetchVersion() {
|
||||
const res = await graphql.query("queryOAPVersion").params({});
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
this.version = res.data.data.version;
|
||||
this.version = res.data.version;
|
||||
return res.data;
|
||||
},
|
||||
async queryMenuItems() {
|
||||
const res: AxiosResponse = await graphql.query("queryMenuItems").params({});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
const res = await graphql.query("queryMenuItems").params({});
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return res.data.data;
|
||||
return res.data;
|
||||
},
|
||||
setReloadTimer(timer: IntervalHandle) {
|
||||
this.reloadTimer = timer;
|
||||
|
@ -25,7 +25,6 @@ import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import type { Instance } from "@/types/selector";
|
||||
|
||||
interface AsyncProfilingState {
|
||||
@ -59,79 +58,77 @@ export const asyncProfilingStore = defineStore({
|
||||
async getTaskList() {
|
||||
const selectorStore = useSelectorStore();
|
||||
this.loadingTasks = true;
|
||||
const res: AxiosResponse = await graphql.query("getAsyncTaskList").params({
|
||||
const response = await graphql.query("getAsyncTaskList").params({
|
||||
request: {
|
||||
serviceId: selectorStore.currentService.id,
|
||||
limit: 10000,
|
||||
},
|
||||
});
|
||||
this.loadingTasks = false;
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.taskList = res.data.data.asyncTaskList.tasks || [];
|
||||
this.taskList = response.data.asyncTaskList.tasks || [];
|
||||
this.selectedTask = this.taskList[0] || {};
|
||||
this.setAnalyzeTrees([]);
|
||||
this.setSelectedTask(this.selectedTask);
|
||||
if (!this.taskList.length) {
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getTaskLogs(param: { taskID: string }) {
|
||||
const res: AxiosResponse = await graphql.query("getAsyncProfileTaskProcess").params(param);
|
||||
const response = await graphql.query("getAsyncProfileTaskProcess").params(param);
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.taskProgress = res.data.data.taskProgress;
|
||||
return res.data;
|
||||
this.taskProgress = response.data.taskProgress;
|
||||
return response;
|
||||
},
|
||||
async getServiceInstances(param: { serviceId: string; isRelation: boolean }): Promise<Nullable<AxiosResponse>> {
|
||||
async getServiceInstances(param: { serviceId: string; isRelation: boolean }) {
|
||||
if (!param.serviceId) {
|
||||
return null;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryInstances").params({
|
||||
const response = await graphql.query("queryInstances").params({
|
||||
serviceId: param.serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
this.instances = (res.data.data.pods || []).map((d: Instance) => {
|
||||
if (!response.errors) {
|
||||
this.instances = (response.data.pods || []).map((d: Instance) => {
|
||||
d.value = d.id || "";
|
||||
return d;
|
||||
});
|
||||
}
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async createTask(param: AsyncProfileTaskCreationRequest) {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("saveAsyncProfileTask")
|
||||
.params({ asyncProfilerTaskCreationRequest: param });
|
||||
const response = await graphql.query("saveAsyncProfileTask").params({ asyncProfilerTaskCreationRequest: param });
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.getTaskList();
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getAsyncProfilingAnalyze(params: { taskId: string; instanceIds: Array<string>; eventType: string }) {
|
||||
if (!params.instanceIds.length) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
this.loadingTree = true;
|
||||
const res: AxiosResponse = await graphql.query("getAsyncProfileAnalyze").params({ request: params });
|
||||
const response = await graphql.query("getAsyncProfileAnalyze").params({ request: params });
|
||||
this.loadingTree = false;
|
||||
if (res.data.errors) {
|
||||
if (response.errors) {
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
const { analysisResult } = res.data.data;
|
||||
const { analysisResult } = response.data;
|
||||
if (!analysisResult) {
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.analyzeTrees = [analysisResult.tree];
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -21,7 +21,6 @@ import type { Instance } from "@/types/selector";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { MonitorInstance, MonitorProcess } from "@/types/continous-profiling";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { dateFormat } from "@/utils/dateFormat";
|
||||
|
||||
interface ContinousProfilingState {
|
||||
@ -84,37 +83,37 @@ export const continousProfilingStore = defineStore({
|
||||
checkItems: CheckItems[];
|
||||
}[],
|
||||
) {
|
||||
const res: AxiosResponse = await graphql.query("editStrategy").params({
|
||||
const response = await graphql.query("editStrategy").params({
|
||||
request: {
|
||||
serviceId,
|
||||
targets,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getStrategyList(params: { serviceId: string }) {
|
||||
if (!params.serviceId) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
this.policyLoading = true;
|
||||
const res: AxiosResponse = await graphql.query("getStrategyList").params(params);
|
||||
const response = await graphql.query("getStrategyList").params(params);
|
||||
|
||||
this.policyLoading = false;
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
const list = res.data.data.strategyList || [];
|
||||
const list = response.data.strategyList || [];
|
||||
if (!list.length) {
|
||||
this.taskList = [];
|
||||
this.instances = [];
|
||||
this.instance = null;
|
||||
}
|
||||
const arr = list.length ? res.data.data.strategyList : [{ type: "", checkItems: [{ type: "" }] }];
|
||||
const arr = list.length ? response.data.strategyList : [{ type: "", checkItems: [{ type: "" }] }];
|
||||
this.strategyList = arr.map((d: StrategyItem, index: number) => {
|
||||
return {
|
||||
...d,
|
||||
@ -123,25 +122,25 @@ export const continousProfilingStore = defineStore({
|
||||
});
|
||||
this.setSelectedStrategy(this.strategyList[0]);
|
||||
if (!this.selectedStrategy.type) {
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.getMonitoringInstances(params.serviceId);
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getMonitoringInstances(serviceId: string): Promise<Nullable<AxiosResponse>> {
|
||||
async getMonitoringInstances(serviceId: string) {
|
||||
this.instancesLoading = true;
|
||||
if (!serviceId) {
|
||||
return null;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("getMonitoringInstances").params({
|
||||
const response = await graphql.query("getMonitoringInstances").params({
|
||||
serviceId,
|
||||
target: this.selectedStrategy.type,
|
||||
});
|
||||
this.instancesLoading = false;
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.instances = (res.data.data.instances || [])
|
||||
this.instances = (response.data.instances || [])
|
||||
.map((d: MonitorInstance) => {
|
||||
const processes = (d.processes || [])
|
||||
.sort((c: MonitorProcess, d: MonitorProcess) => d.lastTriggerTimestamp - c.lastTriggerTimestamp)
|
||||
@ -161,7 +160,7 @@ export const continousProfilingStore = defineStore({
|
||||
})
|
||||
.sort((a: MonitorInstance, b: MonitorInstance) => b.lastTriggerTimestamp - a.lastTriggerTimestamp);
|
||||
this.instance = this.instances[0] || null;
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -18,11 +18,10 @@ import { defineStore } from "pinia";
|
||||
import { store } from "@/store";
|
||||
import type { LayoutConfig } from "@/types/dashboard";
|
||||
import graphql from "@/graphql";
|
||||
import query from "@/graphql/fetch";
|
||||
import fetchQuery from "@/graphql/fetch";
|
||||
import type { DashboardItem } from "@/types/dashboard";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { NewControl, TextConfig, TimeRangeConfig, ControlsTypes } from "../data";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { EntityType, WidgetType } from "@/views/dashboard/data";
|
||||
interface DashboardState {
|
||||
@ -299,37 +298,20 @@ export const dashboardStore = defineStore({
|
||||
}
|
||||
}
|
||||
},
|
||||
async fetchMetricType(item: string) {
|
||||
const res: AxiosResponse = await graphql.query("queryTypeOfMetrics").params({ name: item });
|
||||
|
||||
return res.data;
|
||||
},
|
||||
async getTypeOfMQE(expression: string) {
|
||||
const res: AxiosResponse = await graphql.query("getTypeOfMQE").params({ expression });
|
||||
|
||||
return res.data;
|
||||
},
|
||||
async fetchMetricList(regex: string) {
|
||||
const res: AxiosResponse = await graphql.query("queryMetrics").params({ regex });
|
||||
|
||||
return res.data;
|
||||
},
|
||||
async fetchMetricValue(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
|
||||
const res: AxiosResponse = await query(param);
|
||||
return res.data;
|
||||
return await fetchQuery(param);
|
||||
},
|
||||
async fetchTemplates() {
|
||||
const res: AxiosResponse = await graphql.query("getTemplates").params({});
|
||||
const res = await graphql.query("getTemplates").params({});
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
const data = res.data.data.getAllTemplates;
|
||||
const data = res.data.getAllTemplates;
|
||||
let list = [];
|
||||
for (const t of data) {
|
||||
const c = JSON.parse(t.configuration);
|
||||
const key = [c.layer, c.entity, c.name].join("_");
|
||||
|
||||
list.push({
|
||||
...c,
|
||||
id: t.id,
|
||||
@ -372,20 +354,20 @@ export const dashboardStore = defineStore({
|
||||
this.dashboards = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
|
||||
},
|
||||
async updateDashboard(setting: { id: string; configuration: string }) {
|
||||
const res: AxiosResponse = await graphql.query("updateTemplate").params({
|
||||
const resp = await graphql.query("updateTemplate").params({
|
||||
setting,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
ElMessage.error(res.data.errors);
|
||||
return res.data;
|
||||
if (resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
return resp;
|
||||
}
|
||||
const json = res.data.data.changeTemplate;
|
||||
const json = resp.data.changeTemplate;
|
||||
if (!json.status) {
|
||||
ElMessage.error(json.message);
|
||||
return res.data;
|
||||
return resp;
|
||||
}
|
||||
ElMessage.success("Saved successfully");
|
||||
return res.data;
|
||||
return resp;
|
||||
},
|
||||
async saveDashboard() {
|
||||
if (!this.currentDashboard?.name) {
|
||||
@ -419,14 +401,14 @@ export const dashboardStore = defineStore({
|
||||
}
|
||||
res = await graphql.query("addNewTemplate").params({ setting: { configuration: JSON.stringify(c) } });
|
||||
|
||||
json = res.data.data.addTemplate;
|
||||
json = res.data.addTemplate;
|
||||
if (!json.status) {
|
||||
ElMessage.error(json.message);
|
||||
}
|
||||
}
|
||||
if (res.data.errors || res.errors) {
|
||||
ElMessage.error(res.data.errors);
|
||||
return res.data;
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
return res;
|
||||
}
|
||||
if (!json.status) {
|
||||
return json;
|
||||
@ -448,16 +430,16 @@ export const dashboardStore = defineStore({
|
||||
return json;
|
||||
},
|
||||
async deleteDashboard() {
|
||||
const res: AxiosResponse = await graphql.query("removeTemplate").params({ id: this.currentDashboard?.id });
|
||||
const res = await graphql.query("removeTemplate").params({ id: this.currentDashboard?.id });
|
||||
|
||||
if (res.data.errors) {
|
||||
ElMessage.error(res.data.errors);
|
||||
return res.data;
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
return res;
|
||||
}
|
||||
const json = res.data.data.disableTemplate;
|
||||
const json = res.data.disableTemplate;
|
||||
if (!json.status) {
|
||||
ElMessage.error(json.message);
|
||||
return res.data;
|
||||
return res;
|
||||
}
|
||||
this.dashboards = this.dashboards.filter((d: Recordable) => d.id !== this.currentDashboard?.id);
|
||||
const key = [this.currentDashboard?.layer, this.currentDashboard?.entity, this.currentDashboard?.name].join("_");
|
||||
|
@ -18,7 +18,6 @@ import { defineStore } from "pinia";
|
||||
import type { Instance } from "@/types/selector";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import type { Conditions, Log } from "@/types/demand-log";
|
||||
@ -60,16 +59,16 @@ export const demandLogStore = defineStore({
|
||||
},
|
||||
async getInstances(id: string) {
|
||||
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
|
||||
const res: AxiosResponse = await graphql.query("queryInstances").params({
|
||||
const response = await graphql.query("queryInstances").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
});
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.instances = res.data.data.pods || [];
|
||||
return res.data;
|
||||
this.instances = response.data.pods || [];
|
||||
return response;
|
||||
},
|
||||
async getContainers(serviceInstanceId: string) {
|
||||
if (!serviceInstanceId) {
|
||||
@ -78,35 +77,35 @@ export const demandLogStore = defineStore({
|
||||
const condition = {
|
||||
serviceInstanceId,
|
||||
};
|
||||
const res: AxiosResponse = await graphql.query("fetchContainers").params({ condition });
|
||||
const response = await graphql.query("fetchContainers").params({ condition });
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
if (res.data.data.containers.errorReason) {
|
||||
if (response.data.containers.errorReason) {
|
||||
this.containers = [{ label: "", value: "" }];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.containers = res.data.data.containers.containers.map((d: string) => {
|
||||
this.containers = response.data.containers.containers.map((d: string) => {
|
||||
return { label: d, value: d };
|
||||
});
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getDemandLogs() {
|
||||
this.loadLogs = true;
|
||||
const res: AxiosResponse = await graphql.query("fetchDemandPodLogs").params({ condition: this.conditions });
|
||||
const response = await graphql.query("fetchDemandPodLogs").params({ condition: this.conditions });
|
||||
this.loadLogs = false;
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
if (res.data.data.logs.errorReason) {
|
||||
this.setLogs([], res.data.data.logs.errorReason);
|
||||
return res.data;
|
||||
if (response.data.logs.errorReason) {
|
||||
this.setLogs([], response.data.logs.errorReason);
|
||||
return response;
|
||||
}
|
||||
this.total = res.data.data.logs.logs.length;
|
||||
const logs = res.data.data.logs.logs.map((d: Log) => d.content).join("\n");
|
||||
this.total = response.data.logs.logs.length;
|
||||
const logs = response.data.logs.logs.map((d: Log) => d.content).join("\n");
|
||||
this.setLogs(logs);
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -19,7 +19,6 @@ import type { Option } from "@/types/app";
|
||||
import type { EBPFTaskCreationRequest, EBPFProfilingSchedule, EBPFTaskList, AnalyzationTrees } from "@/types/ebpf";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { EBPFProfilingTriggerType } from "../data";
|
||||
interface EbpfState {
|
||||
taskList: Array<Recordable<EBPFTaskList>>;
|
||||
@ -57,70 +56,70 @@ export const ebpfStore = defineStore({
|
||||
this.analyzeTrees = tree;
|
||||
},
|
||||
async getCreateTaskData(serviceId: string) {
|
||||
const res: AxiosResponse = await graphql.query("getCreateTaskData").params({ serviceId });
|
||||
const response = await graphql.query("getCreateTaskData").params({ serviceId });
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
const json = res.data.data.createTaskData;
|
||||
const json = response.data.createTaskData;
|
||||
this.couldProfiling = json.couldProfiling || false;
|
||||
this.labels = json.processLabels.map((d: string) => {
|
||||
return { label: d, value: d };
|
||||
});
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async createTask(param: EBPFTaskCreationRequest) {
|
||||
const res: AxiosResponse = await graphql.query("saveEBPFTask").params({ request: param });
|
||||
const response = await graphql.query("saveEBPFTask").params({ request: param });
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.getTaskList({
|
||||
serviceId: param.serviceId,
|
||||
targets: ["ON_CPU", "OFF_CPU"],
|
||||
triggerType: EBPFProfilingTriggerType.FIXED_TIME,
|
||||
});
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getTaskList(params: { serviceId: string; targets: string[] }) {
|
||||
if (!params.serviceId) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("getEBPFTasks").params(params);
|
||||
const response = await graphql.query("getEBPFTasks").params(params);
|
||||
|
||||
this.ebpfTips = "";
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.taskList = res.data.data.queryEBPFTasks || [];
|
||||
this.taskList = response.data.queryEBPFTasks || [];
|
||||
this.selectedTask = this.taskList[0] || {};
|
||||
this.setSelectedTask(this.selectedTask);
|
||||
if (!this.taskList.length) {
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.getEBPFSchedules({ taskId: String(this.taskList[0].taskId) });
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getEBPFSchedules(params: { taskId: string }) {
|
||||
if (!params.taskId) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
|
||||
const res: AxiosResponse = await graphql.query("getEBPFSchedules").params({ ...params });
|
||||
const response = await graphql.query("getEBPFSchedules").params({ ...params });
|
||||
|
||||
if (res.data.errors) {
|
||||
if (response.errors) {
|
||||
this.eBPFSchedules = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.ebpfTips = "";
|
||||
const { eBPFSchedules } = res.data.data;
|
||||
const { eBPFSchedules } = response.data;
|
||||
|
||||
this.eBPFSchedules = eBPFSchedules;
|
||||
if (!eBPFSchedules.length) {
|
||||
this.eBPFSchedules = [];
|
||||
this.analyzeTrees = [];
|
||||
}
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getEBPFAnalyze(params: {
|
||||
scheduleIdList: string[];
|
||||
@ -134,24 +133,24 @@ export const ebpfStore = defineStore({
|
||||
if (!params.timeRanges.length) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("getEBPFResult").params(params);
|
||||
const response = await graphql.query("getEBPFResult").params(params);
|
||||
|
||||
if (res.data.errors) {
|
||||
if (response.errors) {
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
const { analysisEBPFResult } = res.data.data;
|
||||
const { analysisEBPFResult } = response.data;
|
||||
this.ebpfTips = analysisEBPFResult.tip;
|
||||
if (!analysisEBPFResult) {
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
if (analysisEBPFResult.tip) {
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.analyzeTrees = analysisEBPFResult.trees;
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -17,7 +17,6 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import type { Event, QueryEventCondition } from "@/types/events";
|
||||
import type { Instance, Endpoint } from "@/types/selector";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
@ -47,48 +46,48 @@ export const eventStore = defineStore({
|
||||
},
|
||||
async getInstances() {
|
||||
const serviceId = useSelectorStore().currentService ? useSelectorStore().currentService.id : "";
|
||||
const res: AxiosResponse = await graphql.query("queryInstances").params({
|
||||
const response = await graphql.query("queryInstances").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
});
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.instances = [{ value: "", label: "All" }, ...res.data.data.pods];
|
||||
return res.data;
|
||||
this.instances = [{ value: "", label: "All" }, ...response.data.pods];
|
||||
return response;
|
||||
},
|
||||
async getEndpoints(keyword: string) {
|
||||
const serviceId = useSelectorStore().currentService ? useSelectorStore().currentService.id : "";
|
||||
if (!serviceId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
|
||||
const response = await graphql.query("queryEndpoints").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
keyword: keyword || "",
|
||||
limit: EndpointsTopNDefault,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.endpoints = [{ value: "", label: "All" }, ...res.data.data.pods];
|
||||
return res.data;
|
||||
this.endpoints = [{ value: "", label: "All" }, ...response.data.pods];
|
||||
return response;
|
||||
},
|
||||
async getEvents() {
|
||||
this.loading = true;
|
||||
const res: AxiosResponse = await graphql.query("queryEvents").params({
|
||||
const response = await graphql.query("queryEvents").params({
|
||||
condition: {
|
||||
...this.condition,
|
||||
time: useAppStoreWithOut().durationTime,
|
||||
},
|
||||
});
|
||||
this.loading = false;
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
if (res.data.data.fetchEvents) {
|
||||
this.events = (res.data.data.fetchEvents.events || []).map((item: Event) => {
|
||||
if (response.data.fetchEvents) {
|
||||
this.events = (response.data.fetchEvents.events || []).map((item: Event) => {
|
||||
let scope = "Service";
|
||||
if (item.source.serviceInstance) {
|
||||
scope = "ServiceInstance";
|
||||
@ -103,7 +102,7 @@ export const eventStore = defineStore({
|
||||
return item;
|
||||
});
|
||||
}
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -18,7 +18,6 @@ import { defineStore } from "pinia";
|
||||
import type { Instance, Endpoint, Service } from "@/types/selector";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
@ -62,51 +61,51 @@ export const logStore = defineStore({
|
||||
};
|
||||
},
|
||||
async getServices(layer: string) {
|
||||
const res: AxiosResponse = await graphql.query("queryServices").params({
|
||||
const response = await graphql.query("queryServices").params({
|
||||
layer,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.services = res.data.data.services;
|
||||
return res.data;
|
||||
this.services = response.data.services;
|
||||
return response;
|
||||
},
|
||||
async getInstances(id: string) {
|
||||
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
|
||||
const res: AxiosResponse = await graphql.query("queryInstances").params({
|
||||
const response = await graphql.query("queryInstances").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
});
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.instances = [{ value: "0", label: "All" }, ...res.data.data.pods];
|
||||
return res.data;
|
||||
this.instances = [{ value: "0", label: "All" }, ...response.data.pods];
|
||||
return response;
|
||||
},
|
||||
async getEndpoints(id: string, keyword?: string) {
|
||||
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
|
||||
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
|
||||
const response = await graphql.query("queryEndpoints").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
keyword: keyword || "",
|
||||
limit: EndpointsTopNDefault,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.endpoints = [{ value: "0", label: "All" }, ...res.data.data.pods];
|
||||
return res.data;
|
||||
this.endpoints = [{ value: "0", label: "All" }, ...response.data.pods];
|
||||
return response;
|
||||
},
|
||||
async getLogsByKeywords() {
|
||||
const res: AxiosResponse = await graphql.query("queryLogsByKeywords").params({});
|
||||
const response = await graphql.query("queryLogsByKeywords").params({});
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
|
||||
this.supportQueryLogsByKeywords = res.data.data.support;
|
||||
return res.data;
|
||||
this.supportQueryLogsByKeywords = response.data.support;
|
||||
return response;
|
||||
},
|
||||
async getLogs() {
|
||||
const dashboardStore = useDashboardStore();
|
||||
@ -117,39 +116,31 @@ export const logStore = defineStore({
|
||||
},
|
||||
async getServiceLogs() {
|
||||
this.loadLogs = true;
|
||||
const res: AxiosResponse = await graphql.query("queryServiceLogs").params({ condition: this.conditions });
|
||||
const response = await graphql.query("queryServiceLogs").params({ condition: this.conditions });
|
||||
this.loadLogs = false;
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
|
||||
this.logs = res.data.data.queryLogs.logs;
|
||||
return res.data;
|
||||
this.logs = response.data.queryLogs.logs;
|
||||
return response;
|
||||
},
|
||||
async getBrowserLogs() {
|
||||
this.loadLogs = true;
|
||||
const res: AxiosResponse = await graphql.query("queryBrowserErrorLogs").params({ condition: this.conditions });
|
||||
const response = await graphql.query("queryBrowserErrorLogs").params({ condition: this.conditions });
|
||||
|
||||
this.loadLogs = false;
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.logs = res.data.data.queryBrowserErrorLogs.logs;
|
||||
return res.data;
|
||||
this.logs = response.data.queryBrowserErrorLogs.logs;
|
||||
return response;
|
||||
},
|
||||
async getLogTagKeys() {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryLogTagKeys")
|
||||
.params({ duration: useAppStoreWithOut().durationTime });
|
||||
|
||||
return res.data;
|
||||
return await graphql.query("queryLogTagKeys").params({ duration: useAppStoreWithOut().durationTime });
|
||||
},
|
||||
async getLogTagValues(tagKey: string) {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryLogTagValues")
|
||||
.params({ tagKey, duration: useAppStoreWithOut().durationTime });
|
||||
|
||||
return res.data;
|
||||
return await graphql.query("queryLogTagValues").params({ tagKey, duration: useAppStoreWithOut().durationTime });
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -18,7 +18,6 @@ import { defineStore } from "pinia";
|
||||
import type { EBPFTaskList, ProcessNode } from "@/types/ebpf";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import type { Call } from "@/types/topology";
|
||||
import type { LayoutConfig } from "@/types/dashboard";
|
||||
import { ElMessage } from "element-plus";
|
||||
@ -126,65 +125,65 @@ export const networkProfilingStore = defineStore({
|
||||
minDuration: number;
|
||||
}[],
|
||||
) {
|
||||
const res: AxiosResponse = await graphql.query("newNetworkProfiling").params({
|
||||
const response = await graphql.query("newNetworkProfiling").params({
|
||||
request: {
|
||||
instanceId,
|
||||
samplings: params,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getTaskList(params: { serviceId: string; serviceInstanceId: string; targets: string[] }) {
|
||||
if (!params.serviceId) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("getEBPFTasks").params(params);
|
||||
const response = await graphql.query("getEBPFTasks").params(params);
|
||||
|
||||
this.networkTip = "";
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.networkTasks = res.data.data.queryEBPFTasks || [];
|
||||
this.networkTasks = response.data.queryEBPFTasks || [];
|
||||
this.selectedNetworkTask = this.networkTasks[0] || {};
|
||||
this.setSelectedNetworkTask(this.selectedNetworkTask);
|
||||
if (!this.networkTasks.length) {
|
||||
this.nodes = [];
|
||||
this.calls = [];
|
||||
}
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async keepNetworkProfiling(taskId: string) {
|
||||
if (!taskId) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("aliveNetworkProfiling").params({ taskId });
|
||||
const response = await graphql.query("aliveNetworkProfiling").params({ taskId });
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.aliveNetwork = res.data.data.keepEBPFNetworkProfiling.status;
|
||||
this.aliveNetwork = response.data.keepEBPFNetworkProfiling.status;
|
||||
if (!this.aliveNetwork) {
|
||||
ElMessage.warning(res.data.data.keepEBPFNetworkProfiling.errorReason);
|
||||
ElMessage.warning(response.data.keepEBPFNetworkProfiling.errorReason);
|
||||
}
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getProcessTopology(params: { duration: DurationTime; serviceInstanceId: string }) {
|
||||
this.loadNodes = true;
|
||||
const res: AxiosResponse = await graphql.query("getProcessTopology").params(params);
|
||||
const response = await graphql.query("getProcessTopology").params(params);
|
||||
this.loadNodes = false;
|
||||
if (res.data.errors) {
|
||||
if (response.errors) {
|
||||
this.nodes = [];
|
||||
this.calls = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
const { topology } = res.data.data;
|
||||
const { topology } = response.data;
|
||||
|
||||
this.setTopology(topology);
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -26,7 +26,6 @@ import type {
|
||||
import type { Trace } from "@/types/trace";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { EndpointsTopNDefault } from "../data";
|
||||
|
||||
@ -94,38 +93,38 @@ export const profileStore = defineStore({
|
||||
this.highlightTop = !this.highlightTop;
|
||||
},
|
||||
async getEndpoints(serviceId: string, keyword?: string) {
|
||||
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
|
||||
const response = await graphql.query("queryEndpoints").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
keyword: keyword || "",
|
||||
limit: EndpointsTopNDefault,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.endpoints = res.data.data.pods || [];
|
||||
return res.data;
|
||||
this.endpoints = response.data.pods || [];
|
||||
return response.data;
|
||||
},
|
||||
async getTaskEndpoints(serviceId: string, keyword?: string) {
|
||||
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
|
||||
const response = await graphql.query("queryEndpoints").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
keyword: keyword || "",
|
||||
limit: EndpointsTopNDefault,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.taskEndpoints = [{ value: "", label: "All" }, ...res.data.data.pods];
|
||||
return res.data;
|
||||
this.taskEndpoints = [{ value: "", label: "All" }, ...response.data.pods];
|
||||
return response;
|
||||
},
|
||||
async getTaskList() {
|
||||
const res: AxiosResponse = await graphql.query("getProfileTaskList").params(this.condition);
|
||||
const response = await graphql.query("getProfileTaskList").params(this.condition);
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
const list = res.data.data.taskList || [];
|
||||
const list = response.data.taskList || [];
|
||||
this.taskList = list;
|
||||
this.currentTask = list[0] || {};
|
||||
if (!list.length) {
|
||||
@ -133,29 +132,29 @@ export const profileStore = defineStore({
|
||||
this.segmentSpans = [];
|
||||
this.analyzeTrees = [];
|
||||
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.getSegmentList({ taskID: list[0].id });
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getSegmentList(params: { taskID: string }) {
|
||||
if (!params.taskID) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("getProfileTaskSegmentList").params(params);
|
||||
const response = await graphql.query("getProfileTaskSegmentList").params(params);
|
||||
|
||||
if (res.data.errors) {
|
||||
if (response.errors) {
|
||||
this.segmentList = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
const { segmentList } = res.data.data;
|
||||
const { segmentList } = response.data;
|
||||
|
||||
this.segmentList = segmentList || [];
|
||||
if (!segmentList.length) {
|
||||
this.segmentSpans = [];
|
||||
this.analyzeTrees = [];
|
||||
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
if (segmentList[0]) {
|
||||
this.setCurrentSegment(segmentList[0]);
|
||||
@ -163,22 +162,22 @@ export const profileStore = defineStore({
|
||||
} else {
|
||||
this.setCurrentSegment({});
|
||||
}
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getSegmentSpans(params: { segmentId: string }) {
|
||||
if (!(params && params.segmentId)) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryProfileSegment").params(params);
|
||||
if (res.data.errors) {
|
||||
const response = await graphql.query("queryProfileSegment").params(params);
|
||||
if (response.errors) {
|
||||
this.segmentSpans = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
const { segment } = res.data.data;
|
||||
const { segment } = response.data;
|
||||
if (!segment) {
|
||||
this.segmentSpans = [];
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.segmentSpans = segment.spans.map((d: SegmentSpan) => {
|
||||
return {
|
||||
@ -189,52 +188,52 @@ export const profileStore = defineStore({
|
||||
});
|
||||
if (!(segment.spans && segment.spans.length)) {
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
const index = segment.spans.length - 1 || 0;
|
||||
this.currentSpan = segment.spans[index];
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getProfileAnalyze(params: Array<{ segmentId: string; timeRange: { start: number; end: number } }>) {
|
||||
if (!params.length) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("getProfileAnalyze").params({ queries: params });
|
||||
const response = await graphql.query("getProfileAnalyze").params({ queries: params });
|
||||
|
||||
if (res.data.errors) {
|
||||
if (response.errors) {
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
const { analyze, tip } = res.data.data;
|
||||
const { analyze, tip } = response.data;
|
||||
if (tip) {
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
|
||||
if (!analyze) {
|
||||
this.analyzeTrees = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.analyzeTrees = analyze.trees;
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async createTask(param: ProfileTaskCreationRequest) {
|
||||
const res: AxiosResponse = await graphql.query("saveProfileTask").params({ creationRequest: param });
|
||||
const response = await graphql.query("saveProfileTask").params({ creationRequest: param });
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.getTaskList();
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getTaskLogs(param: { taskID: string }) {
|
||||
const res: AxiosResponse = await graphql.query("getProfileTaskLogs").params(param);
|
||||
const response = await graphql.query("getProfileTaskLogs").params(param);
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.taskLogs = res.data.data.taskLogs;
|
||||
return res.data;
|
||||
this.taskLogs = response.data.taskLogs;
|
||||
return response;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -18,7 +18,6 @@ import { defineStore } from "pinia";
|
||||
import type { Service, Instance, Endpoint, Process } from "@/types/selector";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { EndpointsTopNDefault } from "../data";
|
||||
interface SelectorState {
|
||||
@ -77,62 +76,55 @@ export const selectorStore = defineStore({
|
||||
setDestProcesses(processes: Array<Process>) {
|
||||
this.destProcesses = processes;
|
||||
},
|
||||
async fetchLayers(): Promise<AxiosResponse> {
|
||||
const res: AxiosResponse = await graphql.query("queryLayers").params({});
|
||||
|
||||
return res.data || {};
|
||||
async fetchLayers() {
|
||||
return await graphql.query("queryLayers").params({});
|
||||
},
|
||||
async fetchServices(layer: string): Promise<AxiosResponse> {
|
||||
const res: AxiosResponse = await graphql.query("queryServices").params({ layer });
|
||||
async fetchServices(layer: string) {
|
||||
const res = await graphql.query("queryServices").params({ layer });
|
||||
|
||||
if (!res.data.errors) {
|
||||
this.services = res.data.data.services || [];
|
||||
this.destServices = res.data.data.services || [];
|
||||
if (!res.errors) {
|
||||
this.services = res.data.services || [];
|
||||
this.destServices = res.data.services || [];
|
||||
}
|
||||
return res.data;
|
||||
},
|
||||
async getServiceInstances(param?: { serviceId: string; isRelation: boolean }): Promise<Nullable<AxiosResponse>> {
|
||||
async getServiceInstances(param?: { serviceId: string; isRelation: boolean }) {
|
||||
const serviceId = param ? param.serviceId : this.currentService?.id;
|
||||
if (!serviceId) {
|
||||
return null;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryInstances").params({
|
||||
const resp = await graphql.query("queryInstances").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
if (!resp.errors) {
|
||||
if (param && param.isRelation) {
|
||||
this.destPods = res.data.data.pods || [];
|
||||
return res.data;
|
||||
this.destPods = resp.data.pods || [];
|
||||
return resp;
|
||||
}
|
||||
this.pods = res.data.data.pods || [];
|
||||
this.pods = resp.data.pods || [];
|
||||
}
|
||||
return res.data;
|
||||
return resp;
|
||||
},
|
||||
async getProcesses(param?: { instanceId: string; isRelation: boolean }): Promise<Nullable<AxiosResponse>> {
|
||||
async getProcesses(param?: { instanceId: string; isRelation: boolean }) {
|
||||
const instanceId = param ? param.instanceId : this.currentPod?.id;
|
||||
if (!instanceId) {
|
||||
return null;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryProcesses").params({
|
||||
const res = await graphql.query("queryProcesses").params({
|
||||
instanceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
if (!res.errors) {
|
||||
if (param && param.isRelation) {
|
||||
this.destProcesses = res.data.data.processes || [];
|
||||
return res.data;
|
||||
this.destProcesses = res.data.processes || [];
|
||||
return res;
|
||||
}
|
||||
this.processes = res.data.data.processes || [];
|
||||
this.processes = res.data.processes || [];
|
||||
}
|
||||
return res.data;
|
||||
return res;
|
||||
},
|
||||
async getEndpoints(params: {
|
||||
keyword?: string;
|
||||
serviceId?: string;
|
||||
isRelation?: boolean;
|
||||
limit?: number;
|
||||
}): Promise<Nullable<AxiosResponse>> {
|
||||
async getEndpoints(params: { keyword?: string; serviceId?: string; isRelation?: boolean; limit?: number }) {
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
@ -140,96 +132,96 @@ export const selectorStore = defineStore({
|
||||
if (!serviceId) {
|
||||
return null;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
|
||||
const res = await graphql.query("queryEndpoints").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
keyword: params.keyword || "",
|
||||
limit: params.limit || EndpointsTopNDefault,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
if (!res.errors) {
|
||||
if (params.isRelation) {
|
||||
this.destPods = res.data.data.pods || [];
|
||||
return res.data;
|
||||
this.destPods = res.data.pods || [];
|
||||
return res;
|
||||
}
|
||||
this.pods = res.data.data.pods || [];
|
||||
this.pods = res.data.pods || [];
|
||||
}
|
||||
return res.data;
|
||||
return res;
|
||||
},
|
||||
async getService(serviceId: string, isRelation: boolean) {
|
||||
if (!serviceId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryService").params({
|
||||
const res = await graphql.query("queryService").params({
|
||||
serviceId,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
if (!res.errors) {
|
||||
if (isRelation) {
|
||||
this.setCurrentDestService(res.data.data.service);
|
||||
this.destServices = [res.data.data.service];
|
||||
return res.data;
|
||||
this.setCurrentDestService(res.data.service);
|
||||
this.destServices = [res.data.service];
|
||||
return res;
|
||||
}
|
||||
this.setCurrentService(res.data.data.service);
|
||||
this.services = [res.data.data.service];
|
||||
this.setCurrentService(res.data.service);
|
||||
this.services = [res.data.service];
|
||||
}
|
||||
|
||||
return res.data;
|
||||
return res;
|
||||
},
|
||||
async getInstance(instanceId: string, isRelation?: boolean) {
|
||||
if (!instanceId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryInstance").params({
|
||||
const res = await graphql.query("queryInstance").params({
|
||||
instanceId,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
if (!res.errors) {
|
||||
if (isRelation) {
|
||||
this.currentDestPod = res.data.data.instance || null;
|
||||
this.destPods = [res.data.data.instance];
|
||||
return res.data;
|
||||
this.currentDestPod = res.data.instance || null;
|
||||
this.destPods = [res.data.instance];
|
||||
return res;
|
||||
}
|
||||
this.currentPod = res.data.data.instance || null;
|
||||
this.pods = [res.data.data.instance];
|
||||
this.currentPod = res.data.instance || null;
|
||||
this.pods = [res.data.instance];
|
||||
}
|
||||
|
||||
return res.data;
|
||||
return res;
|
||||
},
|
||||
async getEndpoint(endpointId: string, isRelation?: string) {
|
||||
if (!endpointId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryEndpoint").params({
|
||||
const res = await graphql.query("queryEndpoint").params({
|
||||
endpointId,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
if (isRelation) {
|
||||
this.currentDestPod = res.data.data.endpoint || null;
|
||||
this.destPods = [res.data.data.endpoint];
|
||||
return res.data;
|
||||
this.currentDestPod = res.data.endpoint || null;
|
||||
this.destPods = [res.data.endpoint];
|
||||
return res;
|
||||
}
|
||||
this.currentPod = res.data.data.endpoint || null;
|
||||
this.pods = [res.data.data.endpoint];
|
||||
return res.data;
|
||||
this.currentPod = res.data.endpoint || null;
|
||||
this.pods = [res.data.endpoint];
|
||||
return res;
|
||||
},
|
||||
async getProcess(processId: string, isRelation?: boolean) {
|
||||
if (!processId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryProcess").params({
|
||||
const res = await graphql.query("queryProcess").params({
|
||||
processId,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
if (!res.errors) {
|
||||
if (isRelation) {
|
||||
this.currentDestProcess = res.data.data.process || null;
|
||||
this.destProcesses = [res.data.data.process];
|
||||
this.currentDestProcess = res.data.process || null;
|
||||
this.destProcesses = [res.data.process];
|
||||
return res.data;
|
||||
}
|
||||
this.currentProcess = res.data.data.process || null;
|
||||
this.processes = [res.data.data.process];
|
||||
this.currentProcess = res.data.process || null;
|
||||
this.processes = [res.data.process];
|
||||
}
|
||||
|
||||
return res.data;
|
||||
return res;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -18,7 +18,6 @@ import { defineStore } from "pinia";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import type { EBPFTaskList } from "@/types/ebpf";
|
||||
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
|
||||
@ -57,20 +56,18 @@ export const taskTimelineStore = defineStore({
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
this.loading = true;
|
||||
const res: AxiosResponse = await graphql.query("getEBPFTasks").params(params);
|
||||
const response = await graphql.query("getEBPFTasks").params(params);
|
||||
|
||||
this.loading = false;
|
||||
this.errorTip = "";
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
const selectorStore = useSelectorStore();
|
||||
this.taskList = (res.data.data.queryEBPFTasks || []).filter(
|
||||
this.taskList = (response.data.queryEBPFTasks || []).filter(
|
||||
(d: EBPFTaskList) => selectorStore.currentProcess && d.processId === selectorStore.currentProcess.id,
|
||||
);
|
||||
// this.selectedTask = this.taskList[0] || {};
|
||||
// await this.getGraphData();
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getGraphData() {
|
||||
let res: any = {};
|
||||
|
@ -21,8 +21,7 @@ import graphql from "@/graphql";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import query from "@/graphql/fetch";
|
||||
import fetchQuery from "@/graphql/fetch";
|
||||
import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor";
|
||||
|
||||
interface MetricVal {
|
||||
@ -305,14 +304,14 @@ export const topologyStore = defineStore({
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const duration = useAppStoreWithOut().durationTime;
|
||||
const res: AxiosResponse = await graphql.query("getServicesTopology").params({
|
||||
const res = await graphql.query("getServicesTopology").params({
|
||||
serviceIds,
|
||||
duration,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
return res.data.data.topology;
|
||||
return res.data.topology;
|
||||
},
|
||||
async getInstanceTopology() {
|
||||
const { currentService, currentDestService } = useSelectorStore();
|
||||
@ -322,15 +321,15 @@ export const topologyStore = defineStore({
|
||||
if (!(serverServiceId && clientServiceId)) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("getInstanceTopology").params({
|
||||
const res = await graphql.query("getInstanceTopology").params({
|
||||
clientServiceId,
|
||||
serverServiceId,
|
||||
duration,
|
||||
});
|
||||
if (!res.data.errors) {
|
||||
this.setInstanceTopology(res.data.data.topology);
|
||||
if (!res.errors) {
|
||||
this.setInstanceTopology(res.data.topology);
|
||||
}
|
||||
return res.data;
|
||||
return res;
|
||||
},
|
||||
async updateEndpointTopology(endpointIds: string[], depth: number) {
|
||||
if (!endpointIds.length) {
|
||||
@ -432,12 +431,12 @@ export const topologyStore = defineStore({
|
||||
});
|
||||
const queryStr = `query queryData(${variables}) {${fragment}}`;
|
||||
const conditions = { duration };
|
||||
const res: AxiosResponse = await query({ queryStr, conditions });
|
||||
const res = await fetchQuery({ queryStr, conditions });
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
const topo = res.data.data;
|
||||
const topo = res.data;
|
||||
const calls = [] as Call[];
|
||||
const nodes = [] as Node[];
|
||||
for (const key of Object.keys(topo)) {
|
||||
@ -449,13 +448,13 @@ export const topologyStore = defineStore({
|
||||
return { calls, nodes };
|
||||
},
|
||||
async getTopologyExpressionValue(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
|
||||
const res: AxiosResponse = await query(param);
|
||||
const res = await fetchQuery(param);
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return res.data;
|
||||
return res;
|
||||
},
|
||||
async getLinkExpressions(expressions: string[], type: string) {
|
||||
if (!expressions.length) {
|
||||
@ -503,22 +502,20 @@ export const topologyStore = defineStore({
|
||||
if (!(id && layer)) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("getHierarchyServiceTopology")
|
||||
.params({ serviceId: id, layer: layer });
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
const res = await graphql.query("getHierarchyServiceTopology").params({ serviceId: id, layer: layer });
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
const resp = await this.getListLayerLevels();
|
||||
if (resp.errors) {
|
||||
return resp;
|
||||
}
|
||||
const levels = resp.data.levels || [];
|
||||
this.setHierarchyServiceTopology(res.data.data.hierarchyServiceTopology || {}, levels);
|
||||
return res.data;
|
||||
const levels = resp.levels || [];
|
||||
this.setHierarchyServiceTopology(res.data.hierarchyServiceTopology || {}, levels);
|
||||
return res;
|
||||
},
|
||||
async getListLayerLevels() {
|
||||
const res: AxiosResponse = await graphql.query("queryListLayerLevels").params({});
|
||||
const res = await graphql.query("queryListLayerLevels").params({});
|
||||
|
||||
return res.data;
|
||||
},
|
||||
@ -529,19 +526,19 @@ export const topologyStore = defineStore({
|
||||
if (!(currentPod && dashboardStore.layerId)) {
|
||||
return new Promise((resolve) => resolve({}));
|
||||
}
|
||||
const res: AxiosResponse = await graphql
|
||||
const res = await graphql
|
||||
.query("getHierarchyInstanceTopology")
|
||||
.params({ instanceId: currentPod.id, layer: dashboardStore.layerId });
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
const resp = await this.getListLayerLevels();
|
||||
if (resp.errors) {
|
||||
return resp;
|
||||
}
|
||||
const levels = resp.data.levels || [];
|
||||
this.setHierarchyInstanceTopology(res.data.data.hierarchyInstanceTopology || {}, levels);
|
||||
return res.data;
|
||||
const levels = resp.levels || [];
|
||||
this.setHierarchyInstanceTopology(res.data.hierarchyInstanceTopology || {}, levels);
|
||||
return res;
|
||||
},
|
||||
async queryHierarchyNodeExpressions(expressions: string[], layer: string) {
|
||||
const nodes = this.hierarchyServiceNodes.filter((n: HierarchyNode) => n.layer === layer);
|
||||
|
@ -19,7 +19,6 @@ import type { Instance, Endpoint, Service } from "@/types/selector";
|
||||
import type { Trace, Span } from "@/types/trace";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { QueryOrders } from "@/views/dashboard/data";
|
||||
@ -34,6 +33,7 @@ interface TraceState {
|
||||
conditions: Recordable;
|
||||
traceSpanLogs: Recordable[];
|
||||
selectorStore: Recordable;
|
||||
selectedSpan: Recordable<Span>;
|
||||
}
|
||||
|
||||
export const traceStore = defineStore({
|
||||
@ -45,6 +45,7 @@ export const traceStore = defineStore({
|
||||
traceList: [],
|
||||
traceSpans: [],
|
||||
currentTrace: {},
|
||||
selectedSpan: {},
|
||||
conditions: {
|
||||
queryDuration: useAppStoreWithOut().durationTime,
|
||||
traceState: "ALL",
|
||||
@ -64,6 +65,9 @@ export const traceStore = defineStore({
|
||||
setTraceSpans(spans: Span[]) {
|
||||
this.traceSpans = spans;
|
||||
},
|
||||
setSelectedSpan(span: Span) {
|
||||
this.selectedSpan = span;
|
||||
},
|
||||
resetState() {
|
||||
this.traceSpans = [];
|
||||
this.traceList = [];
|
||||
@ -76,125 +80,115 @@ export const traceStore = defineStore({
|
||||
};
|
||||
},
|
||||
async getServices(layer: string) {
|
||||
const res: AxiosResponse = await graphql.query("queryServices").params({
|
||||
const response = await graphql.query("queryServices").params({
|
||||
layer,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.services = res.data.data.services;
|
||||
return res.data;
|
||||
this.services = response.data.services;
|
||||
return response;
|
||||
},
|
||||
async getService(serviceId: string) {
|
||||
if (!serviceId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryService").params({
|
||||
const response = await graphql.query("queryService").params({
|
||||
serviceId,
|
||||
});
|
||||
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getInstance(instanceId: string) {
|
||||
if (!instanceId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryInstance").params({
|
||||
const response = await graphql.query("queryInstance").params({
|
||||
instanceId,
|
||||
});
|
||||
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getEndpoint(endpointId: string) {
|
||||
if (!endpointId) {
|
||||
return;
|
||||
}
|
||||
const res: AxiosResponse = await graphql.query("queryEndpoint").params({
|
||||
return await graphql.query("queryEndpoint").params({
|
||||
endpointId,
|
||||
});
|
||||
|
||||
return res.data;
|
||||
},
|
||||
async getInstances(id: string) {
|
||||
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
|
||||
const res: AxiosResponse = await graphql.query("queryInstances").params({
|
||||
const response = await graphql.query("queryInstances").params({
|
||||
serviceId: serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
});
|
||||
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.instances = [{ value: "0", label: "All" }, ...res.data.data.pods];
|
||||
return res.data;
|
||||
this.instances = [{ value: "0", label: "All" }, ...response.data.pods];
|
||||
return response;
|
||||
},
|
||||
async getEndpoints(id: string, keyword?: string) {
|
||||
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
|
||||
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
|
||||
const response = await graphql.query("queryEndpoints").params({
|
||||
serviceId,
|
||||
duration: useAppStoreWithOut().durationTime,
|
||||
keyword: keyword || "",
|
||||
limit: EndpointsTopNDefault,
|
||||
});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.endpoints = [{ value: "0", label: "All" }, ...res.data.data.pods];
|
||||
return res.data;
|
||||
this.endpoints = [{ value: "0", label: "All" }, ...response.data.pods];
|
||||
return response;
|
||||
},
|
||||
async getTraces() {
|
||||
const res: AxiosResponse = await graphql.query("queryTraces").params({ condition: this.conditions });
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
const response = await graphql.query("queryTraces").params({ condition: this.conditions });
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
if (!res.data.data.data.traces.length) {
|
||||
if (!response.data.data.traces.length) {
|
||||
this.traceList = [];
|
||||
this.setCurrentTrace({});
|
||||
this.setTraceSpans([]);
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.getTraceSpans({ traceId: res.data.data.data.traces[0].traceIds[0] });
|
||||
this.traceList = res.data.data.data.traces.map((d: Trace) => {
|
||||
this.getTraceSpans({ traceId: response.data.data.traces[0].traceIds[0] });
|
||||
this.traceList = response.data.data.traces.map((d: Trace) => {
|
||||
d.traceIds = d.traceIds.map((id: string) => {
|
||||
return { value: id, label: id };
|
||||
});
|
||||
return d;
|
||||
});
|
||||
this.setCurrentTrace(res.data.data.data.traces[0] || {});
|
||||
return res.data;
|
||||
this.setCurrentTrace(response.data.data.traces[0] || {});
|
||||
return response;
|
||||
},
|
||||
async getTraceSpans(params: { traceId: string }) {
|
||||
const res: AxiosResponse = await graphql.query("queryTrace").params(params);
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
const response = await graphql.query("queryTrace").params(params);
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
const data = res.data.data.trace.spans;
|
||||
const data = response.data.trace.spans;
|
||||
|
||||
this.setTraceSpans(data || []);
|
||||
return res.data;
|
||||
return response;
|
||||
},
|
||||
async getSpanLogs(params: Recordable) {
|
||||
const res: AxiosResponse = await graphql.query("queryServiceLogs").params(params);
|
||||
if (res.data.errors) {
|
||||
const response = await graphql.query("queryServiceLogs").params(params);
|
||||
if (response.errors) {
|
||||
this.traceSpanLogs = [];
|
||||
return res.data;
|
||||
return response;
|
||||
}
|
||||
this.traceSpanLogs = res.data.data.queryLogs.logs || [];
|
||||
return res.data;
|
||||
this.traceSpanLogs = response.data.queryLogs.logs || [];
|
||||
return response;
|
||||
},
|
||||
async getTagKeys() {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryTraceTagKeys")
|
||||
.params({ duration: useAppStoreWithOut().durationTime });
|
||||
|
||||
return res.data;
|
||||
return await graphql.query("queryTraceTagKeys").params({ duration: useAppStoreWithOut().durationTime });
|
||||
},
|
||||
async getTagValues(tagKey: string) {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryTraceTagValues")
|
||||
.params({ tagKey, duration: useAppStoreWithOut().durationTime });
|
||||
|
||||
return res.data;
|
||||
return await graphql.query("queryTraceTagValues").params({ tagKey, duration: useAppStoreWithOut().durationTime });
|
||||
},
|
||||
},
|
||||
});
|
||||
|
10
src/types/trace.d.ts
vendored
10
src/types/trace.d.ts
vendored
@ -48,9 +48,10 @@ export interface Span {
|
||||
logs?: log[];
|
||||
parentSegmentId?: string;
|
||||
refs?: Ref[];
|
||||
key?: string;
|
||||
}
|
||||
export type Ref = {
|
||||
type: string;
|
||||
type?: string;
|
||||
parentSegmentId: string;
|
||||
parentSpanId: number;
|
||||
traceId: string;
|
||||
@ -60,13 +61,6 @@ export interface log {
|
||||
data: Map<string, string>;
|
||||
}
|
||||
|
||||
export interface Ref {
|
||||
traceId: string;
|
||||
parentSegmentId: string;
|
||||
parentSpanId: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface StatisticsSpan {
|
||||
groupRef: StatisticsGroupRef;
|
||||
maxTime: number;
|
||||
|
@ -77,6 +77,7 @@ limitations under the License. -->
|
||||
border-bottom: 1px solid $border-color-primary;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
min-height: 350px;
|
||||
}
|
||||
|
||||
.log-header {
|
||||
|
@ -43,8 +43,7 @@ limitations under the License. -->
|
||||
<Table
|
||||
:data="profileStore.segmentSpans"
|
||||
:traceId="profileStore.currentSegment.traceId"
|
||||
:showBtnDetail="true"
|
||||
headerType="profile"
|
||||
:headerType="WidgetType.Profile"
|
||||
@select="selectSpan"
|
||||
/>
|
||||
</div>
|
||||
@ -53,13 +52,14 @@ limitations under the License. -->
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import Table from "../../trace/components/Table/Index.vue";
|
||||
import Table from "../../trace/components/Table.vue";
|
||||
import { useProfileStore } from "@/store/modules/profile";
|
||||
import Selector from "@/components/Selector.vue";
|
||||
import type { Span } from "@/types/trace";
|
||||
import type { Option } from "@/types/app";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { ProfileDataMode, ProfileDisplayMode } from "./data";
|
||||
import { WidgetType } from "@/views/dashboard/data";
|
||||
|
||||
/* global defineEmits*/
|
||||
const emits = defineEmits(["loading", "displayMode"]);
|
||||
|
@ -148,7 +148,7 @@ limitations under the License. -->
|
||||
</div>
|
||||
<div class="label">Unhealthy Description</div>
|
||||
<el-input v-model="description.unhealthy" placeholder="Please input description" size="small" class="mt-5" />
|
||||
<el-button @click="setLegend" class="mt-20" size="small" type="primary">
|
||||
<el-button @click="setLegend" class="mt-20 mb-20" size="small" type="primary">
|
||||
{{ t("setLegend") }}
|
||||
</el-button>
|
||||
</div>
|
||||
@ -208,11 +208,6 @@ limitations under the License. -->
|
||||
getDashboardList();
|
||||
async function getDashboardList() {
|
||||
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
|
||||
const json = await dashboardStore.fetchMetricList();
|
||||
if (json.errors) {
|
||||
ElMessage.error(json.errors);
|
||||
return;
|
||||
}
|
||||
const entity =
|
||||
dashboardStore.entity === EntityType[1].value
|
||||
? EntityType[0].value
|
||||
|
@ -84,7 +84,7 @@ limitations under the License. -->
|
||||
:data="traceStore.traceSpans"
|
||||
:traceId="traceStore.currentTrace.traceIds[0].value"
|
||||
:showBtnDetail="false"
|
||||
HeaderType="trace"
|
||||
:headerType="WidgetType.Trace"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -160,6 +160,7 @@ limitations under the License. -->
|
||||
appStore,
|
||||
loading,
|
||||
traceId,
|
||||
WidgetType,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -14,8 +14,35 @@ limitations under the License. -->
|
||||
<div class="trace-t-loading" v-show="loading">
|
||||
<Icon iconName="spinner" size="sm" />
|
||||
</div>
|
||||
<div ref="traceGraph" class="d3-graph"></div>
|
||||
<el-dialog v-model="showDetail" :destroy-on-close="true" fullscreen @closed="showDetail = false">
|
||||
<TableContainer
|
||||
v-if="type === TraceGraphType.TABLE"
|
||||
:tableData="segmentId"
|
||||
:type="type"
|
||||
:headerType="headerType"
|
||||
:traceId="traceId"
|
||||
@select="handleSelectSpan"
|
||||
>
|
||||
<div class="trace-tips" v-if="!segmentId.length">{{ $t("noData") }}</div>
|
||||
</TableContainer>
|
||||
<div v-else ref="traceGraph" class="d3-graph"></div>
|
||||
<div id="trace-action-box">
|
||||
<div @click="viewSpanDetails">Span details</div>
|
||||
<div v-for="span in parentSpans" :key="span.segmentId" @click="viewParentSpan(span)">
|
||||
{{ `Parent span: ${span.endpointName} -> Start time: ${visDate(span.startTime)}` }}
|
||||
</div>
|
||||
<div v-for="span in refParentSpans" :key="span.segmentId" @click="viewParentSpan(span)">
|
||||
{{ `Ref to span: ${span.endpointName} -> Start time: ${visDate(span.startTime)}` }}
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="showDetail"
|
||||
width="60%"
|
||||
center
|
||||
align-center
|
||||
:destroy-on-close="true"
|
||||
@closed="showDetail = false"
|
||||
v-if="currentSpan?.segmentId"
|
||||
>
|
||||
<SpanDetail :currentSpan="currentSpan" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
@ -23,70 +50,138 @@ limitations under the License. -->
|
||||
import { ref, watch, onBeforeUnmount, onMounted } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import * as d3 from "d3";
|
||||
import dayjs from "dayjs";
|
||||
import ListGraph from "../../utils/d3-trace-list";
|
||||
import TreeGraph from "../../utils/d3-trace-tree";
|
||||
import type { Span, Ref } from "@/types/trace";
|
||||
import SpanDetail from "./SpanDetail.vue";
|
||||
import TableContainer from "../Table/TableContainer.vue";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { debounce } from "@/utils/debounce";
|
||||
import { mutationObserver } from "@/utils/mutation";
|
||||
import { TraceGraphType } from "../constant";
|
||||
import { Themes } from "@/constants/data";
|
||||
|
||||
/* global defineProps, Nullable, defineExpose, Recordable */
|
||||
/* global Recordable, Nullable */
|
||||
const props = defineProps({
|
||||
data: { type: Array as PropType<Span[]>, default: () => [] },
|
||||
traceId: { type: String, default: "" },
|
||||
type: { type: String, default: "List" },
|
||||
type: { type: String, default: TraceGraphType.LIST },
|
||||
headerType: { type: String, default: "" },
|
||||
});
|
||||
const appStore = useAppStoreWithOut();
|
||||
const loading = ref<boolean>(false);
|
||||
const showDetail = ref<boolean>(false);
|
||||
const fixSpansSize = ref<number>(0);
|
||||
const segmentId = ref<Recordable[]>([]);
|
||||
const currentSpan = ref<Array<Span>>([]);
|
||||
const currentSpan = ref<Nullable<Span>>(null);
|
||||
const refSpans = ref<Array<Ref>>([]);
|
||||
const tree = ref<Nullable<any>>(null);
|
||||
const traceGraph = ref<Nullable<HTMLDivElement>>(null);
|
||||
const parentSpans = ref<Array<Span>>([]);
|
||||
const refParentSpans = ref<Array<Span>>([]);
|
||||
const debounceFunc = debounce(draw, 500);
|
||||
const visDate = (date: number, pattern = "YYYY-MM-DD HH:mm:ss:SSS") => dayjs(date).format(pattern);
|
||||
|
||||
defineExpose({
|
||||
tree,
|
||||
});
|
||||
onMounted(() => {
|
||||
loading.value = true;
|
||||
changeTree();
|
||||
if (!traceGraph.value) {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
draw();
|
||||
loading.value = false;
|
||||
|
||||
// monitor segment list width changes.
|
||||
mutationObserver.create("trigger-resize", () => {
|
||||
d3.selectAll(".d3-tip").remove();
|
||||
debounceFunc();
|
||||
});
|
||||
|
||||
window.addEventListener("resize", debounceFunc);
|
||||
});
|
||||
|
||||
function draw() {
|
||||
if (props.type === TraceGraphType.TABLE) {
|
||||
segmentId.value = setLevel(segmentId.value);
|
||||
return;
|
||||
}
|
||||
if (!traceGraph.value) {
|
||||
return;
|
||||
}
|
||||
d3.selectAll(".d3-tip").remove();
|
||||
if (props.type === "List") {
|
||||
if (props.type === TraceGraphType.LIST) {
|
||||
tree.value = new ListGraph(traceGraph.value, handleSelectSpan);
|
||||
tree.value.init({ label: "TRACE_ROOT", children: segmentId.value }, props.data, fixSpansSize.value);
|
||||
tree.value.init(
|
||||
{ label: "TRACE_ROOT", children: segmentId.value },
|
||||
getRefsAllNodes({ label: "TRACE_ROOT", children: segmentId.value }),
|
||||
fixSpansSize.value,
|
||||
);
|
||||
tree.value.draw();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (props.type === TraceGraphType.TREE) {
|
||||
tree.value = new TreeGraph(traceGraph.value, handleSelectSpan);
|
||||
tree.value.init({ label: `${props.traceId}`, children: segmentId.value }, props.data);
|
||||
tree.value.init(
|
||||
{ label: `${props.traceId}`, children: segmentId.value },
|
||||
getRefsAllNodes({ label: "TRACE_ROOT", children: segmentId.value }),
|
||||
);
|
||||
}
|
||||
}
|
||||
function handleSelectSpan(i: Recordable) {
|
||||
function handleSelectSpan(i: any) {
|
||||
const spans = [];
|
||||
const refSpans = [];
|
||||
parentSpans.value = [];
|
||||
refParentSpans.value = [];
|
||||
if (props.type === TraceGraphType.TABLE) {
|
||||
currentSpan.value = i;
|
||||
} else {
|
||||
currentSpan.value = i.data;
|
||||
}
|
||||
if (!currentSpan.value) {
|
||||
return;
|
||||
}
|
||||
for (const ref of currentSpan.value.refs || []) {
|
||||
refSpans.push(ref);
|
||||
}
|
||||
if (currentSpan.value.parentSpanId > -1) {
|
||||
spans.push({
|
||||
parentSegmentId: currentSpan.value.segmentId,
|
||||
parentSpanId: currentSpan.value.parentSpanId,
|
||||
traceId: currentSpan.value.traceId,
|
||||
});
|
||||
}
|
||||
for (const span of refSpans) {
|
||||
const item = props.data.find(
|
||||
(d) => d.segmentId === span.parentSegmentId && d.spanId === span.parentSpanId && d.traceId === span.traceId,
|
||||
);
|
||||
item && refParentSpans.value.push(item);
|
||||
}
|
||||
for (const span of spans) {
|
||||
const item = props.data.find(
|
||||
(d) => d.segmentId === span.parentSegmentId && d.spanId === span.parentSpanId && d.traceId === span.traceId,
|
||||
);
|
||||
item && parentSpans.value.push(item);
|
||||
}
|
||||
}
|
||||
function viewParentSpan(span: Recordable) {
|
||||
if (props.type === TraceGraphType.TABLE) {
|
||||
setTableSpanStyle(span);
|
||||
return;
|
||||
}
|
||||
tree.value.highlightParents(span);
|
||||
}
|
||||
function viewSpanDetails() {
|
||||
showDetail.value = true;
|
||||
hideActionBox();
|
||||
}
|
||||
function setTableSpanStyle(span: Recordable) {
|
||||
const itemDom: any = document.querySelector(`.trace-item-${span.key}`);
|
||||
const items: any = document.querySelectorAll(".trace-item");
|
||||
for (const item of items) {
|
||||
item.style.background = appStore.theme === Themes.Dark ? "#212224" : "#fff";
|
||||
}
|
||||
itemDom.style.background = appStore.theme === Themes.Dark ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)";
|
||||
hideActionBox();
|
||||
}
|
||||
function hideActionBox() {
|
||||
const box: any = document.querySelector("#trace-action-box");
|
||||
box.style.display = "none";
|
||||
}
|
||||
function traverseTree(node: Recordable, spanId: string, segmentId: string, data: Recordable) {
|
||||
if (!node || node.isBroken) {
|
||||
@ -229,6 +324,7 @@ limitations under the License. -->
|
||||
}
|
||||
for (const i of [...fixSpans, ...props.data]) {
|
||||
i.label = i.endpointName || "no operation name";
|
||||
i.key = Math.random().toString(36).substring(2, 36);
|
||||
i.children = [];
|
||||
if (segmentGroup[i.segmentId]) {
|
||||
segmentGroup[i.segmentId].push(i);
|
||||
@ -272,21 +368,12 @@ limitations under the License. -->
|
||||
}
|
||||
}
|
||||
for (const i in segmentGroup) {
|
||||
if (segmentGroup[i].refs.length) {
|
||||
let exit = null;
|
||||
for (const ref of segmentGroup[i].refs) {
|
||||
const e = props.data.find(
|
||||
(i: Recordable) =>
|
||||
ref.traceId === i.traceId && ref.parentSegmentId === i.segmentId && ref.parentSpanId === i.spanId,
|
||||
);
|
||||
if (e) {
|
||||
exit = e;
|
||||
}
|
||||
}
|
||||
if (exit) {
|
||||
if (!segmentGroup[ref.parentSegmentId]) {
|
||||
segmentId.value.push(segmentGroup[i]);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (!segmentGroup[i].refs.length && segmentGroup[i].parentSpanId === -1) {
|
||||
segmentId.value.push(segmentGroup[i]);
|
||||
}
|
||||
}
|
||||
@ -310,6 +397,34 @@ limitations under the License. -->
|
||||
}
|
||||
}
|
||||
}
|
||||
function setLevel(arr: Recordable[], level = 1, totalExec?: number) {
|
||||
for (const item of arr) {
|
||||
item.level = level;
|
||||
totalExec = totalExec || item.endTime - item.startTime;
|
||||
item.totalExec = totalExec;
|
||||
if (item.children && item.children.length > 0) {
|
||||
setLevel(item.children, level + 1, totalExec);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
function getRefsAllNodes(tree: Recordable) {
|
||||
let nodes = [];
|
||||
let stack = [tree];
|
||||
|
||||
while (stack.length > 0) {
|
||||
const node = stack.pop();
|
||||
nodes.push(node);
|
||||
|
||||
if (node?.children && node.children.length > 0) {
|
||||
for (let i = node.children.length - 1; i >= 0; i--) {
|
||||
stack.push(node.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
function compare(p: string) {
|
||||
return (m: Recordable, n: Recordable) => {
|
||||
const a = m[p];
|
||||
@ -346,7 +461,7 @@ limitations under the License. -->
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
<style lang="scss">
|
||||
.d3-graph {
|
||||
height: 100%;
|
||||
}
|
||||
@ -356,36 +471,50 @@ limitations under the License. -->
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
.trace-node-container {
|
||||
fill: rgb(0 0 0 / 0%);
|
||||
stroke-width: 5px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
fill: rgb(0 0 0 / 5%);
|
||||
}
|
||||
}
|
||||
|
||||
.trace-node .node-text {
|
||||
font: 12.5px sans-serif;
|
||||
font: 12px sans-serif;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.domain {
|
||||
.trace-node.highlighted .node-text,
|
||||
.trace-node.highlightedParent .node-text {
|
||||
font-weight: bold;
|
||||
fill: #409eff;
|
||||
}
|
||||
|
||||
.highlightedParent .node,
|
||||
.highlighted .node {
|
||||
stroke-width: 4;
|
||||
fill: var(--font-color);
|
||||
stroke: var(--font-color);
|
||||
}
|
||||
|
||||
.trace-node.highlighted .trace-node-text,
|
||||
.trace-node.highlightedParent .trace-node-text {
|
||||
font-weight: bold;
|
||||
fill: #409eff;
|
||||
}
|
||||
|
||||
#trace-action-box {
|
||||
position: absolute;
|
||||
color: $font-color;
|
||||
cursor: pointer;
|
||||
border: var(--sw-topology-border);
|
||||
border-radius: 3px;
|
||||
background-color: $theme-background;
|
||||
padding: 10px 0;
|
||||
display: none;
|
||||
|
||||
div {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
text-align: left;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.time-charts-item {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border: 1px solid;
|
||||
font-size: 11px;
|
||||
border-radius: 4px;
|
||||
div:hover {
|
||||
color: $active-color;
|
||||
background-color: $popper-hover-bg-color;
|
||||
}
|
||||
|
||||
.dialog-c-text {
|
||||
white-space: pre;
|
||||
overflow: auto;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
|
@ -87,7 +87,14 @@ limitations under the License. -->
|
||||
{{ t("relatedTraceLogs") }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-dialog v-model="showEventDetail" :destroy-on-close="true" fullscreen @closed="showEventDetail = false">
|
||||
<el-dialog
|
||||
v-model="showEventDetail"
|
||||
width="60%"
|
||||
center
|
||||
align-center
|
||||
:destroy-on-close="true"
|
||||
@closed="showEventDetail = false"
|
||||
>
|
||||
<div>
|
||||
<div class="mb-10">
|
||||
<span class="grey title">Name:</span>
|
||||
@ -115,7 +122,14 @@ limitations under the License. -->
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="showRelatedLogs" :destroy-on-close="true" fullscreen @closed="showRelatedLogs = false">
|
||||
<el-dialog
|
||||
v-model="showRelatedLogs"
|
||||
width="60%"
|
||||
center
|
||||
align-center
|
||||
:destroy-on-close="true"
|
||||
@closed="showRelatedLogs = false"
|
||||
>
|
||||
<el-pagination
|
||||
v-model="pageNum"
|
||||
:page-size="pageSize"
|
||||
@ -295,4 +309,10 @@ limitations under the License. -->
|
||||
.link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.log-tips {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin: 50px 0;
|
||||
}
|
||||
</style>
|
||||
|
@ -22,7 +22,7 @@ limitations under the License. -->
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="list">
|
||||
<Graph :data="data" :traceId="traceId" type="List" />
|
||||
<Graph :data="data" :traceId="traceId" :type="TraceGraphType.LIST" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -31,8 +31,11 @@ limitations under the License. -->
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import * as d3 from "d3";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import type { Span } from "@/types/trace";
|
||||
import Graph from "./D3Graph/Index.vue";
|
||||
import { Themes } from "@/constants/data";
|
||||
import { TraceGraphType } from "./constant";
|
||||
|
||||
/* global defineProps, Recordable*/
|
||||
const props = defineProps({
|
||||
@ -40,6 +43,7 @@ limitations under the License. -->
|
||||
traceId: { type: String, default: "" },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const list = computed(() => Array.from(new Set(props.data.map((i: Span) => i.serviceCode))));
|
||||
|
||||
function computedScale(i: number) {
|
||||
@ -52,13 +56,13 @@ limitations under the License. -->
|
||||
|
||||
function downloadTrace() {
|
||||
const serializer = new XMLSerializer();
|
||||
const svgNode: any = d3.select(".trace-list-dowanload").node();
|
||||
const svgNode: any = d3.select(".trace-list").node();
|
||||
const source = `<?xml version="1.0" standalone="no"?>\r\n${serializer.serializeToString(svgNode)}`;
|
||||
const canvas = document.createElement("canvas");
|
||||
const context: any = canvas.getContext("2d");
|
||||
canvas.width = (d3.select(".trace-list-dowanload") as Recordable)._groups[0][0].clientWidth;
|
||||
canvas.height = (d3.select(".trace-list-dowanload") as Recordable)._groups[0][0].clientHeight;
|
||||
context.fillStyle = "#fff";
|
||||
canvas.width = (d3.select(".trace-list") as Recordable)._groups[0][0].clientWidth;
|
||||
canvas.height = (d3.select(".trace-list") as Recordable)._groups[0][0].clientHeight;
|
||||
context.fillStyle = appStore.theme === Themes.Dark ? "#212224" : `#fff`;
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
const image = new Image();
|
||||
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(source)}`;
|
||||
@ -93,6 +97,7 @@ limitations under the License. -->
|
||||
|
||||
.list {
|
||||
height: calc(100% - 150px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.event-tag {
|
||||
|
@ -17,7 +17,7 @@ limitations under the License. -->
|
||||
<div class="trace-t-loading" v-show="loading">
|
||||
<Icon iconName="spinner" size="sm" />
|
||||
</div>
|
||||
<TableContainer :tableData="tableData" type="statistics" :HeaderType="HeaderType">
|
||||
<TableContainer :tableData="tableData" :type="TraceGraphType.STATISTICS" :headerType="headerType">
|
||||
<div class="trace-tips" v-if="!tableData.length">{{ $t("noData") }}</div>
|
||||
</TableContainer>
|
||||
</div>
|
||||
@ -28,13 +28,14 @@ limitations under the License. -->
|
||||
import TableContainer from "./Table/TableContainer.vue";
|
||||
import traceTable from "../utils/trace-table";
|
||||
import type { StatisticsSpan, Span, StatisticsGroupRef } from "@/types/trace";
|
||||
import { TraceGraphType } from "./constant";
|
||||
|
||||
/* global defineProps, defineEmits, Recordable*/
|
||||
const props = defineProps({
|
||||
data: { type: Array as PropType<Span[]>, default: () => [] },
|
||||
traceId: { type: String, default: "" },
|
||||
showBtnDetail: { type: Boolean, default: false },
|
||||
HeaderType: { type: String, default: "" },
|
||||
headerType: { type: String, default: "" },
|
||||
});
|
||||
const emit = defineEmits(["load"]);
|
||||
const loading = ref<boolean>(true);
|
||||
|
38
src/views/dashboard/related/trace/components/Table.vue
Normal file
38
src/views/dashboard/related/trace/components/Table.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<!-- 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="trace-table-charts">
|
||||
<Graph :data="data" :traceId="traceId" :type="TraceGraphType.TABLE" :headerType="headerType" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import type { Span } from "@/types/trace";
|
||||
import Graph from "./D3Graph/Index.vue";
|
||||
import { TraceGraphType } from "./constant";
|
||||
|
||||
defineProps({
|
||||
data: { type: Array as PropType<Span[]>, default: () => [] },
|
||||
traceId: { type: String, default: "" },
|
||||
headerType: { type: String, default: "" },
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.trace-table-charts {
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
@ -1,108 +0,0 @@
|
||||
<!-- 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="trace-table">
|
||||
<div class="trace-t-loading" v-show="loading">
|
||||
<Icon iconName="spinner" size="sm" />
|
||||
</div>
|
||||
<TableContainer
|
||||
:tableData="tableData"
|
||||
type="table"
|
||||
:headerType="headerType"
|
||||
:traceId="traceId"
|
||||
@select="handleSelectSpan"
|
||||
>
|
||||
<div class="trace-tips" v-if="!tableData.length">{{ $t("noData") }}</div>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import TableContainer from "./TableContainer.vue";
|
||||
import traceTable from "../../utils/trace-table";
|
||||
import type { Span } from "@/types/trace";
|
||||
|
||||
/* global defineProps, defineEmits, Recordable */
|
||||
const props = defineProps({
|
||||
data: { type: Array as PropType<Span[]>, default: () => [] },
|
||||
traceId: { type: String, default: "" },
|
||||
showBtnDetail: { type: Boolean, default: false },
|
||||
headerType: { type: String, default: "" },
|
||||
});
|
||||
const emit = defineEmits(["select", "view", "load"]);
|
||||
const loading = ref<boolean>(true);
|
||||
const tableData = ref<Recordable[]>([]);
|
||||
const showDetail = ref<boolean>(false);
|
||||
const currentSpan = ref<Span | Recordable>({});
|
||||
|
||||
onMounted(() => {
|
||||
tableData.value = formatData(traceTable.changeTree(props.data, props.traceId));
|
||||
loading.value = false;
|
||||
emit("load", () => {
|
||||
loading.value = true;
|
||||
});
|
||||
});
|
||||
|
||||
function formatData(arr: Recordable[], level = 1, totalExec?: number) {
|
||||
for (const item of arr) {
|
||||
item.level = level;
|
||||
totalExec = totalExec || item.endTime - item.startTime;
|
||||
item.totalExec = totalExec;
|
||||
if (item.children && item.children.length > 0) {
|
||||
formatData(item.children, level + 1, totalExec);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function handleSelectSpan(data: Span) {
|
||||
currentSpan.value = data;
|
||||
if (!props.showBtnDetail) {
|
||||
showDetail.value = true;
|
||||
}
|
||||
emit("select", data);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
() => {
|
||||
if (!props.data.length) {
|
||||
tableData.value = [];
|
||||
return;
|
||||
}
|
||||
tableData.value = formatData(traceTable.changeTree(props.data, props.traceId));
|
||||
loading.value = false;
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dialog-c-text {
|
||||
white-space: pre;
|
||||
overflow: auto;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.trace-tips {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.trace-table {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
|
||||
<template>
|
||||
<div class="trace">
|
||||
<div class="trace-header" v-if="type === 'statistics'">
|
||||
<div class="trace-table">
|
||||
<div class="trace-table-header" v-if="type === TraceGraphType.STATISTICS">
|
||||
<div :class="item.label" v-for="(item, index) in headerData" :key="index">
|
||||
{{ item.value }}
|
||||
<span
|
||||
@ -28,7 +28,7 @@ limitations under the License. -->
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trace-header" v-else>
|
||||
<div class="trace-table-header" v-else>
|
||||
<div class="method" :style="`width: ${method}px`">
|
||||
<span class="cp dragger" ref="dragger">
|
||||
<Icon iconName="settings_ethernet" size="sm" />
|
||||
@ -44,10 +44,10 @@ limitations under the License. -->
|
||||
:traceId="traceId"
|
||||
v-for="(item, index) in tableData"
|
||||
:data="item"
|
||||
:key="'key' + index"
|
||||
:key="`key${index}`"
|
||||
:type="type"
|
||||
:headerType="headerType"
|
||||
@select="selectItem"
|
||||
@click="selectItem"
|
||||
/>
|
||||
<slot></slot>
|
||||
</div>
|
||||
@ -55,9 +55,11 @@ limitations under the License. -->
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import type { Span } from "@/types/trace";
|
||||
import { useTraceStore } from "@/store/modules/trace";
|
||||
import TableItem from "./TableItem.vue";
|
||||
import { ProfileConstant, TraceConstant, StatisticsConstant } from "./data";
|
||||
import { TraceGraphType } from "../constant";
|
||||
import { WidgetType } from "@/views/dashboard/data";
|
||||
|
||||
/* global defineProps, Nullable, defineEmits, Recordable*/
|
||||
const props = defineProps({
|
||||
@ -67,20 +69,21 @@ limitations under the License. -->
|
||||
traceId: { type: String, default: "" },
|
||||
});
|
||||
const emits = defineEmits(["select"]);
|
||||
const traceStore = useTraceStore();
|
||||
const method = ref<number>(300);
|
||||
const componentKey = ref<number>(300);
|
||||
const flag = ref<boolean>(true);
|
||||
const dragger = ref<Nullable<HTMLSpanElement>>(null);
|
||||
let headerData: Recordable[] = TraceConstant;
|
||||
|
||||
if (props.headerType === "profile") {
|
||||
if (props.headerType === WidgetType.Profile) {
|
||||
headerData = ProfileConstant;
|
||||
}
|
||||
if (props.type === "statistics") {
|
||||
if (props.type === TraceGraphType.STATISTICS) {
|
||||
headerData = StatisticsConstant;
|
||||
}
|
||||
onMounted(() => {
|
||||
if (props.type === "statistics") {
|
||||
if (props.type === TraceGraphType.STATISTICS) {
|
||||
return;
|
||||
}
|
||||
const drag: Nullable<HTMLSpanElement> = dragger.value;
|
||||
@ -101,8 +104,24 @@ limitations under the License. -->
|
||||
};
|
||||
};
|
||||
});
|
||||
function selectItem(span: Span) {
|
||||
emits("select", span);
|
||||
function selectItem(event: MouseEvent) {
|
||||
emits("select", traceStore.selectedSpan);
|
||||
if (props.headerType === WidgetType.Profile) {
|
||||
return;
|
||||
}
|
||||
if (props.type === TraceGraphType.STATISTICS) {
|
||||
return;
|
||||
}
|
||||
const item: any = document.querySelector("#trace-action-box");
|
||||
const tableBox = document.querySelector(".trace-table-charts")?.getBoundingClientRect();
|
||||
if (!tableBox) {
|
||||
return;
|
||||
}
|
||||
const offsetX = event.x - tableBox.x;
|
||||
const offsetY = event.y - tableBox.y;
|
||||
item.style.display = "block";
|
||||
item.style.top = `${offsetY + 20}px`;
|
||||
item.style.left = `${offsetX + 10}px`;
|
||||
}
|
||||
function sortStatistics(key: string) {
|
||||
const element = props.tableData;
|
||||
@ -152,7 +171,7 @@ limitations under the License. -->
|
||||
<style lang="scss" scoped>
|
||||
@import url("./table.scss");
|
||||
|
||||
.trace {
|
||||
.trace-table {
|
||||
font-size: $font-size-smaller;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
@ -163,7 +182,7 @@ limitations under the License. -->
|
||||
float: right;
|
||||
}
|
||||
|
||||
.trace-header {
|
||||
.trace-table-header {
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
border-left: 0;
|
||||
@ -171,7 +190,7 @@ limitations under the License. -->
|
||||
border-bottom: 1px solid var(--sw-trace-list-border);
|
||||
}
|
||||
|
||||
.trace-header div {
|
||||
.trace-table-header div {
|
||||
display: inline-block;
|
||||
background-color: var(--sw-table-header);
|
||||
padding: 0 4px;
|
||||
|
@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
|
||||
<template>
|
||||
<div v-if="type === 'statistics'">
|
||||
<div v-if="type === TraceGraphType.STATISTICS">
|
||||
<div class="trace-item">
|
||||
<div :class="['method']">
|
||||
<el-tooltip :content="data.groupRef.endpointName" placement="bottom" :show-after="300">
|
||||
<el-tooltip :content="data.groupRef.endpointName" placement="top" :show-after="300">
|
||||
<span>
|
||||
{{ data.groupRef.endpointName }}
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div :class="['type']">
|
||||
<el-tooltip :content="data.groupRef.type" placement="bottom" :show-after="300">
|
||||
<el-tooltip :content="data.groupRef.type" placement="top" :show-after="300">
|
||||
<span>
|
||||
{{ data.groupRef.type }}
|
||||
</span>
|
||||
@ -55,6 +55,7 @@ limitations under the License. -->
|
||||
'level' + (data.level - 1),
|
||||
{ 'trace-item-error': data.isError },
|
||||
{ profiled: data.profiled === false },
|
||||
`trace-item-${data.key}`,
|
||||
]"
|
||||
:data-text="data.profiled === false ? 'No Thread Dump' : ''"
|
||||
>
|
||||
@ -75,7 +76,7 @@ limitations under the License. -->
|
||||
/>
|
||||
<el-tooltip
|
||||
:content="data.type === 'Entry' ? 'Entry' : 'Exit'"
|
||||
placement="bottom"
|
||||
placement="top"
|
||||
:show-after="300"
|
||||
v-if="['Entry', 'Exit'].includes(data.type)"
|
||||
>
|
||||
@ -83,12 +84,12 @@ limitations under the License. -->
|
||||
<Icon :iconName="data.type === 'Entry' ? 'entry' : 'exit'" size="sm" class="mr-5" />
|
||||
</span>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="isCrossThread" content="CROSS_THREAD" placement="bottom" :show-after="300">
|
||||
<el-tooltip v-if="isCrossThread" content="CROSS_THREAD" placement="top" :show-after="300">
|
||||
<span>
|
||||
<Icon iconName="cross" size="sm" class="mr-5" />
|
||||
</span>
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="data.endpointName" placement="bottom" :show-after="300">
|
||||
<el-tooltip :content="data.endpointName" placement="top" :show-after="300">
|
||||
<span>
|
||||
{{ data.endpointName }}
|
||||
</span>
|
||||
@ -109,19 +110,19 @@ limitations under the License. -->
|
||||
{{ data.dur ? data.dur + "" : "0" }}
|
||||
</div>
|
||||
<div class="api">
|
||||
<el-tooltip :show-after="300" :content="data.component || '-'" placement="bottom">
|
||||
<el-tooltip :show-after="300" :content="data.component || '-'" placement="top">
|
||||
<span>{{ data.component || "-" }}</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="application">
|
||||
<el-tooltip :show-after="300" :content="data.serviceCode || '-'" placement="bottom">
|
||||
<el-tooltip :show-after="300" :content="data.serviceCode || '-'" placement="top">
|
||||
<span>{{ data.serviceCode }}</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="application" v-show="headerType === 'profile'" @click="viewSpan($event)">
|
||||
<div class="application" v-show="headerType === WidgetType.Profile" @click="viewSpan($event)">
|
||||
<span>{{ t("view") }}</span>
|
||||
</div>
|
||||
<div class="application" v-show="headerType !== 'profile'">
|
||||
<div class="application" v-show="headerType !== WidgetType.Profile">
|
||||
<span>{{ data.attachedEvents && data.attachedEvents.length }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -133,7 +134,6 @@ limitations under the License. -->
|
||||
:data="child"
|
||||
:type="type"
|
||||
:headerType="headerType"
|
||||
@select="selectedItem(child)"
|
||||
/>
|
||||
</div>
|
||||
<el-dialog v-model="showDetail" :destroy-on-close="true" fullscreen @closed="showDetail = false">
|
||||
@ -148,7 +148,10 @@ limitations under the License. -->
|
||||
import SpanDetail from "../D3Graph/SpanDetail.vue";
|
||||
import { dateFormat } from "@/utils/dateFormat";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useTraceStore } from "@/store/modules/trace";
|
||||
import { Themes } from "@/constants/data";
|
||||
import { TraceGraphType } from "../constant";
|
||||
import { WidgetType } from "@/views/dashboard/data";
|
||||
|
||||
/*global Recordable*/
|
||||
const props = {
|
||||
@ -161,10 +164,10 @@ limitations under the License. -->
|
||||
export default defineComponent({
|
||||
name: "TableItem",
|
||||
props,
|
||||
emits: ["select"],
|
||||
components: { SpanDetail },
|
||||
setup(props, { emit }) {
|
||||
setup(props) {
|
||||
const appStore = useAppStoreWithOut();
|
||||
const traceStore = useTraceStore();
|
||||
const displayChildren = ref<boolean>(true);
|
||||
const showDetail = ref<boolean>(false);
|
||||
const { t } = useI18n();
|
||||
@ -193,7 +196,6 @@ limitations under the License. -->
|
||||
const key = props.data.refs.findIndex((d: { type: string }) => d.type === "CROSS_THREAD");
|
||||
return key > -1 ? true : false;
|
||||
});
|
||||
|
||||
function toggle() {
|
||||
displayChildren.value = !displayChildren.value;
|
||||
}
|
||||
@ -213,27 +215,28 @@ limitations under the License. -->
|
||||
}
|
||||
function selectSpan(event: Recordable) {
|
||||
const dom = event.composedPath().find((d: Recordable) => d.className.includes("trace-item"));
|
||||
|
||||
emit("select", props.data);
|
||||
if (props.headerType === "profile") {
|
||||
selectedItem(props.data);
|
||||
if (props.headerType === WidgetType.Profile) {
|
||||
showSelectSpan(dom);
|
||||
return;
|
||||
}
|
||||
viewSpanDetail(dom);
|
||||
}
|
||||
function viewSpan(event: Recordable) {
|
||||
showDetail.value = true;
|
||||
const dom = event.composedPath().find((d: Recordable) => d.className.includes("trace-item"));
|
||||
emit("select", props.data);
|
||||
selectedItem(props.data);
|
||||
viewSpanDetail(dom);
|
||||
}
|
||||
|
||||
function selectedItem(data: HTMLSpanElement) {
|
||||
emit("select", data);
|
||||
function selectedItem(span: Recordable) {
|
||||
traceStore.setSelectedSpan(span);
|
||||
}
|
||||
function viewSpanDetail(dom: HTMLSpanElement) {
|
||||
showSelectSpan(dom);
|
||||
if (props.type === TraceGraphType.STATISTICS) {
|
||||
showDetail.value = true;
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => appStore.theme,
|
||||
() => {
|
||||
@ -262,6 +265,8 @@ limitations under the License. -->
|
||||
viewSpan,
|
||||
t,
|
||||
appStore,
|
||||
TraceGraphType,
|
||||
WidgetType,
|
||||
};
|
||||
},
|
||||
});
|
||||
@ -279,8 +284,6 @@ limitations under the License. -->
|
||||
}
|
||||
|
||||
.trace-item.level0 {
|
||||
color: #448dfe;
|
||||
|
||||
&:hover {
|
||||
background: rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ limitations under the License. -->
|
||||
</a>
|
||||
</div>
|
||||
<div class="trace-tree">
|
||||
<Graph ref="charts" :data="data" :traceId="traceId" type="Tree" />
|
||||
<Graph ref="charts" :data="data" :traceId="traceId" :type="TraceGraphType.TREE" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -46,6 +46,7 @@ limitations under the License. -->
|
||||
import type { Span } from "@/types/trace";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ref, computed } from "vue";
|
||||
import { TraceGraphType } from "./constant";
|
||||
|
||||
/* global defineProps */
|
||||
const props = defineProps({
|
||||
|
@ -15,11 +15,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
const CancelToken = axios.CancelToken;
|
||||
|
||||
export const cancelToken = (): any =>
|
||||
new CancelToken(function executor(c) {
|
||||
const w = window as any;
|
||||
(w.axiosCancel || []).push(c);
|
||||
});
|
||||
export enum TraceGraphType {
|
||||
TREE = "Tree",
|
||||
LIST = "List",
|
||||
TABLE = "Table",
|
||||
STATISTICS = "Statistics",
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
|
||||
import List from "./List.vue";
|
||||
import Tree from "./Tree.vue";
|
||||
import Table from "./Table/Index.vue";
|
||||
import Table from "./Table.vue";
|
||||
import Statistics from "./Statistics.vue";
|
||||
|
||||
export default {
|
||||
|
@ -42,16 +42,17 @@ export default class ListGraph {
|
||||
private xAxis: any = null;
|
||||
private sequentialScale: any = null;
|
||||
private root: any = null;
|
||||
private selectedNode: any = null;
|
||||
constructor(el: HTMLDivElement, handleSelectSpan: (i: Trace) => void) {
|
||||
this.handleSelectSpan = handleSelectSpan;
|
||||
this.el = el;
|
||||
this.width = el.getBoundingClientRect().width - 10;
|
||||
this.height = el.getBoundingClientRect().height - 10;
|
||||
d3.select(".trace-list-dowanload").remove();
|
||||
d3.select(`.${this.el?.className} .trace-list`).remove();
|
||||
this.svg = d3
|
||||
.select(this.el)
|
||||
.append("svg")
|
||||
.attr("class", "trace-list-dowanload")
|
||||
.attr("class", "trace-list")
|
||||
.attr("width", this.width > 0 ? this.width : 10)
|
||||
.attr("height", this.height > 0 ? this.height : 10)
|
||||
.attr("transform", `translate(-5, 0)`);
|
||||
@ -85,7 +86,8 @@ export default class ListGraph {
|
||||
L${d.target.y} ${d.target.x - 5}`;
|
||||
}
|
||||
init(data: Recordable, row: Recordable[], fixSpansSize: number) {
|
||||
d3.select(".trace-xaxis").remove();
|
||||
d3.select(`.${this.el?.className} .trace-xaxis`).remove();
|
||||
d3.select("#trace-action-box").style("display", "none");
|
||||
this.row = row;
|
||||
this.data = data;
|
||||
this.min = d3.min(this.row.map((i) => i.startTime));
|
||||
@ -113,6 +115,14 @@ export default class ListGraph {
|
||||
this.root = d3.hierarchy(this.data, (d) => d.children);
|
||||
this.root.x0 = 0;
|
||||
this.root.y0 = 0;
|
||||
const t = this;
|
||||
d3.select("svg.trace-list").on("click", function (event: MouseEvent) {
|
||||
if (event.target === this) {
|
||||
d3.select("#trace-action-box").style("display", "none");
|
||||
t.selectedNode && t.selectedNode.classed("highlighted", false);
|
||||
t.clearParentHighlight();
|
||||
}
|
||||
});
|
||||
}
|
||||
draw(callback: Function) {
|
||||
this.update(this.root, callback);
|
||||
@ -142,19 +152,35 @@ export default class ListGraph {
|
||||
.enter()
|
||||
.append("g")
|
||||
.attr("transform", `translate(${source.y0},${source.x0})`)
|
||||
.attr("id", (d: Recordable) => `list-node-${d.id}`)
|
||||
.attr("class", "trace-node")
|
||||
.attr("style", "cursor: pointer")
|
||||
.style("opacity", 0)
|
||||
.on("mouseover", function (event: MouseEvent, d: Trace) {
|
||||
t.tip.show(d, this);
|
||||
})
|
||||
.on("mouseout", function (event: MouseEvent, d: Trace) {
|
||||
t.tip.hide(d, this);
|
||||
})
|
||||
.on("click", (event: MouseEvent, d: Trace) => {
|
||||
if (this.handleSelectSpan) {
|
||||
this.handleSelectSpan(d);
|
||||
.on("click", function (event: MouseEvent, d: Trace & { id: string }) {
|
||||
event.stopPropagation();
|
||||
t.tip.hide(d, this);
|
||||
d3.select(this).classed("highlighted", true);
|
||||
const nodeBox = this.getBoundingClientRect();
|
||||
const svgBox = (d3.select(`.${t.el?.className} .trace-list`) as any).node().getBoundingClientRect();
|
||||
const offsetX = nodeBox.x - svgBox.x;
|
||||
const offsetY = nodeBox.y - svgBox.y;
|
||||
d3.select("#trace-action-box")
|
||||
.style("display", "block")
|
||||
.style("left", `${offsetX + 30}px`)
|
||||
.style("top", `${offsetY + 40}px`);
|
||||
t.selectedNode = d3.select(this);
|
||||
if (t.handleSelectSpan) {
|
||||
t.handleSelectSpan(d);
|
||||
}
|
||||
t.root.descendants().map((node: { id: number }) => {
|
||||
d3.select(`#list-node-${node.id}`).classed("highlightedParent", false);
|
||||
return node;
|
||||
});
|
||||
});
|
||||
nodeEnter
|
||||
.append("rect")
|
||||
@ -239,14 +265,14 @@ export default class ListGraph {
|
||||
.attr("cx", (d: Recordable) => {
|
||||
const events = d.data.attachedEvents;
|
||||
if (events && events.length > 9) {
|
||||
return 272;
|
||||
return 273;
|
||||
} else {
|
||||
return 270;
|
||||
}
|
||||
})
|
||||
.attr("cy", -5)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", appStore.theme === Themes.Dark ? "#666" : "#e66")
|
||||
.attr("stroke", "#e66")
|
||||
.style("opacity", (d: Recordable) => {
|
||||
const events = d.data.attachedEvents;
|
||||
if (events && events.length) {
|
||||
@ -259,7 +285,7 @@ export default class ListGraph {
|
||||
.append("text")
|
||||
.attr("x", 267)
|
||||
.attr("y", -1)
|
||||
.attr("fill", appStore.theme === Themes.Dark ? "#666" : "#e66")
|
||||
.attr("fill", "#e66")
|
||||
.style("font-size", "10px")
|
||||
.text((d: Recordable) => {
|
||||
const events = d.data.attachedEvents;
|
||||
@ -324,16 +350,6 @@ export default class ListGraph {
|
||||
if (d.data.children.length === 0) return;
|
||||
this.click(d, this);
|
||||
});
|
||||
nodeUpdate
|
||||
.transition()
|
||||
.duration(400)
|
||||
.attr("transform", (d: Recordable) => `translate(${d.y + 3},${d.x})`)
|
||||
.style("opacity", 1)
|
||||
.select("circle")
|
||||
.style("fill", (d: Recordable) =>
|
||||
d._children ? `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}` : "#eee",
|
||||
);
|
||||
|
||||
// Transition exiting nodes to the parent's new position.
|
||||
node
|
||||
.exit()
|
||||
@ -381,6 +397,39 @@ export default class ListGraph {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
clearParentHighlight() {
|
||||
return this.root.descendants().map((node: { id: number }) => {
|
||||
d3.select(`#list-node-${node.id}`).classed("highlightedParent", false);
|
||||
return node;
|
||||
});
|
||||
}
|
||||
highlightParents(span: Recordable) {
|
||||
if (!span) {
|
||||
return;
|
||||
}
|
||||
const nodes = this.clearParentHighlight();
|
||||
const parentSpan = nodes.find(
|
||||
(node: Recordable) =>
|
||||
span.spanId === node.data.spanId &&
|
||||
span.segmentId === node.data.segmentId &&
|
||||
span.traceId === node.data.traceId,
|
||||
);
|
||||
if (!parentSpan) return;
|
||||
d3.select(`#list-node-${parentSpan.id}`).classed("highlightedParent", true);
|
||||
d3.select("#trace-action-box").style("display", "none");
|
||||
this.selectedNode.classed("highlighted", false);
|
||||
const container = document.querySelector(".trace-chart .charts");
|
||||
const containerRect = container?.getBoundingClientRect();
|
||||
if (!containerRect) return;
|
||||
const targetElement = document.querySelector(`#list-node-${parentSpan.id}`);
|
||||
if (!targetElement) return;
|
||||
const targetRect = targetElement.getBoundingClientRect();
|
||||
container?.scrollTo({
|
||||
left: targetRect.left - containerRect.left + container?.scrollLeft,
|
||||
top: targetRect.top - containerRect.top + container?.scrollTop - 100,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
visDate(date: number, pattern = "YYYY-MM-DD HH:mm:ss:SSS") {
|
||||
return dayjs(date).format(pattern);
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ export default class TraceMap {
|
||||
private topChildMax: number[] = [];
|
||||
private topChildMin: number[] = [];
|
||||
private nodeUpdate: Nullable<any> = null;
|
||||
private selectedNode: any = null;
|
||||
|
||||
constructor(el: HTMLDivElement, handleSelectSpan: (i: Trace) => void) {
|
||||
this.el = el;
|
||||
@ -55,7 +56,7 @@ export default class TraceMap {
|
||||
this.topChild = [];
|
||||
this.width = el.clientWidth - 20;
|
||||
this.height = el.clientHeight - 30;
|
||||
d3.select(".d3-trace-tree").remove();
|
||||
d3.select(`.${this.el?.className} .d3-trace-tree`).remove();
|
||||
this.body = d3
|
||||
.select(this.el)
|
||||
.append("svg")
|
||||
@ -80,6 +81,7 @@ export default class TraceMap {
|
||||
this.svg.call(this.tip);
|
||||
}
|
||||
init(data: Recordable, row: Recordable) {
|
||||
d3.select("#trace-action-box").style("display", "none");
|
||||
this.treemap = d3.tree().size([row.length * 35, this.width]);
|
||||
this.row = row;
|
||||
this.data = data;
|
||||
@ -124,24 +126,33 @@ export default class TraceMap {
|
||||
this.update(this.root);
|
||||
}
|
||||
update(source: Recordable) {
|
||||
const t = this;
|
||||
const appStore = useAppStoreWithOut();
|
||||
const that: any = this;
|
||||
const treeData = this.treemap(this.root);
|
||||
const nodes = treeData.descendants(),
|
||||
links = treeData.descendants().slice(1);
|
||||
const nodes = treeData.descendants();
|
||||
const links = treeData.descendants().slice(1);
|
||||
|
||||
nodes.forEach(function (d: Recordable) {
|
||||
d.y = d.depth * 140;
|
||||
});
|
||||
|
||||
const node = this.svg.selectAll("g.node").data(nodes, (d: Recordable) => {
|
||||
const node = this.svg.selectAll("g.trace-node").data(nodes, (d: Recordable) => {
|
||||
return d.id || (d.id = ++this.i);
|
||||
});
|
||||
d3.select("svg.d3-trace-tree").on("click", function (event: MouseEvent) {
|
||||
if (event.target === this) {
|
||||
d3.select("#trace-action-box").style("display", "none");
|
||||
t.selectedNode && t.selectedNode.classed("highlighted", false);
|
||||
t.clearParentHighlight();
|
||||
}
|
||||
});
|
||||
|
||||
const nodeEnter = node
|
||||
.enter()
|
||||
.append("g")
|
||||
.attr("class", "node")
|
||||
.attr("class", "trace-node")
|
||||
.attr("id", (d: Recordable) => `trace-node-${d.id}`)
|
||||
.attr("cursor", "pointer")
|
||||
.attr("transform", function () {
|
||||
return "translate(" + source.y0 + "," + source.x0 + ")";
|
||||
@ -165,9 +176,6 @@ export default class TraceMap {
|
||||
if (_node.length) {
|
||||
that.timeTip.hide(d, _node[0].children[1]);
|
||||
}
|
||||
})
|
||||
.on("click", function (event: MouseEvent, d: Recordable) {
|
||||
that.handleSelectSpan(d);
|
||||
});
|
||||
nodeEnter
|
||||
.append("circle")
|
||||
@ -208,15 +216,15 @@ export default class TraceMap {
|
||||
nodeEnter
|
||||
.append("circle")
|
||||
.attr("class", "node")
|
||||
.attr("r", 1e-6)
|
||||
.style("fill", (d: Recordable) =>
|
||||
d._children ? this.sequentialScale(this.list.indexOf(d.data.serviceCode)) : "#fff",
|
||||
)
|
||||
.attr("r", 2)
|
||||
.attr("stroke", (d: Recordable) => this.sequentialScale(this.list.indexOf(d.data.serviceCode)))
|
||||
.attr("stroke-width", 2.5);
|
||||
|
||||
.attr("stroke-width", 2.5)
|
||||
.attr("fill", (d: Recordable) =>
|
||||
d.data.children.length ? this.sequentialScale(this.list.indexOf(d.data.serviceCode)) : "#fff",
|
||||
);
|
||||
nodeEnter
|
||||
.append("text")
|
||||
.attr("class", "trace-node-text")
|
||||
.attr("font-size", 11)
|
||||
.attr("dy", "-0.5em")
|
||||
.attr("x", function (d: Recordable) {
|
||||
@ -230,24 +238,27 @@ export default class TraceMap {
|
||||
? (d.data.isError ? "◉ " : "") + d.data.label.slice(0, 10) + "..."
|
||||
: (d.data.isError ? "◉ " : "") + d.data.label,
|
||||
)
|
||||
.style("fill", (d: Recordable) =>
|
||||
.attr("fill", (d: Recordable) =>
|
||||
!d.data.isError ? (appStore.theme === Themes.Dark ? "#eee" : "#3d444f") : "#E54C17",
|
||||
);
|
||||
nodeEnter
|
||||
.append("text")
|
||||
.attr("class", "node-text")
|
||||
.attr("x", function (d: Recordable) {
|
||||
return d.children || d._children ? -45 : 15;
|
||||
return d.children || d._children ? -30 : 15;
|
||||
})
|
||||
.attr("dy", "1em")
|
||||
.attr("dy", "1.5em")
|
||||
.attr("fill", appStore.theme === Themes.Dark ? "#888" : "#bbb")
|
||||
.attr("text-anchor", function (d: Recordable) {
|
||||
return d.children || d._children ? "end" : "start";
|
||||
})
|
||||
.style("font-size", "10px")
|
||||
.text(
|
||||
(d: Recordable) => `${d.data.layer || ""}${d.data.component ? "-" + d.data.component : d.data.component || ""}`,
|
||||
);
|
||||
.text((d: Recordable) => {
|
||||
const label = d.data.component
|
||||
? " - " + (d.data.component.length > 10 ? d.data.label.slice(0, 10) + "..." : d.data.component)
|
||||
: "";
|
||||
return `${d.data.layer || ""}${label}`;
|
||||
});
|
||||
nodeEnter
|
||||
.append("rect")
|
||||
.attr("rx", 1)
|
||||
@ -290,12 +301,37 @@ export default class TraceMap {
|
||||
nodeUpdate
|
||||
.select("circle.node")
|
||||
.attr("r", 5)
|
||||
.style("fill", (d: Recordable) =>
|
||||
d._children ? this.sequentialScale(this.list.indexOf(d.data.serviceCode)) : "#fff",
|
||||
)
|
||||
.attr("cursor", "pointer")
|
||||
.on("click", (event: any, d: Recordable) => {
|
||||
.on("click", function (event: MouseEvent, d: Trace & { id: string }) {
|
||||
event.stopPropagation();
|
||||
t.tip.hide(d, this);
|
||||
d3.select(this.parentNode).classed("highlighted", true);
|
||||
const nodeBox = this.getBoundingClientRect();
|
||||
const svgBox = (d3.select(`.${t.el?.className} .d3-trace-tree`) as any).node().getBoundingClientRect();
|
||||
const offsetX = nodeBox.x - svgBox.x;
|
||||
const offsetY = nodeBox.y - svgBox.y;
|
||||
d3.select("#trace-action-box")
|
||||
.style("display", "block")
|
||||
.style("left", `${offsetX + 30}px`)
|
||||
.style("top", `${offsetY + 40}px`);
|
||||
t.selectedNode = d3.select(this.parentNode);
|
||||
if (t.handleSelectSpan) {
|
||||
t.handleSelectSpan(d);
|
||||
}
|
||||
t.root.descendants().map((node: { id: number }) => {
|
||||
d3.select(`#trace-node-${node.id}`).classed("highlightedParent", false);
|
||||
return node;
|
||||
});
|
||||
})
|
||||
.on("dblclick", function (event: MouseEvent, d: Recordable) {
|
||||
event.stopPropagation();
|
||||
t.tip.hide(d, this);
|
||||
if (d.data.children.length === 0) return;
|
||||
click(d);
|
||||
})
|
||||
.on("contextmenu", function (event: MouseEvent, d: Recordable) {
|
||||
event.stopPropagation();
|
||||
t.tip.hide(d, this);
|
||||
if (d.data.children.length === 0) return;
|
||||
click(d);
|
||||
});
|
||||
@ -369,6 +405,39 @@ export default class TraceMap {
|
||||
that.update(d);
|
||||
}
|
||||
}
|
||||
clearParentHighlight() {
|
||||
return this.root.descendants().map((node: { id: number }) => {
|
||||
d3.select(`#trace-node-${node.id}`).classed("highlightedParent", false);
|
||||
return node;
|
||||
});
|
||||
}
|
||||
highlightParents(span: Recordable) {
|
||||
if (!span) {
|
||||
return;
|
||||
}
|
||||
const nodes = this.clearParentHighlight();
|
||||
const parentSpan = nodes.find(
|
||||
(node: Recordable) =>
|
||||
span.spanId === node.data.spanId &&
|
||||
span.segmentId === node.data.segmentId &&
|
||||
span.traceId === node.data.traceId,
|
||||
);
|
||||
if (!parentSpan) return;
|
||||
d3.select(`#trace-node-${parentSpan.id}`).classed("highlightedParent", true);
|
||||
d3.select("#trace-action-box").style("display", "none");
|
||||
this.selectedNode.classed("highlighted", false);
|
||||
const container = document.querySelector(".trace-chart .charts");
|
||||
const containerRect = container?.getBoundingClientRect();
|
||||
if (!containerRect) return;
|
||||
const targetElement = document.querySelector(`#trace-node-${parentSpan.id}`);
|
||||
if (!targetElement) return;
|
||||
const targetRect = targetElement.getBoundingClientRect();
|
||||
container?.scrollTo({
|
||||
left: targetRect.left - containerRect.left + container?.scrollLeft,
|
||||
top: targetRect.top - containerRect.top + container?.scrollTop - 100,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
setDefault() {
|
||||
d3.selectAll(".time-inner").style("opacity", 1);
|
||||
d3.selectAll(".time-inner-duration").style("opacity", 0);
|
||||
|
@ -15,42 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Ref, Span, StatisticsSpan, StatisticsGroupRef, TraceTreeRef } from "@/types/trace";
|
||||
import type { Span, TraceTreeRef } from "@/types/trace";
|
||||
|
||||
export default class TraceUtil {
|
||||
public static buildTraceDataList(data: Span[]): string[] {
|
||||
return Array.from(new Set(data.map((span: Span) => span.serviceCode)));
|
||||
}
|
||||
|
||||
public static changeTree(data: Span[], currentTraceId: string) {
|
||||
const segmentIdList: Span[] = [];
|
||||
const traceTreeRef: Recordable = this.changeTreeCore(data);
|
||||
traceTreeRef.segmentIdGroup.forEach((segmentId: string) => {
|
||||
if (traceTreeRef.segmentMap.get(segmentId).refs) {
|
||||
traceTreeRef.segmentMap.get(segmentId).refs.forEach((ref: Ref) => {
|
||||
if (ref.traceId === currentTraceId) {
|
||||
this.traverseTree(
|
||||
traceTreeRef.segmentMap.get(ref.parentSegmentId) as Span,
|
||||
ref.parentSpanId,
|
||||
ref.parentSegmentId,
|
||||
traceTreeRef.segmentMap.get(segmentId) as Span,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// set a breakpoint at this line
|
||||
traceTreeRef.segmentMap.forEach((value: Span) => {
|
||||
if ((value.refs && value.refs.length === 0) || !value.refs) {
|
||||
segmentIdList.push(value as Span);
|
||||
}
|
||||
});
|
||||
segmentIdList.forEach((segmentId: Span) => {
|
||||
this.collapse(segmentId);
|
||||
});
|
||||
return segmentIdList;
|
||||
}
|
||||
|
||||
public static changeStatisticsTree(data: Span[]): Map<string, Span[]> {
|
||||
const result = new Map<string, Span[]>();
|
||||
const traceTreeRef = this.changeTreeCore(data);
|
||||
@ -255,47 +226,6 @@ export default class TraceUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static traverseTree(node: Span, spanId: number, segmentId: string, childNode: Span) {
|
||||
if (!node || node.isBroken) {
|
||||
return;
|
||||
}
|
||||
if (node.spanId === spanId && node.segmentId === segmentId) {
|
||||
node.children!.push(childNode);
|
||||
return;
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
for (const grandchild of node.children) {
|
||||
this.traverseTree(grandchild, spanId, segmentId, childNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static getSpanGroupData(groupspans: Span[], groupRef: StatisticsGroupRef): StatisticsSpan {
|
||||
let maxTime = 0;
|
||||
let minTime = 0;
|
||||
let sumTime = 0;
|
||||
const count = groupspans.length;
|
||||
groupspans.forEach((groupspan: Span) => {
|
||||
const duration = groupspan.dur || 0;
|
||||
if (duration > maxTime) {
|
||||
maxTime = duration;
|
||||
}
|
||||
if (duration < minTime) {
|
||||
minTime = duration;
|
||||
}
|
||||
sumTime = sumTime + duration;
|
||||
});
|
||||
const avgTime = count === 0 ? 0 : sumTime / count;
|
||||
return {
|
||||
groupRef,
|
||||
maxTime,
|
||||
minTime,
|
||||
sumTime,
|
||||
avgTime,
|
||||
count,
|
||||
};
|
||||
}
|
||||
|
||||
private static calculationChildren(nodes: Span[], result: Map<string, Span[]>): void {
|
||||
nodes.forEach((node: Span) => {
|
||||
const groupRef = node.endpointName + ":" + node.type;
|
||||
|
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
@ -26,6 +26,5 @@ declare global {
|
||||
interface Window {
|
||||
Promise: any;
|
||||
moment: any;
|
||||
axiosCancel: any;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user