mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-07-05 20:15:25 +00:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
35a1ff9b24 | ||
![]() |
e4a43d91e2 | ||
![]() |
1f651cf528 | ||
![]() |
7dcc67f455 | ||
![]() |
a28972bc5c | ||
![]() |
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 |
@ -169,12 +169,13 @@ limitations under the License. -->
|
||||
value: { type: Date },
|
||||
left: { type: Boolean, default: false },
|
||||
right: { type: Boolean, default: false },
|
||||
dates: { type: Array as PropType<number[] | string[]>, default: () => [] },
|
||||
dates: { type: Array as PropType<Date[]>, default: () => [] },
|
||||
disabledDate: { type: Function, default: () => false },
|
||||
format: {
|
||||
type: String,
|
||||
default: "YYYY-MM-DD",
|
||||
},
|
||||
maxRange: { type: Array as PropType<Date[]>, default: () => [] },
|
||||
});
|
||||
const state = reactive({
|
||||
pre: "",
|
||||
@ -241,6 +242,12 @@ limitations under the License. -->
|
||||
const end = computed(() => {
|
||||
return parse(Number(props.dates[1]));
|
||||
});
|
||||
const minStart = computed(() => {
|
||||
return parse(Number(props.maxRange[0]));
|
||||
});
|
||||
const maxEnd = computed(() => {
|
||||
return parse(Number(props.maxRange[1]));
|
||||
});
|
||||
const ys = computed(() => {
|
||||
return Math.floor(state.year / 10) * 10;
|
||||
});
|
||||
@ -369,7 +376,10 @@ limitations under the License. -->
|
||||
flag = tf(props.value, format) === tf(time, format);
|
||||
}
|
||||
classObj[`${state.pre}-date`] = true;
|
||||
classObj[`${state.pre}-date-disabled`] = (props.right && t < start.value) || props.disabledDate(time, format);
|
||||
const rightDisabled = props.right && (t < start.value || t > maxEnd.value || !props.maxRange?.length);
|
||||
const leftDisabled =
|
||||
props.left && (t < minStart.value || t > end.value || !props.maxRange?.length || t > maxEnd.value);
|
||||
classObj[`${state.pre}-date-disabled`] = rightDisabled || leftDisabled || props.disabledDate(time, format);
|
||||
classObj[`${state.pre}-date-on`] = (props.left && t > start.value) || (props.right && t < end.value);
|
||||
classObj[`${state.pre}-date-selected`] = flag;
|
||||
return classObj;
|
||||
|
@ -68,6 +68,7 @@ limitations under the License. -->
|
||||
:left="true"
|
||||
:disabledDate="disabledDate"
|
||||
:format="format"
|
||||
:maxRange="maxRange"
|
||||
@ok="ok"
|
||||
@setDates="setDates"
|
||||
/>
|
||||
@ -78,6 +79,7 @@ limitations under the License. -->
|
||||
:right="true"
|
||||
:disabledDate="disabledDate"
|
||||
:format="format"
|
||||
:maxRange="maxRange"
|
||||
@ok="ok"
|
||||
@setDates="setDates"
|
||||
/>
|
||||
@ -112,11 +114,11 @@ limitations under the License. -->
|
||||
import { useI18n } from "vue-i18n";
|
||||
import DateCalendar from "./DateCalendar.vue";
|
||||
import { useTimeoutFn } from "@/hooks/useTimeout";
|
||||
/*global defineProps, defineEmits*/
|
||||
/*global PropType, defineProps, defineEmits*/
|
||||
const datepicker = ref(null);
|
||||
const { t } = useI18n();
|
||||
const show = ref<boolean>(false);
|
||||
const dates = ref<Date | string[] | any>([]);
|
||||
const dates = ref<Date[]>([]);
|
||||
const props = defineProps({
|
||||
position: { type: String, default: "bottom" },
|
||||
name: [String],
|
||||
@ -149,7 +151,7 @@ limitations under the License. -->
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
dateRangeSelect: [Function],
|
||||
maxRange: { type: Array as PropType<Date[]>, default: () => [] },
|
||||
});
|
||||
const emit = defineEmits(["clear", "input", "confirm", "cancel"]);
|
||||
const local = computed(() => {
|
||||
|
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;
|
||||
}
|
||||
}
|
||||
|
||||
export const BasePath = `/graphql`;
|
||||
|
||||
export async function httpQuery({
|
||||
url = "",
|
||||
method = "GET",
|
||||
json,
|
||||
headers = {},
|
||||
}: {
|
||||
method: string;
|
||||
json: unknown;
|
||||
headers?: Recordable;
|
||||
url: string;
|
||||
}) {
|
||||
const timeoutId = setTimeout(() => {
|
||||
abortRequestsAndUpdate();
|
||||
}, Timeout);
|
||||
|
||||
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, BasePath } 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 customQuery(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
|
||||
const response = await httpQuery({
|
||||
url: BasePath,
|
||||
method: "post",
|
||||
json: { query: param.queryStr, variables: { ...param.conditions } },
|
||||
});
|
||||
if (response.errors) {
|
||||
response.errors = response.errors.map((e: { message: string }) => e.message).join(" ");
|
||||
}
|
||||
return res;
|
||||
return response;
|
||||
}
|
||||
|
||||
export default query;
|
||||
export default customQuery;
|
@ -49,3 +49,28 @@ export const MenuItems = {
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export const RecordsTTL = {
|
||||
query: `getRecordsTTL {
|
||||
normal
|
||||
trace
|
||||
zipkinTrace
|
||||
log
|
||||
browserErrorLog
|
||||
coldNormal
|
||||
coldTrace
|
||||
coldZipkinTrace
|
||||
coldLog
|
||||
coldBrowserErrorLog
|
||||
}`,
|
||||
};
|
||||
export const MetricsTTL = {
|
||||
query: `getMetricsTTL {
|
||||
minute
|
||||
hour
|
||||
day
|
||||
coldMinute
|
||||
coldHour
|
||||
coldDay
|
||||
}`,
|
||||
};
|
||||
|
@ -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: `
|
||||
|
@ -103,3 +103,63 @@ export const TraceTagValues = {
|
||||
query: `
|
||||
tagValues: queryTraceTagAutocompleteValues(tagKey: $tagKey, duration: $duration)`,
|
||||
};
|
||||
|
||||
export const TraceSpansFromColdStage = {
|
||||
variable: "$traceId: ID!, $duration: Duration!, $debug: Boolean",
|
||||
query: `
|
||||
trace: queryTrace(traceId: $traceId, duration: $duration, debug: $debug) {
|
||||
spans {
|
||||
traceId
|
||||
segmentId
|
||||
spanId
|
||||
parentSpanId
|
||||
refs {
|
||||
traceId
|
||||
parentSegmentId
|
||||
parentSpanId
|
||||
type
|
||||
}
|
||||
serviceCode
|
||||
serviceInstanceName
|
||||
startTime
|
||||
endTime
|
||||
endpointName
|
||||
type
|
||||
peer
|
||||
component
|
||||
isError
|
||||
layer
|
||||
tags {
|
||||
key
|
||||
value
|
||||
}
|
||||
logs {
|
||||
time
|
||||
data {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
attachedEvents {
|
||||
startTime {
|
||||
seconds
|
||||
nanos
|
||||
}
|
||||
event
|
||||
endTime {
|
||||
seconds
|
||||
nanos
|
||||
}
|
||||
tags {
|
||||
key
|
||||
value
|
||||
}
|
||||
summary {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
30
src/graphql/http/index.ts
Normal file
30
src/graphql/http/index.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { httpQuery } from "../base";
|
||||
import { HttpURL } from "./url";
|
||||
|
||||
export default async function fetchQuery({ method, json, path }: { method: string; json?: unknown; path: string }) {
|
||||
const response = await httpQuery({
|
||||
method,
|
||||
json,
|
||||
url: (HttpURL as { [key: string]: string })[path],
|
||||
});
|
||||
if (response.errors) {
|
||||
response.errors = response.errors.map((e: { message: string }) => e.message).join(" ");
|
||||
}
|
||||
return response;
|
||||
}
|
22
src/graphql/http/url.ts
Normal file
22
src/graphql/http/url.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
const PREFIX = process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test" ? "/api" : "";
|
||||
export const HttpURL = {
|
||||
ClusterNodes: `${PREFIX}/status/cluster/nodes`,
|
||||
ConfigTTL: `${PREFIX}/status/config/ttl`,
|
||||
DebuggingConfigDump: `${PREFIX}/debugging/config/dump`,
|
||||
};
|
@ -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, BasePath } 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({
|
||||
url: BasePath,
|
||||
method: "post",
|
||||
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,10 +14,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { OAPTimeInfo, OAPVersion, MenuItems } from "../fragments/app";
|
||||
import { OAPTimeInfo, OAPVersion, MenuItems, MetricsTTL, RecordsTTL } from "../fragments/app";
|
||||
|
||||
export const queryOAPTimeInfo = `query queryOAPTimeInfo {${OAPTimeInfo.query}}`;
|
||||
|
||||
export const queryOAPVersion = `query ${OAPVersion.query}`;
|
||||
|
||||
export const queryMenuItems = `query menuItems {${MenuItems.query}}`;
|
||||
|
||||
export const queryMetricsTTL = `query MetricsTTL {${MetricsTTL.query}}`;
|
||||
|
||||
export const queryRecordsTTL = `query RecordsTTL {${RecordsTTL.query}}`;
|
||||
|
@ -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}}`;
|
||||
|
||||
|
@ -15,12 +15,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Traces, TraceSpans, TraceTagKeys, TraceTagValues } from "../fragments/trace";
|
||||
import { Traces, TraceSpans, TraceTagKeys, TraceTagValues, TraceSpansFromColdStage } from "../fragments/trace";
|
||||
|
||||
export const queryTraces = `query queryTraces(${Traces.variable}) {${Traces.query}}`;
|
||||
|
||||
export const queryTrace = `query queryTrace(${TraceSpans.variable}) {${TraceSpans.query}}`;
|
||||
export const querySpans = `query querySpans(${TraceSpans.variable}) {${TraceSpans.query}}`;
|
||||
|
||||
export const queryTraceTagKeys = `query queryTraceTagKeys(${TraceTagKeys.variable}) {${TraceTagKeys.query}}`;
|
||||
|
||||
export const queryTraceTagValues = `query queryTraceTagValues(${TraceTagValues.variable}) {${TraceTagValues.query}}`;
|
||||
|
||||
export const queryTraceSpansFromColdStage = `query queryTraceSpansFromColdStage(${TraceSpansFromColdStage.variable}) {${TraceSpansFromColdStage.query}}`;
|
||||
|
55
src/hooks/useDuration.ts
Normal file
55
src/hooks/useDuration.ts
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useAppStoreWithOut, InitializationDurationRow } from "@/store/modules/app";
|
||||
import type { Duration, DurationTime } from "@/types/app";
|
||||
import getLocalTime from "@/utils/localtime";
|
||||
import dateFormatStep from "@/utils/dateFormat";
|
||||
|
||||
export function useDuration() {
|
||||
let durationRow: Duration = InitializationDurationRow;
|
||||
|
||||
function getDuration() {
|
||||
const appStore = useAppStoreWithOut();
|
||||
return {
|
||||
start: getLocalTime(appStore.utc, durationRow.start),
|
||||
end: getLocalTime(appStore.utc, durationRow.end),
|
||||
step: durationRow.step,
|
||||
};
|
||||
}
|
||||
function getDurationTime(): DurationTime {
|
||||
const { start, step, end } = getDuration();
|
||||
return {
|
||||
start: dateFormatStep(start, step, true),
|
||||
end: dateFormatStep(end, step, true),
|
||||
step: step,
|
||||
};
|
||||
}
|
||||
function setDurationRow(data: Duration) {
|
||||
durationRow = data;
|
||||
}
|
||||
function getMaxRange(day: number) {
|
||||
if (day === -1) {
|
||||
return [];
|
||||
}
|
||||
const gap = (day + 1) * 24 * 60 * 60 * 1000;
|
||||
const dates: Date[] = [new Date(new Date().getTime() - gap), new Date()];
|
||||
|
||||
return dates;
|
||||
}
|
||||
|
||||
return { setDurationRow, getDurationTime, getMaxRange };
|
||||
}
|
@ -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;
|
||||
|
@ -40,14 +40,25 @@ limitations under the License. -->
|
||||
</el-breadcrumb>
|
||||
<div class="title" v-else>{{ pageTitle }}</div>
|
||||
<div class="app-config">
|
||||
<span class="red" v-show="timeRange">{{ t("timeTips") }}</span>
|
||||
<span class="red" v-show="showTimeRangeTips">{{ t("timeTips") }}</span>
|
||||
<TimePicker
|
||||
:value="[appStore.durationRow.start, appStore.durationRow.end]"
|
||||
:maxRange="appStore.maxRange"
|
||||
position="bottom"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@input="changeTimeRange"
|
||||
/>
|
||||
<span> UTC{{ appStore.utcHour >= 0 ? "+" : "" }}{{ `${appStore.utcHour}:${appStore.utcMin}` }} </span>
|
||||
<span class="ml-5">
|
||||
<el-switch
|
||||
v-model="coldStage"
|
||||
inline-prompt
|
||||
active-text="Active Data"
|
||||
inactive-text="Cold Data"
|
||||
@change="changeDataMode"
|
||||
width="90px"
|
||||
/>
|
||||
</span>
|
||||
<span class="ml-5" ref="themeSwitchRef">
|
||||
<el-switch
|
||||
v-model="theme"
|
||||
@ -75,7 +86,7 @@ limitations under the License. -->
|
||||
<script lang="ts" setup>
|
||||
import { Themes } from "@/constants/data";
|
||||
import router from "@/router";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useAppStoreWithOut, InitializationDurationRow } from "@/store/modules/app";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import type { DashboardItem } from "@/types/dashboard";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
@ -92,10 +103,11 @@ limitations under the License. -->
|
||||
const dashboardStore = useDashboardStore();
|
||||
const route = useRoute();
|
||||
const pathNames = ref<{ path?: string; name: string; selected: boolean }[][]>([]);
|
||||
const timeRange = ref<number>(0);
|
||||
const showTimeRangeTips = ref<boolean>(false);
|
||||
const pageTitle = ref<string>("");
|
||||
const theme = ref<boolean>(true);
|
||||
const themeSwitchRef = ref<HTMLElement>();
|
||||
const coldStage = ref<boolean>(false);
|
||||
|
||||
const savedTheme = window.localStorage.getItem("theme-is-dark");
|
||||
if (savedTheme === "false") {
|
||||
@ -110,6 +122,7 @@ limitations under the License. -->
|
||||
resetDuration();
|
||||
getVersion();
|
||||
getNavPaths();
|
||||
setTTL();
|
||||
|
||||
function changeTheme() {
|
||||
const root = document.documentElement;
|
||||
@ -126,6 +139,24 @@ limitations under the License. -->
|
||||
window.localStorage.setItem("theme-is-dark", String(theme.value));
|
||||
}
|
||||
|
||||
function changeDataMode() {
|
||||
appStore.setColdStageMode(coldStage.value);
|
||||
if (coldStage.value) {
|
||||
handleMetricsTTL({
|
||||
minute: appStore.metricsTTL.coldMinute,
|
||||
hour: appStore.metricsTTL.coldHour,
|
||||
day: appStore.metricsTTL.coldDay,
|
||||
});
|
||||
} else {
|
||||
handleMetricsTTL({
|
||||
minute: appStore.metricsTTL.minute,
|
||||
hour: appStore.metricsTTL.hour,
|
||||
day: appStore.metricsTTL.day,
|
||||
});
|
||||
}
|
||||
appStore.setDuration(InitializationDurationRow);
|
||||
}
|
||||
|
||||
function handleChangeTheme() {
|
||||
const x = themeSwitchRef.value?.offsetLeft ?? 0;
|
||||
const y = themeSwitchRef.value?.offsetTop ?? 0;
|
||||
@ -184,13 +215,49 @@ limitations under the License. -->
|
||||
}
|
||||
|
||||
function changeTimeRange(val: Date[]) {
|
||||
timeRange.value = val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000 ? 1 : 0;
|
||||
if (timeRange.value) {
|
||||
showTimeRangeTips.value = val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000;
|
||||
if (showTimeRangeTips.value) {
|
||||
return;
|
||||
}
|
||||
appStore.setDuration(timeFormat(val));
|
||||
}
|
||||
|
||||
async function setTTL() {
|
||||
await getMetricsTTL();
|
||||
await getRecordsTTL();
|
||||
changeDataMode();
|
||||
}
|
||||
async function getRecordsTTL() {
|
||||
const resp = await appStore.queryRecordsTTL();
|
||||
if (resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
}
|
||||
}
|
||||
|
||||
async function getMetricsTTL() {
|
||||
const resp = await appStore.queryMetricsTTL();
|
||||
if (resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMetricsTTL({ minute, hour, day }: { minute: number; hour: number; day: number }) {
|
||||
if (minute === -1 || hour === -1 || day === -1) {
|
||||
return appStore.setMaxRange([]);
|
||||
}
|
||||
if (!day) {
|
||||
return appStore.setMaxRange([]);
|
||||
}
|
||||
const gap = Math.max(day, hour, minute);
|
||||
const dates: Date[] = [new Date(new Date().getTime() - dayToMS(gap + 1)), new Date()];
|
||||
|
||||
appStore.setMaxRange(dates);
|
||||
}
|
||||
|
||||
function dayToMS(day: number) {
|
||||
return day * 24 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
function getNavPaths() {
|
||||
pathNames.value = [];
|
||||
pageTitle.value = "";
|
||||
|
@ -397,5 +397,14 @@ const msg = {
|
||||
instances: "Instances",
|
||||
snapshot: "Snapshot",
|
||||
expression: "Expression",
|
||||
metricsTTL: "Metrics TTL (day)",
|
||||
recordsTTL: "Records TTL (day)",
|
||||
clusterNodes: "Cluster Nodes",
|
||||
debuggingConfigDump: "Dump Effective Configurations",
|
||||
customDuration: "Custom Duration",
|
||||
maxDuration: "Max Duration",
|
||||
minutes: "Minutes",
|
||||
invalidProfilingDurationRange: "Please enter a valid duration between 1 and 900 seconds",
|
||||
taskCreatedSuccessfully: "Task created successfully",
|
||||
};
|
||||
export default msg;
|
||||
|
@ -397,5 +397,14 @@ const msg = {
|
||||
snapshot: "Snapshot",
|
||||
expression: "Expression",
|
||||
asSelector: "As Selector",
|
||||
metricsTTL: "Metrics TTL (day)",
|
||||
recordsTTL: "Records TTL (day)",
|
||||
clusterNodes: "Cluster Nodes",
|
||||
debuggingConfigDump: "Dump Effective Configurations",
|
||||
customDuration: "Duración Personalizada",
|
||||
maxDuration: "Duración Máxima",
|
||||
minutes: "Minutos",
|
||||
invalidProfilingDurationRange: "Por favor ingrese una duración válida entre 1 y 900 segundos",
|
||||
taskCreatedSuccessfully: "Tarea creada exitosamente",
|
||||
};
|
||||
export default msg;
|
||||
|
@ -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;
|
||||
|
@ -395,5 +395,14 @@ const msg = {
|
||||
instances: "实例",
|
||||
snapshot: "快照",
|
||||
expression: "表达式",
|
||||
metricsTTL: "Metrics TTL (day)",
|
||||
recordsTTL: "Records TTL (day)",
|
||||
clusterNodes: "集群节点",
|
||||
debuggingConfigDump: "转储有效配置",
|
||||
customDuration: "自定义时长",
|
||||
maxDuration: "最大时长",
|
||||
minutes: "分钟",
|
||||
invalidProfilingDurationRange: "请输入1到900秒之间的有效时长",
|
||||
taskCreatedSuccessfully: "任务创建成功",
|
||||
};
|
||||
export default msg;
|
||||
|
@ -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 = "";
|
||||
|
@ -15,6 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { WidgetType } from "@/views/dashboard/data";
|
||||
import { HotAndWarmOpt } from "@/views/settings/data";
|
||||
|
||||
export const NewControl = {
|
||||
x: 0,
|
||||
@ -58,3 +59,18 @@ export enum EBPFProfilingTriggerType {
|
||||
}
|
||||
|
||||
export const EndpointsTopNDefault = 20;
|
||||
|
||||
export const TTLTypes = {
|
||||
HotAndWarm: "Hot / Warm",
|
||||
Cold: "Cold",
|
||||
};
|
||||
export const TTLColdMap: Indexable<string> = {
|
||||
coldDay: HotAndWarmOpt[0],
|
||||
coldHour: HotAndWarmOpt[1],
|
||||
coldMinute: HotAndWarmOpt[2],
|
||||
coldNormal: HotAndWarmOpt[3],
|
||||
coldTrace: HotAndWarmOpt[4],
|
||||
coldZipkinTrace: HotAndWarmOpt[5],
|
||||
coldLog: HotAndWarmOpt[6],
|
||||
coldBrowserErrorLog: HotAndWarmOpt[7],
|
||||
};
|
||||
|
@ -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,14 +19,13 @@ 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";
|
||||
import type { MenuOptions, SubItem, MetricsTTL, RecordsTTL } from "@/types/app";
|
||||
import { Themes } from "@/constants/data";
|
||||
/*global Nullable*/
|
||||
interface AppState {
|
||||
durationRow: Recordable;
|
||||
durationRow: Duration;
|
||||
utc: string;
|
||||
utcHour: number;
|
||||
utcMin: number;
|
||||
@ -38,16 +37,22 @@ interface AppState {
|
||||
reloadTimer: Nullable<IntervalHandle>;
|
||||
allMenus: MenuOptions[];
|
||||
theme: string;
|
||||
coldStageMode: boolean;
|
||||
maxRange: Date[];
|
||||
metricsTTL: Recordable<MetricsTTL>;
|
||||
recordsTTL: Recordable<RecordsTTL>;
|
||||
}
|
||||
|
||||
export const InitializationDurationRow = {
|
||||
start: new Date(new Date().getTime() - 1800000),
|
||||
end: new Date(),
|
||||
step: TimeType.MINUTE_TIME,
|
||||
};
|
||||
|
||||
export const appStore = defineStore({
|
||||
id: "app",
|
||||
state: (): AppState => ({
|
||||
durationRow: {
|
||||
start: new Date(new Date().getTime() - 1800000),
|
||||
end: new Date(),
|
||||
step: TimeType.MINUTE_TIME,
|
||||
},
|
||||
durationRow: InitializationDurationRow,
|
||||
utc: "",
|
||||
utcHour: 0,
|
||||
utcMin: 0,
|
||||
@ -59,6 +64,10 @@ export const appStore = defineStore({
|
||||
reloadTimer: null,
|
||||
allMenus: [],
|
||||
theme: Themes.Dark,
|
||||
coldStageMode: false,
|
||||
maxRange: [],
|
||||
metricsTTL: {},
|
||||
recordsTTL: {},
|
||||
}),
|
||||
getters: {
|
||||
duration(): Duration {
|
||||
@ -118,17 +127,14 @@ 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) {
|
||||
this.durationRow = data;
|
||||
},
|
||||
setMaxRange(times: Date[]) {
|
||||
this.maxRange = times;
|
||||
},
|
||||
setTheme(data: string) {
|
||||
this.theme = data;
|
||||
},
|
||||
@ -150,6 +156,9 @@ export const appStore = defineStore({
|
||||
setAutoRefresh(auto: boolean) {
|
||||
this.autoRefresh = auto;
|
||||
},
|
||||
setColdStageMode(mode: boolean) {
|
||||
this.coldStageMode = mode;
|
||||
},
|
||||
runEventStack() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
@ -185,11 +194,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 +206,37 @@ 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;
|
||||
},
|
||||
async queryMetricsTTL() {
|
||||
const response = await graphql.query("queryMetricsTTL").params({});
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
this.metricsTTL = response.data.getMetricsTTL;
|
||||
return response.data;
|
||||
},
|
||||
async queryRecordsTTL() {
|
||||
const res = await graphql.query("queryRecordsTTL").params({});
|
||||
if (res.errors) {
|
||||
return res;
|
||||
}
|
||||
this.recordsTTL = res.data.getRecordsTTL;
|
||||
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 customQuery from "@/graphql/custom-query";
|
||||
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 customQuery(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,10 +18,10 @@ 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";
|
||||
import { useDuration } from "@/hooks/useDuration";
|
||||
import { EndpointsTopNDefault } from "../data";
|
||||
|
||||
interface LogState {
|
||||
@ -34,6 +34,7 @@ interface LogState {
|
||||
logs: Recordable[];
|
||||
loadLogs: boolean;
|
||||
}
|
||||
const { getDurationTime } = useDuration();
|
||||
|
||||
export const logStore = defineStore({
|
||||
id: "log",
|
||||
@ -42,7 +43,7 @@ export const logStore = defineStore({
|
||||
instances: [{ value: "0", label: "All" }],
|
||||
endpoints: [{ value: "0", label: "All" }],
|
||||
conditions: {
|
||||
queryDuration: useAppStoreWithOut().durationTime,
|
||||
queryDuration: getDurationTime(),
|
||||
paging: { pageNum: 1, pageSize: 15 },
|
||||
},
|
||||
supportQueryLogsByKeywords: true,
|
||||
@ -57,56 +58,56 @@ export const logStore = defineStore({
|
||||
resetState() {
|
||||
this.logs = [];
|
||||
this.conditions = {
|
||||
queryDuration: useAppStoreWithOut().durationTime,
|
||||
queryDuration: getDurationTime(),
|
||||
paging: { pageNum: 1, pageSize: 15 },
|
||||
};
|
||||
},
|
||||
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 +118,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;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
80
src/store/modules/settings.ts
Normal file
80
src/store/modules/settings.ts
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "@/store";
|
||||
import fetchQuery from "@/graphql/http";
|
||||
import type { ClusterNode, ConfigTTL } from "@/types/settings";
|
||||
import { HotAndWarmOpt } from "@/views/settings/data";
|
||||
import { TTLTypes, TTLColdMap } from "../data";
|
||||
|
||||
interface SettingsState {
|
||||
clusterNodes: ClusterNode[];
|
||||
debuggingConfig: Indexable<string>;
|
||||
configTTL: Recordable<ConfigTTL>;
|
||||
}
|
||||
|
||||
export const settingsStore = defineStore({
|
||||
id: "settings",
|
||||
state: (): SettingsState => ({
|
||||
clusterNodes: [],
|
||||
debuggingConfig: {},
|
||||
configTTL: {},
|
||||
}),
|
||||
actions: {
|
||||
async getClusterNodes() {
|
||||
const response = await fetchQuery({
|
||||
method: "get",
|
||||
path: "ClusterNodes",
|
||||
});
|
||||
this.clusterNodes = response.nodes;
|
||||
return response;
|
||||
},
|
||||
async getConfigTTL() {
|
||||
const response = await fetchQuery({
|
||||
method: "get",
|
||||
path: "ConfigTTL",
|
||||
});
|
||||
for (const item of Object.keys(response)) {
|
||||
const rows = [];
|
||||
const row: Indexable<string> = { type: TTLTypes.HotAndWarm };
|
||||
const rowCold: Indexable<string> = { type: TTLTypes.Cold };
|
||||
for (const key of Object.keys(response[item])) {
|
||||
if (HotAndWarmOpt.includes(key)) {
|
||||
row[key] = response[item][key];
|
||||
} else {
|
||||
rowCold[TTLColdMap[key] as string] = response[item][key];
|
||||
}
|
||||
}
|
||||
rows.push(row, rowCold);
|
||||
this.configTTL[item] = rows;
|
||||
}
|
||||
return response;
|
||||
},
|
||||
async getDebuggingConfigDump() {
|
||||
const response = await fetchQuery({
|
||||
method: "get",
|
||||
path: "DebuggingConfigDump",
|
||||
});
|
||||
this.debuggingConfig = response;
|
||||
return response;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export function useSettingsStore(): Recordable {
|
||||
return settingsStore(store);
|
||||
}
|
@ -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 customQuery from "@/graphql/custom-query";
|
||||
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 customQuery({ 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 customQuery(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,11 +19,11 @@ 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";
|
||||
import { EndpointsTopNDefault } from "../data";
|
||||
import { useDuration } from "@/hooks/useDuration";
|
||||
interface TraceState {
|
||||
services: Service[];
|
||||
instances: Instance[];
|
||||
@ -34,7 +34,9 @@ interface TraceState {
|
||||
conditions: Recordable;
|
||||
traceSpanLogs: Recordable[];
|
||||
selectorStore: Recordable;
|
||||
selectedSpan: Recordable<Span>;
|
||||
}
|
||||
const { getDurationTime } = useDuration();
|
||||
|
||||
export const traceStore = defineStore({
|
||||
id: "trace",
|
||||
@ -45,8 +47,9 @@ export const traceStore = defineStore({
|
||||
traceList: [],
|
||||
traceSpans: [],
|
||||
currentTrace: {},
|
||||
selectedSpan: {},
|
||||
conditions: {
|
||||
queryDuration: useAppStoreWithOut().durationTime,
|
||||
queryDuration: getDurationTime(),
|
||||
traceState: "ALL",
|
||||
queryOrder: QueryOrders[0].value,
|
||||
paging: { pageNum: 1, pageSize: 20 },
|
||||
@ -64,137 +67,138 @@ export const traceStore = defineStore({
|
||||
setTraceSpans(spans: Span[]) {
|
||||
this.traceSpans = spans;
|
||||
},
|
||||
setSelectedSpan(span: Span) {
|
||||
this.selectedSpan = span;
|
||||
},
|
||||
resetState() {
|
||||
this.traceSpans = [];
|
||||
this.traceList = [];
|
||||
this.currentTrace = {};
|
||||
this.conditions = {
|
||||
queryDuration: useAppStoreWithOut().durationTime,
|
||||
queryDuration: getDurationTime(),
|
||||
paging: { pageNum: 1, pageSize: 20 },
|
||||
traceState: "ALL",
|
||||
queryOrder: QueryOrders[0].value,
|
||||
};
|
||||
},
|
||||
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 appStore = useAppStoreWithOut();
|
||||
let response;
|
||||
if (appStore.coldStageMode) {
|
||||
response = await graphql
|
||||
.query("queryTraceSpansFromColdStage")
|
||||
.params({ ...params, duration: this.conditions.queryDuration });
|
||||
} else {
|
||||
response = await graphql.query("querySpans").params(params);
|
||||
}
|
||||
const data = res.data.data.trace.spans;
|
||||
if (response.errors) {
|
||||
return response;
|
||||
}
|
||||
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 });
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -39,14 +39,12 @@ export type EventParams = {
|
||||
seriesIndex: number;
|
||||
seriesName: string;
|
||||
name: string;
|
||||
dataIndex: number;
|
||||
data: unknown;
|
||||
dataType: string;
|
||||
value: number | any[];
|
||||
color: string;
|
||||
event: Record<string, T>;
|
||||
event: Recordable;
|
||||
dataIndex: number;
|
||||
event: any;
|
||||
};
|
||||
|
||||
export interface MenuOptions extends SubItem {
|
||||
@ -68,3 +66,25 @@ export interface SubItem {
|
||||
descKey: string;
|
||||
i18nKey: string;
|
||||
}
|
||||
|
||||
export interface MetricsTTL {
|
||||
minute: number;
|
||||
hour: number;
|
||||
day: number;
|
||||
coldMinute: number;
|
||||
coldHour: number;
|
||||
coldDay: number;
|
||||
}
|
||||
|
||||
export interface RecordsTTL {
|
||||
normal: number;
|
||||
trace: number;
|
||||
zipkinTrace: number;
|
||||
log: number;
|
||||
browserErrorLog: number;
|
||||
coldNormal: number;
|
||||
coldTrace: number;
|
||||
coldZipkinTrace: number;
|
||||
coldLog: number;
|
||||
coldBrowserErrorLog: number;
|
||||
}
|
2
src/types/components.d.ts
vendored
2
src/types/components.d.ts
vendored
@ -42,6 +42,8 @@ declare module 'vue' {
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
Graph: typeof import('./../components/Graph/Graph.vue')['default']
|
||||
|
@ -35,7 +35,7 @@ export interface MonitorInstance {
|
||||
lastTriggerTimestamp: number;
|
||||
processes: MonitorProcess[];
|
||||
}
|
||||
interface MonitorProcess {
|
||||
export interface MonitorProcess {
|
||||
id: string;
|
||||
name: string;
|
||||
detectType: string;
|
@ -60,10 +60,7 @@ export type Filters = {
|
||||
dataIndex: number;
|
||||
sourceId: string;
|
||||
isRange?: boolean;
|
||||
duration?: {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
};
|
||||
duration?: DurationTime;
|
||||
traceId?: string;
|
||||
spanId?: string;
|
||||
segmentId?: string;
|
@ -15,7 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Process } from "./selector";
|
||||
import type { Process as selectorProcess } from "./selector";
|
||||
|
||||
export interface EBPFTaskCreationRequest {
|
||||
serviceId: string;
|
||||
processLabels: string[];
|
||||
@ -58,12 +59,12 @@ interface ProfilingCause {
|
||||
export interface EBPFProfilingSchedule {
|
||||
scheduleId: string;
|
||||
taskId: string;
|
||||
process: Process;
|
||||
process: selectorProcess;
|
||||
endTime: number;
|
||||
startTime: number;
|
||||
}
|
||||
|
||||
export type Process = Process;
|
||||
export type Process = selectorProcess;
|
||||
export type StackElement = {
|
||||
id: string;
|
||||
originId: string;
|
||||
@ -106,7 +107,6 @@ export type ProcessNode = {
|
||||
serviceName: string;
|
||||
serviceInstanceId: string;
|
||||
serviceInstanceName: string;
|
||||
name: string;
|
||||
isReal: boolean;
|
||||
x?: number;
|
||||
y?: number;
|
@ -14,11 +14,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import type { Duration } from "./app";
|
||||
|
||||
const enum EventType {
|
||||
ALL = "",
|
||||
NORMAL = "Normal",
|
||||
ERROR = "Error",
|
||||
}
|
||||
|
||||
export type Event = {
|
||||
uuid: string;
|
||||
source: SourceInput;
|
||||
name: string;
|
||||
type: string;
|
||||
type: EventType;
|
||||
message: string;
|
||||
parameters: { key: string; value: string }[];
|
||||
startTime: number | string;
|
@ -42,16 +42,6 @@ export type Endpoint = {
|
||||
merge?: string;
|
||||
};
|
||||
|
||||
export type Service = {
|
||||
id: string;
|
||||
value: string;
|
||||
label: string;
|
||||
group: string;
|
||||
normal: boolean;
|
||||
layers: string[];
|
||||
shortName: string;
|
||||
};
|
||||
|
||||
export type Process = {
|
||||
id: string;
|
||||
name: string;
|
@ -15,11 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
const CancelToken = axios.CancelToken;
|
||||
import type { MetricsTTL, RecordsTTL } from "@/types/app";
|
||||
|
||||
export const cancelToken = (): any =>
|
||||
new CancelToken(function executor(c) {
|
||||
const w = window as any;
|
||||
(w.axiosCancel || []).push(c);
|
||||
});
|
||||
export type ClusterNode = {
|
||||
host: string;
|
||||
port: number;
|
||||
self: boolean;
|
||||
};
|
||||
|
||||
export type ConfigTTL = {
|
||||
metrics: MetricsTTL;
|
||||
records: RecordsTTL;
|
||||
};
|
@ -47,10 +47,10 @@ export interface Span {
|
||||
tags?: Array<Map<string, string>>;
|
||||
logs?: log[];
|
||||
parentSegmentId?: string;
|
||||
refs?: Ref[];
|
||||
key?: string;
|
||||
}
|
||||
export type Ref = {
|
||||
type: string;
|
||||
type?: string;
|
||||
parentSegmentId: string;
|
||||
parentSpanId: number;
|
||||
traceId: string;
|
||||
@ -60,13 +60,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;
|
||||
@ -81,7 +74,7 @@ export interface StatisticsGroupRef {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export class TraceTreeRef {
|
||||
export interface TraceTreeRef {
|
||||
segmentMap: Map<string, Span>;
|
||||
segmentIdGroup: string[];
|
||||
}
|
@ -13,172 +13,8 @@ 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="settings">
|
||||
<div class="flex-h item">
|
||||
<span class="label">{{ t("language") }}</span>
|
||||
<Selector
|
||||
v-model="lang"
|
||||
:options="Languages"
|
||||
placeholder="Select a language"
|
||||
@change="setLang"
|
||||
size="small"
|
||||
style="font-size: 14px; width: 180px"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-h item">
|
||||
<span class="label">{{ t("serverZone") }}</span>
|
||||
<div>
|
||||
<span>UTC</span>
|
||||
<span class="ml-5 mr-5">{{ utcHour >= 0 ? "+" : "" }}</span>
|
||||
<input type="number" v-model="utcHour" min="-12" max="14" class="utc-input" @change="setUTCHour" />
|
||||
<span class="ml-5 mr-5">:</span>
|
||||
<span class="utc-min">{{ utcMin > 9 || utcMin === 0 ? null : 0 }}</span>
|
||||
<input type="number" v-model="utcMin" min="0" max="59" class="utc-input" @change="setUTCMin" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-h item">
|
||||
<span class="label">{{ t("auto") }}</span>
|
||||
<el-switch v-model="auto" @change="handleAuto" style="height: 25px" />
|
||||
<div class="auto-time ml-5">
|
||||
<span class="auto-select">
|
||||
<input type="number" v-model="autoTime" @change="changeAutoTime" min="1" />
|
||||
</span>
|
||||
{{ t("second") }}
|
||||
<i class="ml-10">{{ t("timeReload") }}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Content />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import { Languages } from "@/constants/data";
|
||||
import Selector from "@/components/Selector.vue";
|
||||
import getLocalTime from "@/utils/localtime";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const lang = ref<string>(locale.value || "en");
|
||||
const autoTime = ref<number>(6);
|
||||
const auto = ref<boolean>(appStore.autoRefresh || false);
|
||||
const utcHour = ref<number>(appStore.utcHour);
|
||||
const utcMin = ref<number>(appStore.utcMin);
|
||||
|
||||
const handleReload = () => {
|
||||
const gap = appStore.duration.end.getTime() - appStore.duration.start.getTime();
|
||||
const dates: Date[] = [
|
||||
getLocalTime(appStore.utc, new Date(new Date().getTime() - gap)),
|
||||
getLocalTime(appStore.utc, new Date()),
|
||||
];
|
||||
appStore.setDuration(timeFormat(dates));
|
||||
};
|
||||
const handleAuto = () => {
|
||||
if (autoTime.value < 1) {
|
||||
return;
|
||||
}
|
||||
appStore.setAutoRefresh(auto.value);
|
||||
if (auto.value) {
|
||||
handleReload();
|
||||
appStore.setReloadTimer(setInterval(handleReload, autoTime.value * 1000));
|
||||
} else {
|
||||
if (appStore.reloadTimer) {
|
||||
clearInterval(appStore.reloadTimer);
|
||||
}
|
||||
}
|
||||
};
|
||||
const changeAutoTime = () => {
|
||||
if (autoTime.value < 1) {
|
||||
return;
|
||||
}
|
||||
if (appStore.reloadTimer) {
|
||||
clearInterval(appStore.reloadTimer);
|
||||
}
|
||||
if (auto.value) {
|
||||
handleReload();
|
||||
appStore.setReloadTimer(setInterval(handleReload, autoTime.value * 1000));
|
||||
}
|
||||
};
|
||||
const setLang = (): void => {
|
||||
locale.value = lang.value;
|
||||
window.localStorage.setItem("language", lang.value);
|
||||
};
|
||||
const setUTCHour = () => {
|
||||
if (utcHour.value < -12) {
|
||||
utcHour.value = -12;
|
||||
}
|
||||
if (utcHour.value > 14) {
|
||||
utcHour.value = 14;
|
||||
}
|
||||
if (isNaN(utcHour.value)) {
|
||||
utcHour.value = 0;
|
||||
}
|
||||
appStore.setUTC(utcHour.value, utcMin.value);
|
||||
};
|
||||
const setUTCMin = () => {
|
||||
if (utcMin.value < 0) {
|
||||
utcMin.value = 0;
|
||||
}
|
||||
if (utcMin.value > 59) {
|
||||
utcMin.value = 59;
|
||||
}
|
||||
if (isNaN(utcMin.value)) {
|
||||
utcMin.value = 0;
|
||||
}
|
||||
appStore.setUTC(utcHour.value, utcMin.value);
|
||||
};
|
||||
import Content from "./settings/Index.vue";
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.utc-input {
|
||||
color: inherit;
|
||||
background: 0;
|
||||
border: 0;
|
||||
outline: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.utc-min {
|
||||
display: inline-block;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.auto-select {
|
||||
border-radius: 3px;
|
||||
background-color: $theme-background;
|
||||
padding: 1px;
|
||||
|
||||
input {
|
||||
width: 38px;
|
||||
border-style: unset;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.settings {
|
||||
color: var(--sw-setting-color);
|
||||
font-size: 13px;
|
||||
padding: 20px;
|
||||
|
||||
.item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
input {
|
||||
outline: 0;
|
||||
width: 50px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid $disabled-color;
|
||||
text-align: center;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 180px;
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
color: $font-color;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -30,6 +30,16 @@ limitations under the License. -->
|
||||
<span class="grey">{{ t("searchKeyword") }}: </span>
|
||||
<el-input size="small" v-model="keyword" class="alarm-tool-input" @change="refreshAlarms({ pageNum: 1 })" />
|
||||
</div>
|
||||
<div>
|
||||
<span class="sm b grey mr-5">{{ t("timeRange") }}:</span>
|
||||
<TimePicker
|
||||
:value="[durationRow.start, durationRow.end]"
|
||||
:maxRange="maxRange"
|
||||
position="bottom"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@input="changeDuration"
|
||||
/>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-model="pageNum"
|
||||
@ -51,31 +61,40 @@ limitations under the License. -->
|
||||
</nav>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ElMessage } from "element-plus";
|
||||
import ConditionTags from "@/views/components/ConditionTags.vue";
|
||||
import { AlarmOptions } from "./data";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useAppStoreWithOut, InitializationDurationRow } from "@/store/modules/app";
|
||||
import { useAlarmStore } from "@/store/modules/alarm";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useDuration } from "@/hooks/useDuration";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import type { DurationTime, Duration } from "@/types/app";
|
||||
import { Themes } from "@/constants/data";
|
||||
|
||||
/*global Recordable */
|
||||
const appStore = useAppStoreWithOut();
|
||||
const alarmStore = useAlarmStore();
|
||||
const { t } = useI18n();
|
||||
const { setDurationRow, getDurationTime, getMaxRange } = useDuration();
|
||||
const pageSize = 20;
|
||||
const entity = ref<string>("");
|
||||
const keyword = ref<string>("");
|
||||
const pageNum = ref<number>(1);
|
||||
const duration = ref<DurationTime>(getDurationTime());
|
||||
const durationRow = ref<Duration>(InitializationDurationRow);
|
||||
const total = computed(() =>
|
||||
alarmStore.alarms.length === pageSize ? pageSize * pageNum.value + 1 : pageSize * pageNum.value,
|
||||
);
|
||||
const maxRange = computed(() =>
|
||||
getMaxRange(appStore.coldStageMode ? appStore.recordsTTL.coldNormal : appStore.recordsTTL.normal),
|
||||
);
|
||||
|
||||
refreshAlarms({ pageNum: 1 });
|
||||
|
||||
async function refreshAlarms(param: { pageNum: number; tagsMap?: any }) {
|
||||
const params: any = {
|
||||
duration: appStore.durationTime,
|
||||
async function refreshAlarms(param: { pageNum: number; tagsMap?: Recordable }) {
|
||||
const params: Recordable = {
|
||||
duration: duration.value,
|
||||
paging: {
|
||||
pageNum: param.pageNum,
|
||||
pageSize,
|
||||
@ -91,7 +110,14 @@ limitations under the License. -->
|
||||
}
|
||||
}
|
||||
|
||||
function changeEntity(param: any) {
|
||||
function changeDuration(val: Date[]) {
|
||||
durationRow.value = timeFormat(val);
|
||||
setDurationRow(durationRow.value);
|
||||
duration.value = getDurationTime();
|
||||
refreshAlarms({ pageNum: 1 });
|
||||
}
|
||||
|
||||
function changeEntity(param: { value: string }[]) {
|
||||
entity.value = param[0].value;
|
||||
refreshAlarms({ pageNum: 1 });
|
||||
}
|
||||
@ -100,6 +126,16 @@ limitations under the License. -->
|
||||
pageNum.value = p;
|
||||
refreshAlarms({ pageNum: p });
|
||||
}
|
||||
|
||||
watch(
|
||||
() => appStore.coldStageMode,
|
||||
() => {
|
||||
durationRow.value = InitializationDurationRow;
|
||||
setDurationRow(durationRow.value);
|
||||
duration.value = getDurationTime();
|
||||
refreshAlarms({ pageNum: 1 });
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.alarm-tool {
|
||||
|
@ -25,12 +25,25 @@ limitations under the License. -->
|
||||
:options="asyncProfilingStore.instances"
|
||||
placeholder="Select instances"
|
||||
@change="changeInstances"
|
||||
:filterable="false"
|
||||
:filterable="true"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label">{{ t("duration") }}</div>
|
||||
<Radio class="mb-5" :value="duration" :options="DurationOptions" @change="changeDuration" />
|
||||
<div v-if="duration === DurationOptions[5].value" class="custom-duration">
|
||||
<div class="label">{{ t("customDuration") }} ({{ t("seconds") }})</div>
|
||||
<el-input
|
||||
size="small"
|
||||
class="profile-input"
|
||||
v-model="customDurationSeconds"
|
||||
type="number"
|
||||
:min="1"
|
||||
:max="900"
|
||||
placeholder="Enter duration in seconds (1-900)"
|
||||
/>
|
||||
<div class="hint">{{ t("maxDuration") }}: 900 {{ t("seconds") }} (15 {{ t("minutes") }})</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label">{{ t("profilingEvents") }}</div>
|
||||
@ -113,6 +126,7 @@ limitations under the License. -->
|
||||
const execArgs = ref<string>("");
|
||||
const loading = ref<boolean>(false);
|
||||
const PartofEvents = [ProfilingEvents[3], ProfilingEvents[4], ProfilingEvents[5]];
|
||||
const customDurationSeconds = ref<number>(60);
|
||||
|
||||
function changeDuration(val: string) {
|
||||
duration.value = val;
|
||||
@ -138,10 +152,22 @@ limitations under the License. -->
|
||||
}
|
||||
|
||||
async function createTask() {
|
||||
let finalDuration: number;
|
||||
|
||||
if (duration.value === DurationOptions[5].value) {
|
||||
if (!customDurationSeconds.value || customDurationSeconds.value < 1 || customDurationSeconds.value > 900) {
|
||||
ElMessage.error(t("invalidProfilingDurationRange"));
|
||||
return;
|
||||
}
|
||||
finalDuration = customDurationSeconds.value;
|
||||
} else {
|
||||
finalDuration = Number(duration.value);
|
||||
}
|
||||
|
||||
const params = {
|
||||
serviceId: selectorStore.currentService.id,
|
||||
serviceInstanceIds: serviceInstanceIds.value,
|
||||
duration: Number(duration.value) * 60,
|
||||
duration: finalDuration,
|
||||
events: asyncEvents.value,
|
||||
execArgs: execArgs.value,
|
||||
};
|
||||
@ -158,7 +184,7 @@ limitations under the License. -->
|
||||
return;
|
||||
}
|
||||
emits("close");
|
||||
ElMessage.success("Task created successfully");
|
||||
ElMessage.success(t("taskCreatedSuccessfully"));
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@ -184,4 +210,13 @@ limitations under the License. -->
|
||||
width: 600px;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.custom-duration {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: $font-size-smaller;
|
||||
color: var(--text-color-placeholder);
|
||||
}
|
||||
</style>
|
||||
|
@ -16,9 +16,12 @@
|
||||
*/
|
||||
|
||||
export const DurationOptions = [
|
||||
{ value: "5", label: "5 min" },
|
||||
{ value: "10", label: "10 min" },
|
||||
{ value: "15", label: "15 min" },
|
||||
{ value: "30", label: "30 sec" },
|
||||
{ value: "60", label: "1 min" },
|
||||
{ value: "300", label: "5 min" },
|
||||
{ value: "600", label: "10 min" },
|
||||
{ value: "900", label: "15 min" },
|
||||
{ value: "custom", label: "Custom" },
|
||||
];
|
||||
|
||||
export const ProfilingEvents = ["CPU", "ALLOC", "LOCK", "WALL", "CTIMER", "ITIMER"];
|
||||
|
@ -56,6 +56,16 @@ limitations under the License. -->
|
||||
@change="changeField('category', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span class="sm b grey mr-5">{{ t("timeRange") }}:</span>
|
||||
<TimePicker
|
||||
:value="[durationRow.start, durationRow.end]"
|
||||
:maxRange="maxRange"
|
||||
position="bottom"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@input="changeDuration"
|
||||
/>
|
||||
</div>
|
||||
<el-button class="search-btn" size="small" type="primary" @click="searchLogs">
|
||||
{{ t("search") }}
|
||||
</el-button>
|
||||
@ -119,20 +129,21 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch, onUnmounted } from "vue";
|
||||
import { ref, reactive, watch, onUnmounted, computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { Option } from "@/types/app";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useLogStore } from "@/store/modules/log";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useAppStoreWithOut, InitializationDurationRow } from "@/store/modules/app";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { useDuration } from "@/hooks/useDuration";
|
||||
import ConditionTags from "@/views/components/ConditionTags.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import { EntityType } from "../../data";
|
||||
import { ErrorCategory } from "./data";
|
||||
import type { LayoutConfig } from "@/types/dashboard";
|
||||
import type { DurationTime } from "@/types/app";
|
||||
import type { Option, DurationTime, Duration } from "@/types/app";
|
||||
|
||||
/*global defineProps, Recordable */
|
||||
const props = defineProps({
|
||||
@ -147,8 +158,9 @@ limitations under the License. -->
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const logStore = useLogStore();
|
||||
const { setDurationRow, getDurationTime, getMaxRange } = useDuration();
|
||||
const traceId = ref<string>((props.data.filters && props.data.filters.traceId) || "");
|
||||
const duration = ref<DurationTime>((props.data.filters && props.data.filters.duration) || appStore.durationTime);
|
||||
const duration = ref<DurationTime>((props.data.filters && props.data.filters.duration) || getDurationTime());
|
||||
const keywordsOfContent = ref<string[]>([]);
|
||||
const excludingKeywordsOfContent = ref<string[]>([]);
|
||||
const tagsList = ref<string[]>([]);
|
||||
@ -156,12 +168,24 @@ limitations under the License. -->
|
||||
const contentStr = ref<string>("");
|
||||
const excludingContentStr = ref<string>("");
|
||||
const isBrowser = ref<boolean>(dashboardStore.layerId === "BROWSER");
|
||||
const durationRow = ref<Duration>(InitializationDurationRow);
|
||||
const state = reactive<Recordable>({
|
||||
instance: { value: "0", label: "All" },
|
||||
endpoint: { value: "0", label: "All" },
|
||||
service: { value: "", label: "" },
|
||||
category: { value: "ALL", label: "All" },
|
||||
});
|
||||
const maxRange = computed(() =>
|
||||
getMaxRange(
|
||||
appStore.coldStageMode
|
||||
? isBrowser.value
|
||||
? appStore.recordsTTL.coldBrowserErrorLog
|
||||
: appStore.recordsTTL.coldLog
|
||||
: isBrowser.value
|
||||
? appStore.recordsTTL.browserErrorLog
|
||||
: appStore.recordsTTL.log,
|
||||
),
|
||||
);
|
||||
if (props.needQuery) {
|
||||
init();
|
||||
}
|
||||
@ -275,6 +299,11 @@ limitations under the License. -->
|
||||
ElMessage.error(res.errors);
|
||||
}
|
||||
}
|
||||
function changeDuration(val: Date[]) {
|
||||
durationRow.value = timeFormat(val);
|
||||
setDurationRow(durationRow.value);
|
||||
duration.value = getDurationTime();
|
||||
}
|
||||
function changeField(type: string, opt: any) {
|
||||
state[type] = opt[0];
|
||||
if (type === "service") {
|
||||
@ -352,12 +381,12 @@ limitations under the License. -->
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => appStore.durationTime,
|
||||
() => appStore.coldStageMode,
|
||||
() => {
|
||||
duration.value = appStore.durationTime;
|
||||
if (dashboardStore.entity === EntityType[1].value) {
|
||||
durationRow.value = InitializationDurationRow;
|
||||
setDurationRow(durationRow.value);
|
||||
duration.value = getDurationTime();
|
||||
init();
|
||||
}
|
||||
},
|
||||
);
|
||||
watch(
|
||||
@ -368,7 +397,7 @@ limitations under the License. -->
|
||||
return;
|
||||
}
|
||||
traceId.value = props.data.filters.traceId || "";
|
||||
duration.value = props.data.filters.duration || appStore.durationTime;
|
||||
duration.value = props.data.filters.duration || getDurationTime();
|
||||
init();
|
||||
}
|
||||
},
|
||||
|
@ -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,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -71,24 +71,36 @@ limitations under the License. -->
|
||||
<span class="grey mr-5">-</span>
|
||||
<el-input size="small" class="inputs" v-model="maxTraceDuration" type="number" />
|
||||
</div>
|
||||
<div>
|
||||
<span class="sm b grey mr-5">{{ t("timeRange") }}:</span>
|
||||
<TimePicker
|
||||
:value="[durationRow.start, durationRow.end]"
|
||||
:maxRange="maxRange"
|
||||
position="bottom"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@input="changeDuration"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-h">
|
||||
<ConditionTags :type="'TRACE'" @update="updateTags" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch, onUnmounted } from "vue";
|
||||
import { ref, reactive, watch, onUnmounted, computed } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { Option, DurationTime } from "@/types/app";
|
||||
import type { Option, DurationTime, Duration } from "@/types/app";
|
||||
import { useTraceStore } from "@/store/modules/trace";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useAppStoreWithOut, InitializationDurationRow } from "@/store/modules/app";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import ConditionTags from "@/views/components/ConditionTags.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { EntityType, QueryOrders, Status } from "../../data";
|
||||
import type { LayoutConfig } from "@/types/dashboard";
|
||||
import { useDuration } from "@/hooks/useDuration";
|
||||
|
||||
/*global defineProps, defineEmits, Recordable */
|
||||
const emits = defineEmits(["get", "search"]);
|
||||
@ -99,14 +111,15 @@ limitations under the License. -->
|
||||
default: () => ({ graph: {} }),
|
||||
},
|
||||
});
|
||||
const filters = reactive<Recordable>(props.data.filters || {});
|
||||
const traceId = ref<string>(filters.traceId || "");
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const traceStore = useTraceStore();
|
||||
const duration = ref<DurationTime>(filters.duration || appStore.durationTime);
|
||||
const { setDurationRow, getDurationTime, getMaxRange } = useDuration();
|
||||
const filters = reactive<Recordable>(props.data.filters || {});
|
||||
const traceId = ref<string>(filters.traceId || "");
|
||||
const duration = ref<DurationTime>(filters.duration || getDurationTime());
|
||||
const minTraceDuration = ref<number>();
|
||||
const maxTraceDuration = ref<number>();
|
||||
const tagsList = ref<string[]>([]);
|
||||
@ -117,6 +130,10 @@ limitations under the License. -->
|
||||
endpoint: { value: "0", label: "All" },
|
||||
service: { value: "", label: "" },
|
||||
});
|
||||
const durationRow = ref<Duration>(InitializationDurationRow);
|
||||
const maxRange = computed(() =>
|
||||
getMaxRange(appStore.coldStageMode ? appStore.recordsTTL.coldTrace : appStore.recordsTTL.trace),
|
||||
);
|
||||
if (filters.queryOrder) {
|
||||
traceStore.setTraceCondition({
|
||||
queryOrder: filters.queryOrder,
|
||||
@ -255,6 +272,11 @@ limitations under the License. -->
|
||||
ElMessage.error(resp.errors);
|
||||
}
|
||||
}
|
||||
function changeDuration(val: Date[]) {
|
||||
durationRow.value = timeFormat(val);
|
||||
setDurationRow(durationRow.value);
|
||||
duration.value = getDurationTime();
|
||||
}
|
||||
onUnmounted(() => {
|
||||
traceStore.resetState();
|
||||
const config = props.data;
|
||||
@ -280,12 +302,12 @@ limitations under the License. -->
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => appStore.durationTime,
|
||||
() => appStore.coldStageMode,
|
||||
() => {
|
||||
duration.value = appStore.durationTime;
|
||||
if (dashboardStore.entity === EntityType[1].value) {
|
||||
durationRow.value = InitializationDurationRow;
|
||||
setDurationRow(durationRow.value);
|
||||
duration.value = getDurationTime();
|
||||
init();
|
||||
}
|
||||
},
|
||||
);
|
||||
// Event widget associate with trace widget
|
||||
@ -299,7 +321,7 @@ limitations under the License. -->
|
||||
return;
|
||||
}
|
||||
traceId.value = props.data.filters.traceId || "";
|
||||
duration.value = props.data.filters.duration || appStore.durationTime;
|
||||
duration.value = props.data.filters.duration || getDurationTime();
|
||||
init();
|
||||
},
|
||||
);
|
||||
|
@ -31,6 +31,12 @@ limitations under the License. -->
|
||||
@change="changeLatency"
|
||||
class="ml-10"
|
||||
/>
|
||||
<TimePicker
|
||||
:value="[appStore.durationRow.start, appStore.durationRow.end]"
|
||||
position="bottom"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@input="changeTimeRange"
|
||||
/>
|
||||
<el-popover trigger="hover" width="250" placement="bottom">
|
||||
<template #reference>
|
||||
<div class="cp conditions-popup">
|
||||
@ -82,6 +88,7 @@ limitations under the License. -->
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import ConditionTags from "@/views/components/ConditionTags.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { EntityType, QueryOrders, Status } from "../../data";
|
||||
@ -107,6 +114,7 @@ limitations under the License. -->
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const traceStore = useTraceStore();
|
||||
const timeRange = ref<number>(NaN);
|
||||
const tagsList = ref<string[]>([]);
|
||||
const tagsMap = ref<Option[]>([]);
|
||||
const traceId = ref<string>(filters.refId || "");
|
||||
@ -169,6 +177,15 @@ limitations under the License. -->
|
||||
}
|
||||
await queryTraces();
|
||||
}
|
||||
|
||||
function changeTimeRange(val: Date[]) {
|
||||
timeRange.value = val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000 ? 1 : 0;
|
||||
if (timeRange.value) {
|
||||
return;
|
||||
}
|
||||
appStore.setDuration(timeFormat(val));
|
||||
}
|
||||
|
||||
function changeCondition() {
|
||||
if (conditions.value === "latency") {
|
||||
currentLatency.value = filters.latency ? filters.latency[0].data : [];
|
||||
|
@ -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({
|
||||
|
@ -14,18 +14,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export interface HexagonCreateParams {
|
||||
hexagonParam: number[];
|
||||
count: number;
|
||||
radius: number;
|
||||
origin?: number[];
|
||||
getShader?: any;
|
||||
}
|
||||
|
||||
export interface HexagonGeo {
|
||||
vertices: number[];
|
||||
normals: number[];
|
||||
insCenters: number[];
|
||||
indices: number[];
|
||||
origins: number[];
|
||||
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;
|
||||
|
47
src/views/settings/Index.vue
Normal file
47
src/views/settings/Index.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<!-- 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>
|
||||
<el-tabs v-model="activeName" class="settings-tabs">
|
||||
<el-tab-pane v-for="tab in SettingsTabs" :label="tab.label" :name="tab.value" :key="tab.value">
|
||||
<component :is="TabPanes[tab.value]" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { SettingsTabs } from "./data";
|
||||
import ClusterNodes from "./components/ClusterNodes.vue";
|
||||
import General from "./components/General.vue";
|
||||
import TTL from "./components/TTL.vue";
|
||||
import DebuggingConfigDump from "./components/DebuggingConfigDump.vue";
|
||||
/*global Indexable*/
|
||||
const TabPanes: Indexable = {
|
||||
general: General,
|
||||
ttl: TTL,
|
||||
clusterNodes: ClusterNodes,
|
||||
dumpEffectiveConfigurations: DebuggingConfigDump,
|
||||
};
|
||||
const activeName = ref(SettingsTabs[0].value);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.settings-tabs {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.settings-tabs > .el-tabs__content {
|
||||
color: var(--sw-setting-color);
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
71
src/views/settings/components/ClusterNodes.vue
Normal file
71
src/views/settings/components/ClusterNodes.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<!-- 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="cluster-nodes">
|
||||
<el-table
|
||||
:data="settingsStore.clusterNodes"
|
||||
class="mb-5"
|
||||
:row-style="{ backgroundColor: 'var(--layout-background)' }"
|
||||
>
|
||||
<el-table-column
|
||||
v-for="item in ClusterNodeRow"
|
||||
:prop="item.value"
|
||||
:label="item.label"
|
||||
:key="item.value"
|
||||
:width="item.width"
|
||||
/>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
class="pagination"
|
||||
layout="prev, pager, next"
|
||||
:page-size="pageSize"
|
||||
:total="settingsStore.clusterNodes.length"
|
||||
v-model="currentPage"
|
||||
@current-change="changePage"
|
||||
@prev-click="changePage"
|
||||
@next-click="changePage"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useSettingsStore } from "@/store/modules/settings";
|
||||
import { ClusterNodeRow } from "../data";
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const pageSize = 16;
|
||||
const currentPage = ref<number>(1);
|
||||
|
||||
onMounted(() => {
|
||||
settingsStore.getClusterNodes();
|
||||
});
|
||||
|
||||
const changePage = (pageIndex: number) => {
|
||||
currentPage.value = pageIndex;
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.cluster-nodes {
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 200px;
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
color: $font-color;
|
||||
}
|
||||
</style>
|
70
src/views/settings/components/DebuggingConfigDump.vue
Normal file
70
src/views/settings/components/DebuggingConfigDump.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<!-- 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="debugging-config-dump">
|
||||
<div class="config-dump-content">
|
||||
<div
|
||||
class="mb-10 flex-h"
|
||||
v-for="(item, index) of Object.keys(settingsStore.debuggingConfig)"
|
||||
:key="`${item}${index}`"
|
||||
>
|
||||
<div class="config-key">{{ item }}: </div>
|
||||
<div>{{ settingsStore.debuggingConfig[item] }}</div>
|
||||
</div>
|
||||
<div v-if="!Object.keys(settingsStore.debuggingConfig).length" class="tips">No Data</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from "vue";
|
||||
import { useSettingsStore } from "@/store/modules/settings";
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
onMounted(() => {
|
||||
settingsStore.getDebuggingConfigDump();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.config-dump-content {
|
||||
border: 1px solid var(--el-color-info-light-8);
|
||||
overflow: auto;
|
||||
padding: 5px;
|
||||
border-radius: 5px 3px;
|
||||
height: 700px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.config-key {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.debugging-config-dump {
|
||||
color: var(--sw-setting-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 200px;
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
color: $font-color;
|
||||
}
|
||||
</style>
|
181
src/views/settings/components/General.vue
Normal file
181
src/views/settings/components/General.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<!-- 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="general-settings">
|
||||
<div class="flex-h item">
|
||||
<div class="label">{{ t("language") }}</div>
|
||||
<Selector
|
||||
v-model="lang"
|
||||
:options="Languages"
|
||||
placeholder="Select a language"
|
||||
@change="setLang"
|
||||
size="small"
|
||||
style="font-size: 14px; width: 180px"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-h item">
|
||||
<div class="label">{{ t("serverZone") }}</div>
|
||||
<div>
|
||||
<span>UTC</span>
|
||||
<span class="ml-5 mr-5">{{ utcHour >= 0 ? "+" : "" }}</span>
|
||||
<input type="number" v-model="utcHour" min="-12" max="14" class="utc-input" @change="setUTCHour" />
|
||||
<span class="ml-5 mr-5">:</span>
|
||||
<span class="utc-min">{{ utcMin > 9 || utcMin === 0 ? null : 0 }}</span>
|
||||
<input type="number" v-model="utcMin" min="0" max="59" class="utc-input" @change="setUTCMin" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-h item">
|
||||
<div class="label">{{ t("auto") }}</div>
|
||||
<el-switch v-model="auto" @change="handleAuto" style="height: 25px" />
|
||||
<div class="auto-time ml-5">
|
||||
<span class="auto-select">
|
||||
<input type="number" v-model="autoTime" @change="changeAutoTime" min="1" />
|
||||
</span>
|
||||
{{ t("second") }}
|
||||
<i class="ml-10">{{ t("timeReload") }}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import { Languages } from "@/constants/data";
|
||||
import Selector from "@/components/Selector.vue";
|
||||
import getLocalTime from "@/utils/localtime";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const lang = ref<string>(locale.value || "en");
|
||||
const autoTime = ref<number>(6);
|
||||
const auto = ref<boolean>(appStore.autoRefresh || false);
|
||||
const utcHour = ref<number>(appStore.utcHour);
|
||||
const utcMin = ref<number>(appStore.utcMin);
|
||||
|
||||
const handleReload = () => {
|
||||
const gap = appStore.duration.end.getTime() - appStore.duration.start.getTime();
|
||||
const dates: Date[] = [
|
||||
getLocalTime(appStore.utc, new Date(new Date().getTime() - gap)),
|
||||
getLocalTime(appStore.utc, new Date()),
|
||||
];
|
||||
appStore.setDuration(timeFormat(dates));
|
||||
};
|
||||
const handleAuto = () => {
|
||||
if (autoTime.value < 1) {
|
||||
return;
|
||||
}
|
||||
appStore.setAutoRefresh(auto.value);
|
||||
if (auto.value) {
|
||||
handleReload();
|
||||
appStore.setReloadTimer(setInterval(handleReload, autoTime.value * 1000));
|
||||
} else {
|
||||
if (appStore.reloadTimer) {
|
||||
clearInterval(appStore.reloadTimer);
|
||||
}
|
||||
}
|
||||
};
|
||||
const changeAutoTime = () => {
|
||||
if (autoTime.value < 1) {
|
||||
return;
|
||||
}
|
||||
if (appStore.reloadTimer) {
|
||||
clearInterval(appStore.reloadTimer);
|
||||
}
|
||||
if (auto.value) {
|
||||
handleReload();
|
||||
appStore.setReloadTimer(setInterval(handleReload, autoTime.value * 1000));
|
||||
}
|
||||
};
|
||||
const setLang = (): void => {
|
||||
locale.value = lang.value;
|
||||
window.localStorage.setItem("language", lang.value);
|
||||
};
|
||||
const setUTCHour = () => {
|
||||
if (utcHour.value < -12) {
|
||||
utcHour.value = -12;
|
||||
}
|
||||
if (utcHour.value > 14) {
|
||||
utcHour.value = 14;
|
||||
}
|
||||
if (isNaN(utcHour.value)) {
|
||||
utcHour.value = 0;
|
||||
}
|
||||
appStore.setUTC(utcHour.value, utcMin.value);
|
||||
};
|
||||
const setUTCMin = () => {
|
||||
if (utcMin.value < 0) {
|
||||
utcMin.value = 0;
|
||||
}
|
||||
if (utcMin.value > 59) {
|
||||
utcMin.value = 59;
|
||||
}
|
||||
if (isNaN(utcMin.value)) {
|
||||
utcMin.value = 0;
|
||||
}
|
||||
appStore.setUTC(utcHour.value, utcMin.value);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.utc-input {
|
||||
color: inherit;
|
||||
background: 0;
|
||||
border: 0;
|
||||
outline: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.utc-min {
|
||||
display: inline-block;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.auto-select {
|
||||
border-radius: 3px;
|
||||
background-color: $theme-background;
|
||||
padding: 1px;
|
||||
|
||||
input {
|
||||
width: 38px;
|
||||
border-style: unset;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.general-settings {
|
||||
font-size: 13px;
|
||||
|
||||
.item {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
outline: 0;
|
||||
width: 50px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid $disabled-color;
|
||||
text-align: center;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 200px;
|
||||
display: inline-block;
|
||||
color: $font-color;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
</style>
|
49
src/views/settings/components/TTL.vue
Normal file
49
src/views/settings/components/TTL.vue
Normal file
@ -0,0 +1,49 @@
|
||||
<!-- 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="ttl">
|
||||
<div class="label">{{ t("metricsTTL") }}</div>
|
||||
<TTLTable :data="settingsStore.configTTL.metrics" :metricsRows="MetricsTTLRow" />
|
||||
<div class="label">{{ t("recordsTTL") }}</div>
|
||||
<TTLTable :data="settingsStore.configTTL.records" :metricsRows="RecordsTTLRow" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useSettingsStore } from "@/store/modules/settings";
|
||||
import TTLTable from "./TTLTable.vue";
|
||||
import { MetricsTTLRow, RecordsTTLRow } from "../data";
|
||||
|
||||
const { t } = useI18n();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
onMounted(() => {
|
||||
settingsStore.getConfigTTL();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ttl {
|
||||
color: var(--sw-setting-color);
|
||||
font-size: 13px;
|
||||
|
||||
.label {
|
||||
margin: 15px 0;
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
color: $font-color;
|
||||
}
|
||||
}
|
||||
</style>
|
44
src/views/settings/components/TTLTable.vue
Normal file
44
src/views/settings/components/TTLTable.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<!-- 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>
|
||||
<el-table
|
||||
:data="data"
|
||||
class="mb-5"
|
||||
:row-style="{ backgroundColor: 'var(--layout-background)' }"
|
||||
:cell-style="(data: Indexable) => (data.columnIndex === 0 ? { backgroundColor: 'var(--el-table-header-bg-color)' } : {})"
|
||||
>
|
||||
<el-table-column
|
||||
v-for="item in metricsRows"
|
||||
:prop="item.value"
|
||||
:label="item.label"
|
||||
:key="item.value"
|
||||
:width="item.width"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row && scope.row[item.value] ? (scope.row[item.value] < 0 ? "N/A" : scope.row[item.value]) : "N/A" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
/*global PropType, Indexable */
|
||||
defineProps({
|
||||
metricsRows: {
|
||||
type: Array as PropType<{ width?: number; value: string; label: string }[]>,
|
||||
default: () => [],
|
||||
},
|
||||
data: { type: Array as PropType<Indexable[]>, default: () => [] },
|
||||
});
|
||||
</script>
|
101
src/views/settings/data.ts
Normal file
101
src/views/settings/data.ts
Normal file
@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export const ClusterNodeRow = [
|
||||
{
|
||||
label: "Host",
|
||||
value: "host",
|
||||
},
|
||||
{
|
||||
label: "Port",
|
||||
value: "port",
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
label: "Self",
|
||||
value: "self",
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
|
||||
export const SettingsTabs = [
|
||||
{
|
||||
label: "General",
|
||||
value: "general",
|
||||
},
|
||||
{
|
||||
label: "TTL",
|
||||
value: "ttl",
|
||||
},
|
||||
{
|
||||
label: "Cluster Nodes",
|
||||
value: "clusterNodes",
|
||||
},
|
||||
{
|
||||
label: "Dump Effective Configurations",
|
||||
value: "dumpEffectiveConfigurations",
|
||||
},
|
||||
];
|
||||
|
||||
export const HotAndWarmOpt = ["day", "hour", "minute", "normal", "trace", "zipkinTrace", "log", "browserErrorLog"];
|
||||
|
||||
export const MetricsTTLRow = [
|
||||
{
|
||||
label: "Type",
|
||||
value: "type",
|
||||
width: 260,
|
||||
},
|
||||
{
|
||||
label: "Day",
|
||||
value: HotAndWarmOpt[0],
|
||||
},
|
||||
{
|
||||
label: "Hour",
|
||||
value: HotAndWarmOpt[1],
|
||||
},
|
||||
{
|
||||
label: "Minute",
|
||||
value: HotAndWarmOpt[2],
|
||||
},
|
||||
];
|
||||
|
||||
export const RecordsTTLRow = [
|
||||
{
|
||||
label: "Type",
|
||||
value: "type",
|
||||
width: 260,
|
||||
},
|
||||
{
|
||||
label: "Normal",
|
||||
value: HotAndWarmOpt[3],
|
||||
},
|
||||
{
|
||||
label: "Trace",
|
||||
value: HotAndWarmOpt[4],
|
||||
},
|
||||
{
|
||||
label: "Zipkin Trace",
|
||||
value: HotAndWarmOpt[5],
|
||||
},
|
||||
{
|
||||
label: "Log",
|
||||
value: HotAndWarmOpt[6],
|
||||
},
|
||||
{
|
||||
label: "Browser Error Log",
|
||||
value: HotAndWarmOpt[7],
|
||||
},
|
||||
];
|
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;
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,11 @@ export default ({ mode }: ConfigEnv): UserConfig => {
|
||||
target: `${VITE_SW_PROXY_TARGET || "http://127.0.0.1:12800"}`,
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/api": {
|
||||
target: `${VITE_SW_PROXY_TARGET || "http://127.0.0.1:12800"}`,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ""),
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
|
Loading…
Reference in New Issue
Block a user