Compare commits

..

No commits in common. "main" and "v10.1.0" have entirely different histories.

142 changed files with 5813 additions and 6737 deletions

View File

@ -37,7 +37,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
node-version: [16.x, 18.x, 20.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}

7542
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "skywalking-booster-ui",
"version": "10.2.0",
"version": "9.4.0",
"private": true,
"scripts": {
"dev": "vite",
@ -18,17 +18,19 @@
"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.7.5",
"d3": "^7.3.0",
"d3-flame-graph": "^4.1.3",
"d3-tip": "^0.9.1",
"echarts": "^5.2.2",
"element-plus": "^2.9.4",
"element-plus": "^2.2.5",
"lodash": "^4.17.21",
"monaco-editor": "^0.34.1",
"pinia": "^2.0.28",
"vis-timeline": "^7.5.1",
"vue": "^3.2.45",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.14.3",
"vue-i18n": "^9.1.9",
"vue-router": "^4.1.6",
"vue-types": "^4.1.1"
},
@ -40,10 +42,11 @@
"@types/d3-tip": "^3.5.5",
"@types/echarts": "^4.9.12",
"@types/jsdom": "^20.0.1",
"@types/lodash": "^4.14.179",
"@types/node": "^18.11.12",
"@types/three": "^0.131.0",
"@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"@vitejs/plugin-vue": "^4.0.0",
"@vitejs/plugin-vue-jsx": "^3.0.0",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.0",
"@vue/test-utils": "^2.2.6",
@ -61,21 +64,21 @@
"postcss-html": "^1.3.0",
"postcss-scss": "^4.0.2",
"prettier": "^2.7.1",
"sass": "^1.85.0",
"sass": "^1.56.1",
"start-server-and-test": "^2.0.5",
"stylelint": "15.9.0",
"stylelint-config-html": "^1.0.0",
"stylelint-config-prettier": "9.0.4",
"stylelint-config-standard": "^33.0.0",
"stylelint-order": "^6.0.3",
"typescript": "^5.7.3",
"typescript": "~4.7.4",
"unplugin-auto-import": "^0.18.2",
"unplugin-vue-components": "^0.27.3",
"vite": "^6.3.4",
"vite": "^4.5.3",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-svg-icons": "^2.0.1",
"vitest": "^3.0.5",
"vue-tsc": "^2.2.2"
"vitest": "^0.25.6",
"vue-tsc": "^1.8.27"
},
"browserslist": [
"> 1%",

View File

@ -1,18 +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. -->
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M512 992c-83.2 0-166.4-19.2-243.2-64-89.6-51.2-160-134.4-204.8-230.4C25.6 601.6 19.2 486.4 51.2 390.4c25.6-102.4 89.6-192 172.8-256C307.2 70.4 409.6 38.4 518.4 38.4c19.2 0 32 12.8 32 32s-19.2 25.6-38.4 25.6c-89.6 0-179.2 32-256 83.2S128 313.6 108.8 403.2s-12.8 185.6 19.2 268.8c32 83.2 96 153.6 179.2 198.4 76.8 44.8 172.8 64 262.4 51.2 89.6-12.8 172.8-51.2 236.8-115.2s108.8-147.2 115.2-236.8c12.8-89.6-6.4-185.6-51.2-262.4-6.4-12.8-6.4-32 12.8-44.8 12.8-12.8 38.4-6.4 44.8 12.8 51.2 89.6 76.8 198.4 57.6 300.8-12.8 102.4-64 204.8-134.4 275.2-76.8 76.8-172.8 121.6-275.2 134.4-19.2 6.4-44.8 6.4-64 6.4z" p-id="8538"></path><path d="M512 480c-19.2 0-32-12.8-32-32V64c0-19.2 12.8-32 32-32s32 12.8 32 32v384c0 19.2-12.8 32-32 32z" p-id="8539"></path><path d="M512 608c-12.8 0-25.6 0-38.4-6.4-12.8-6.4-19.2-12.8-32-19.2-6.4-12.8-12.8-19.2-19.2-32-6.4-12.8-6.4-25.6-6.4-38.4 0-25.6 12.8-51.2 25.6-70.4 38.4-38.4 102.4-38.4 134.4 0 19.2 19.2 32 44.8 32 70.4 0 25.6-12.8 51.2-25.6 70.4-19.2 12.8-44.8 25.6-70.4 25.6z m0-128c-6.4 0-19.2 6.4-25.6 6.4 0 6.4-6.4 19.2-6.4 25.6v12.8c0 6.4 6.4 6.4 6.4 12.8 0 0 6.4 6.4 12.8 6.4 12.8 6.4 25.6 0 32-6.4 6.4-6.4 12.8-19.2 12.8-25.6 0-6.4-6.4-19.2-6.4-25.6-6.4 0-19.2-6.4-25.6-6.4z" p-id="8540"></path><path d="M512 800c-51.2 0-102.4-12.8-147.2-38.4-57.6-32-96-83.2-121.6-140.8-19.2-57.6-25.6-121.6-6.4-185.6 19.2-64 51.2-115.2 102.4-153.6C384 243.2 448 224 512 224c19.2 0 32 12.8 32 32s-12.8 32-32 32c-51.2 0-96 19.2-134.4 44.8-38.4 32-70.4 76.8-83.2 121.6s-6.4 96 12.8 140.8c19.2 44.8 51.2 83.2 96 108.8 44.8 25.6 89.6 32 140.8 25.6 51.2-6.4 96-32 128-64s57.6-83.2 64-128c6.4-51.2-6.4-96-25.6-140.8-12.8-12.8-6.4-32 6.4-38.4 12.8-6.4 32-6.4 44.8 12.8 32 57.6 44.8 121.6 38.4 179.2-6.4 64-38.4 121.6-83.2 166.4-44.8 44.8-102.4 76.8-166.4 83.2H512z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,38 +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. -->
<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>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -13,13 +13,6 @@ 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>
<SelectorLegend
:data="option.legend.data"
:show="legendSelector.isSelector"
:isConfigPage="legendSelector.isConfigPage"
:colors="option.color"
@change="changeLegend"
/>
<div class="chart" ref="chartRef" :style="`height:${height};width:${width};`">
<div v-if="!available" class="no-data">No Data</div>
<div
@ -61,7 +54,6 @@ limitations under the License. -->
import Trace from "@/views/dashboard/related/trace/Index.vue";
import associateProcessor from "@/hooks/useAssociateProcessor";
import { WidgetType } from "@/views/dashboard/data";
import SelectorLegend from "./Legend.vue";
/*global Nullable, defineProps, defineEmits, Indexable*/
const emits = defineEmits(["select"]);
@ -92,10 +84,6 @@ limitations under the License. -->
type: Array as PropType<{ widgetId: string }[]>,
default: () => [],
},
legendSelector: {
type: Object as PropType<Indexable>,
default: () => ({ isConfigPage: false, isSelector: false }),
},
});
const available = computed(
() =>
@ -115,7 +103,6 @@ limitations under the License. -->
if (!instance) {
return;
}
instance.on("click", (params: EventParams) => {
currentParams.value = params;
if (props.option.series.type === "sankey") {
@ -216,23 +203,6 @@ limitations under the License. -->
});
}
function changeLegend(names: string[]) {
const instance = getInstance();
for (const item of props.option.legend.data) {
if (names.includes(item.name)) {
instance.dispatchAction({
type: "legendSelect",
name: item.name,
});
} else {
instance.dispatchAction({
type: "legendUnSelect",
name: item.name,
});
}
}
}
watch(
() => props.option,
(newVal, oldVal) => {

View File

@ -1,74 +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>
<Selector
class="mb-10"
multiple
:value="legend"
size="small"
:options="Options"
@change="changeLegend"
filterable
collapseTags
collapseTagsTooltip
v-if="show"
/>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from "vue";
import type { PropType } from "vue";
import type { Option } from "@/types/app";
import Selector from "./Selector.vue";
const props = defineProps({
data: {
type: Array as PropType<{ name: string }[]>,
default: () => [],
},
colors: {
type: Array as PropType<string[]>,
default: () => [],
},
show: {
type: Boolean,
default: false,
},
isConfigPage: {
type: Boolean,
default: false,
},
});
const emits = defineEmits(["change"]);
const legend = ref<string[]>([]);
const Options = computed(() =>
props.data.map((d: { name: string }, index: number) => ({
label: d.name,
value: d.name,
color: props.colors[index % props.colors.length],
})),
);
function changeLegend(opt: Option[]) {
legend.value = opt.map((d: Option) => d.value);
emits("change", legend.value);
}
watch(
() => props.data,
() => {
legend.value = props.data.map((d) => d.name);
},
);
</script>

View File

@ -1,107 +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>
<el-select
:size="size"
v-model="selected"
:placeholder="placeholder"
@change="changeSelected"
:multiple="multiple"
:disabled="disabled"
:style="{ borderRadius }"
:clearable="clearable"
:remote="isRemote"
:reserve-keyword="isRemote"
:remote-method="remoteMethod"
:filterable="filterable"
:collapse-tags="collapseTags"
:collapse-tags-tooltip="collapseTagsTooltip"
>
<el-option
v-for="(item, index) in options"
:key="`${item.value}${index}`"
:label="item.label || ''"
:value="item.value || ''"
:disabled="item.disabled || false"
>
<div class="flex items-center">
<el-tag :color="item.color" class="mr-5" size="small" />
<span :style="{ color: item.color }">{{ item.label }}</span>
</div>
</el-option>
</el-select>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
import type { PropType } from "vue";
/*global defineProps, defineEmits, Indexable*/
const emit = defineEmits(["change", "query"]);
const props = defineProps({
options: {
type: Array as PropType<
({
label: string | number;
value: string | number;
color: string;
} & { disabled?: boolean })[]
>,
default: () => [],
},
value: {
type: [Array, String, Number, undefined] as PropType<any>,
default: () => [],
},
size: { type: null, default: "default" },
placeholder: {
type: [String, undefined] as PropType<string>,
default: "Select a option",
},
borderRadius: { type: Number, default: 3 },
multiple: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
clearable: { type: Boolean, default: false },
isRemote: { type: Boolean, default: false },
filterable: { type: Boolean, default: true },
collapseTags: { type: Boolean, default: false },
collapseTagsTooltip: { type: Boolean, default: false },
});
const selected = ref<string[] | string>(props.value);
function changeSelected() {
const options = props.options.filter((d: Indexable) =>
props.multiple ? selected.value.includes(d.value) : selected.value === d.value,
);
emit("change", options);
}
function remoteMethod(query: string) {
if (props.isRemote) {
emit("query", query);
}
}
watch(
() => props.value,
(data) => {
selected.value = data;
},
);
</script>
<style lang="scss" scoped>
.el-input__inner {
border-radius: unset !important;
}
</style>

View File

@ -26,8 +26,6 @@ limitations under the License. -->
:reserve-keyword="isRemote"
:remote-method="remoteMethod"
:filterable="filterable"
:collapse-tags="collapseTags"
:collapse-tags-tooltip="collapseTagsTooltip"
>
<el-option
v-for="(item, index) in options"
@ -43,6 +41,11 @@ limitations under the License. -->
import { ref, watch } from "vue";
import type { PropType } from "vue";
// interface Option {
// label: string | number;
// value: string | number;
// }
/*global defineProps, defineEmits, Indexable*/
const emit = defineEmits(["change", "query"]);
const props = defineProps({
@ -70,8 +73,6 @@ limitations under the License. -->
clearable: { type: Boolean, default: false },
isRemote: { type: Boolean, default: false },
filterable: { type: Boolean, default: true },
collapseTags: { type: Boolean, default: false },
collapseTagsTooltip: { type: Boolean, default: false },
});
const selected = ref<string[] | string>(props.value);

View File

@ -18,7 +18,7 @@ import type { App } from "vue";
import Icon from "./Icon.vue";
import TimePicker from "./TimePicker.vue";
import Selector from "./Selector.vue";
import Graph from "./Graph/Graph.vue";
import Graph from "./Graph.vue";
import Radio from "./Radio.vue";
import SelectSingle from "./SelectSingle.vue";
import Tags from "./Tags.vue";

View File

@ -1,75 +0,0 @@
/*
* Licensed to Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Apache Software Foundation (ASF) licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const Timeout = 2 * 60 * 1000;
export let globalAbortController = new AbortController();
export function abortRequestsAndUpdate() {
globalAbortController.abort(`Request timeout ${Timeout}ms`);
globalAbortController = new AbortController();
}
class HTTPError extends Error {
response;
constructor(response: Response, detailText = "") {
super(detailText || response.statusText);
this.name = "HTTPError";
this.response = response;
}
}
const BasePath = `/graphql`;
export async function httpQuery({
path = "",
method = "GET",
json,
headers = {},
}: {
path?: string;
method: string;
json: unknown;
headers: Recordable;
}) {
const timeoutId = setTimeout(() => {
abortRequestsAndUpdate();
}, Timeout);
const url = `${BasePath}${path}`;
const response: Response = await fetch(url, {
method,
headers: {
"Content-Type": "application/json",
accept: "application/json",
...headers,
},
body: JSON.stringify(json),
signal: globalAbortController.signal,
})
.catch((error) => {
throw new HTTPError(error);
})
.finally(() => {
clearTimeout(timeoutId);
});
if (response.ok) {
return response.json();
} else {
console.error(new HTTPError(response));
}
}

View File

@ -14,18 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { httpQuery } from "./base";
import type { AxiosResponse } from "axios";
import axios from "axios";
import { cancelToken } from "@/utils/cancelToken";
async function fetchQuery(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
const response = await httpQuery({
method: "post",
json: { query: param.queryStr, variables: { ...param.conditions } },
headers: {},
});
if (response.errors) {
response.errors = response.errors.map((e: { message: string }) => e.message).join(" ");
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(" ");
}
return response;
return res;
}
export default fetchQuery;
export default query;

View File

@ -24,7 +24,6 @@ export const Alarm = {
message
startTime
scope
name
tags {
key
value
@ -44,35 +43,6 @@ export const Alarm = {
startTime
endTime
}
snapshot {
expression
metrics {
name
results {
metric {
labels {
key
value
}
}
values {
id
owner {
scope
serviceID
serviceName
normal
serviceInstanceID
serviceInstanceName
endpointID
endpointName
}
value
traceID
}
}
}
}
}
}`,
};

View File

@ -1,80 +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.
*/
export const GetAsyncTaskList = {
variable: "$request: AsyncProfilerTaskListRequest!",
query: `
asyncTaskList: queryAsyncProfilerTaskList(request: $request) {
errorReason
tasks {
id
serviceId
serviceInstanceIds
createTime
events
duration
execArgs
}
}
`,
};
export const GetAsyncProfileTaskProcess = {
variable: "$taskId: String!",
query: `
taskProgress: queryAsyncProfilerTaskProgress(taskId: $taskId) {
logs {
id
instanceId
instanceName
operationType
operationTime
}
errorInstanceIds
successInstanceIds
}
`,
};
export const CreateAsyncProfileTask = {
variable: "$asyncProfilerTaskCreationRequest: AsyncProfilerTaskCreationRequest!",
query: `
task: createAsyncProfilerTask(asyncProfilerTaskCreationRequest: $asyncProfilerTaskCreationRequest) {
id
errorReason
code
}
`,
};
export const GetAsyncProfileAnalyze = {
variable: "$request: AsyncProfilerAnalyzationRequest!",
query: `
analysisResult: queryAsyncProfilerAnalyze(request: $request) {
tree {
type
elements {
id
parentId
symbol: codeSignature
dumpCount: total
self
}
}
}
`,
};

View File

@ -14,6 +14,22 @@
* 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: `

View File

@ -73,9 +73,9 @@ export const Processes = {
};
export const Endpoints = {
variable: "$serviceId: ID!, $keyword: String!, $duration: Duration, $limit: Int!",
variable: "$serviceId: ID!, $keyword: String!",
query: `
pods: findEndpoint(serviceId: $serviceId, keyword: $keyword, limit: $limit, duration: $duration) {
pods: findEndpoint(serviceId: $serviceId, keyword: $keyword, limit: 20) {
id
value: name
label: name

View File

@ -14,7 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { httpQuery } from "./base";
import type { AxiosPromise, AxiosResponse } from "axios";
import axios from "axios";
import { cancelToken } from "@/utils/cancelToken";
import * as app from "./query/app";
import * as selector from "./query/selector";
import * as dashboard from "./query/dashboard";
@ -26,7 +28,6 @@ import * as alarm from "./query/alarm";
import * as event from "./query/event";
import * as ebpf from "./query/ebpf";
import * as demandLog from "./query/demand-log";
import * as asyncProfile from "./query/async-profile";
const query: { [key: string]: string } = {
...app,
@ -40,27 +41,32 @@ const query: { [key: string]: string } = {
...event,
...ebpf,
...demandLog,
...asyncProfile,
};
class Graphql {
queryData = "";
query(data: string) {
this.queryData = data;
private queryData = "";
public query(queryData: string) {
this.queryData = queryData;
return this;
}
async params(variables: unknown) {
const response = await httpQuery({
method: "post",
headers: {},
json: {
query: query[this.queryData],
variables,
},
});
if (response.errors) {
response.errors = response.errors.map((e: { message: string }) => e.message).join(" ");
}
return response;
public params(variablesData: unknown): AxiosPromise<void> {
return axios
.post(
"/graphql",
{
query: query[this.queryData],
variables: variablesData,
},
{ 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;
});
}
}

View File

@ -1,31 +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.
*/
import {
GetAsyncTaskList,
GetAsyncProfileTaskProcess,
CreateAsyncProfileTask,
GetAsyncProfileAnalyze,
} from "../fragments/async-profile";
export const getAsyncTaskList = `query getAsyncTaskList(${GetAsyncTaskList.variable}) {${GetAsyncTaskList.query}}`;
export const getAsyncProfileTaskProcess = `query getAsyncProfileTaskProcess(${GetAsyncProfileTaskProcess.variable}) {${GetAsyncProfileTaskProcess.query}}`;
export const saveAsyncProfileTask = `mutation createAsyncProfileTask(${CreateAsyncProfileTask.variable}) {${CreateAsyncProfileTask.query}}`;
export const getAsyncProfileAnalyze = `query getAsyncProfileAnalyze(${GetAsyncProfileAnalyze.variable}) {${GetAsyncProfileAnalyze.query}}`;

View File

@ -14,7 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getAllTemplates, addTemplate, changeTemplate, deleteTemplate } from "../fragments/dashboard";
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}}`;
export const addNewTemplate = `mutation template(${addTemplate.variable}) {${addTemplate.query}}`;

View File

@ -57,16 +57,6 @@ export const RespFields: Indexable = {
name: id
value
refId: traceID
owner {
scope
serviceID
serviceName
normal
serviceInstanceID
serviceInstanceName
endpointID
endpointName
}
}
}
error
@ -112,5 +102,3 @@ export const LightChartColors = [
"#546570",
"#c4ccd3",
];
export const MaxQueryLength = 120;

View File

@ -47,11 +47,11 @@ export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void)
function getWindowWidth() {
const width = document.body.clientWidth;
const xs = screenMap.get(sizeEnum.XS) || 0;
const sm = screenMap.get(sizeEnum.SM) || 0;
const md = screenMap.get(sizeEnum.MD) || 0;
const lg = screenMap.get(sizeEnum.LG) || 0;
const xl = screenMap.get(sizeEnum.XL) || 0;
const xs = screenMap.get(sizeEnum.XS) || "";
const sm = screenMap.get(sizeEnum.SM) || "";
const md = screenMap.get(sizeEnum.MD) || "";
const lg = screenMap.get(sizeEnum.LG) || "";
const xl = screenMap.get(sizeEnum.XL) || "";
if (width < xs) {
screenRef.value = sizeEnum.XS;
} else if (width < sm) {

View File

@ -14,10 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { RespFields, MaximumEntities, MaxQueryLength } from "./data";
import { RespFields, MaximumEntities } from "./data";
import { EntityType, ExpressionResultType } from "@/views/dashboard/data";
import { ElMessage } from "element-plus";
import { useTopologyStore } from "@/store/modules/topology";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app";
@ -25,14 +24,6 @@ import type { MetricConfigOpt } from "@/types/dashboard";
import type { Instance, Endpoint, Service } from "@/types/selector";
import type { Node, Call } from "@/types/topology";
function chunkArray(array: any[], chunkSize: number) {
const result = [];
for (let i = 0; i < array.length; i += chunkSize) {
result.push(array.slice(i, i + chunkSize));
}
return result;
}
export async function useDashboardQueryProcessor(configList: Indexable[]) {
function expressionsGraphql(config: Indexable, idx: number) {
if (!(config.metrics && config.metrics[0])) {
@ -109,12 +100,12 @@ export async function useDashboardQueryProcessor(configList: Indexable[]) {
return { source: {}, tips: [], typesOfMQE: [] };
}
const tips: string[] = [];
const source: Indexable<unknown> = {};
const source: { [key: string]: unknown } = {};
const keys = Object.keys(resp.data);
const typesOfMQE: string[] = [];
for (let i = 0; i < config.metrics.length; i++) {
const metricConfig: MetricConfigOpt = (config.metricConfig && config.metricConfig[i]) || {};
const c: MetricConfigOpt = (config.metricConfig && config.metricConfig[i]) || {};
const obj = resp.data[keys[i]] || {};
const results = obj.results || [];
const name = config.metrics[i];
@ -125,15 +116,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) {
let label =
const 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) {
// If the metrics label does not exist, use the configuration label or expression
label = label ? `${metricConfig.label || name}, ${label}` : metricConfig.label || name;
source[label || c.label || name] = values;
} else {
source[label] = values;
}
source[label] = values;
}
}
if (([ExpressionResultType.RECORD_LIST, ExpressionResultType.SORTED_LIST] as string[]).includes(type)) {
@ -148,7 +139,7 @@ export async function useDashboardQueryProcessor(configList: Indexable[]) {
const appStore = useAppStoreWithOut();
const variables: string[] = [`$duration: Duration!`];
let fragments = "";
let conditions: Recordable<unknown> = {
let conditions: Recordable = {
duration: appStore.durationTime,
};
for (let i = 0; i < configArr.length; i++) {
@ -190,6 +181,13 @@ export async function useDashboardQueryProcessor(configList: Indexable[]) {
return { 0: { source: {}, tips: [], typesOfMQE: [] } };
}
}
function chunkArray(array: any[], chunkSize: number) {
const result = [];
for (let i = 0; i < array.length; i += chunkSize) {
result.push(array.slice(i, i + chunkSize));
}
return result;
}
const partArr = chunkArray(configList, 6);
const promiseArr = partArr.map((d: Array<Indexable>) => fetchMetrics(d));
@ -396,7 +394,7 @@ export function useQueryTopologyExpressionsProcessor(metrics: string[], instance
const appStore = useAppStoreWithOut();
const dashboardStore = useDashboardStore();
function getExpressionQuery(partMetrics?: string[]) {
function getExpressionQuery() {
const conditions: { [key: string]: unknown } = {
duration: appStore.durationTime,
};
@ -450,7 +448,7 @@ export function useQueryTopologyExpressionsProcessor(metrics: string[], instance
};
variables.push(`$entity${index}: Entity!`);
conditions[`entity${index}`] = entity;
const f = (partMetrics || metrics).map((name: string, idx: number) => {
const f = metrics.map((name: string, idx: number) => {
if (index === 0) {
variables.push(`$expression${idx}: String!`);
conditions[`expression${idx}`] = name;
@ -464,19 +462,19 @@ export function useQueryTopologyExpressionsProcessor(metrics: string[], instance
return { queryStr, conditions };
}
function handleExpressionValues(partMetrics: string[], resp: { [key: string]: any }) {
const obj: Indexable = {};
function handleExpressionValues(resp: { [key: string]: any }) {
const obj: any = {};
for (let idx = 0; idx < instances.length; idx++) {
for (let index = 0; index < partMetrics.length; index++) {
for (let index = 0; index < metrics.length; index++) {
const k = "expression" + idx + index;
if (partMetrics[index]) {
if (!obj[partMetrics[index]]) {
obj[partMetrics[index]] = {
if (metrics[index]) {
if (!obj[metrics[index]]) {
obj[metrics[index]] = {
values: [],
};
}
obj[partMetrics[index]].values.push({
value: resp[k] && resp[k].results[0] && resp[k].results[0].values[0].value,
obj[metrics[index]].values.push({
value: resp[k].results[0] && resp[k].results[0].values[0].value,
id: instances[idx].id,
});
}
@ -484,31 +482,6 @@ export function useQueryTopologyExpressionsProcessor(metrics: string[], instance
}
return obj;
}
async function fetchMetrics(partMetrics: string[]) {
const topologyStore = useTopologyStore();
const param = getExpressionQuery(partMetrics);
const res = await topologyStore.getTopologyExpressionValue(param);
if (res.errors) {
ElMessage.error(res.errors);
return;
}
return handleExpressionValues(partMetrics, res.data);
}
async function getMetrics() {
const count = Math.floor(MaxQueryLength / instances.length);
const metricsArr = chunkArray(metrics, count);
const promiseArr = metricsArr.map((d: string[]) => fetchMetrics(d));
const responseList = await Promise.all(promiseArr);
let resp = {};
for (const item of responseList) {
resp = {
...resp,
...item,
};
}
return resp;
}
return { getMetrics, getExpressionQuery };
return { getExpressionQuery, handleExpressionValues };
}

View File

@ -35,7 +35,7 @@ export default function useLegendProcess(legend?: LegendOptions) {
if (keys.length === 1) {
return false;
}
if (legend && (legend.asTable || legend.asSelector)) {
if (legend && legend.asTable) {
return false;
}
return true;

View File

@ -1,47 +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.
*/
import type { MetricsResults } from "@/types/dashboard";
export function useSnapshot(metrics: { name: string; results: MetricsResults[] }[]) {
function processResults() {
const sources = metrics.map((metric: { name: string; results: MetricsResults[] }) => {
const values = metric.results.map(
(r: { values: { value: string }[]; metric: { labels: { key: string; value: string }[] } }) => {
const arr = r.values.map((v: { value: string }) => Number(v.value));
if (!r.metric.labels.length) {
return { values: arr };
}
const name = r.metric.labels
.map(
(label: { key: string; value: string }) =>
`${metric.name}${label ? "{" : ""}${label.key}=${label.value}${label ? "}" : ""}`,
)
.join(",");
return { name, values: arr };
},
);
return { name: metric.name, values };
});
return sources;
}
return {
processResults,
};
}

View File

@ -61,7 +61,7 @@ limitations under the License. -->
<Icon iconName="retry" :loading="appStore.autoRefresh" class="middle" />
</span>
<span class="version ml-5 cp">
<el-popover trigger="hover" :width="250" placement="bottom" :content="appStore.version">
<el-popover trigger="hover" width="250" placement="bottom" :content="appStore.version">
<template #reference>
<span>
<Icon iconName="info_outline" size="middle" />

View File

@ -50,7 +50,7 @@ limitations under the License. -->
</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
<el-menu-item :index="String(menu.name)" v-else>
<el-menu-item :index="String(menu.name)" @click="changePage(menu)" v-else>
<el-icon class="menu-icons" :style="{ marginRight: '12px' }" @mouseover="setCollapse">
<router-link class="items menu-title" :to="menu.children[0].path">
<Icon size="lg" :iconName="menu.meta.icon" />
@ -83,6 +83,7 @@ limitations under the License. -->
const appStore = useAppStoreWithOut();
const router = useRouter();
const name = ref<string>(String(router.currentRoute.value.name));
const theme = ["VirtualMachine", "Kubernetes"].includes(name.value || "") ? ref("light") : ref("black");
const routes = ref<RouteRecordRaw[] | any>(
(router.options.routes || []).filter((d: any) => d.meta && d.meta.activate),
);
@ -99,7 +100,9 @@ limitations under the License. -->
if (route.name === "ViewWidget") {
showMenu.value = false;
}
const changePage = (menu: RouteRecordRaw) => {
theme.value = ["VirtualMachine", "Kubernetes"].includes(String(menu.name)) ? "light" : "black";
};
const filterMenus = (menus: Recordable[]) => {
return menus.filter((d) => d.meta && !d.meta.notShow && d.meta.activate);
};

View File

@ -139,6 +139,7 @@ const msg = {
enableAssociate: "Enable association",
text: "Text",
query: "Query",
endpointTips: "The table shows up to 20 pieces of endpoints.",
viewTrace: "View Related Traces",
relatedTraceOptions: "Related Trace Options",
setLatencyDuration: "Latency Related Metrics",
@ -153,7 +154,6 @@ const msg = {
legendOptions: "Legend Options",
showLegend: "Show Legend",
asTable: "As Table",
asSelector: "As Selector",
toTheRight: "To The Right",
legendValues: "Legend Values",
minDuration: "Minimal Request Duration",
@ -386,16 +386,5 @@ const msg = {
tabExpressions: "Tab Expressions",
hierarchyNodeMetrics: "Metrics for Hierarchy Graph Node",
hierarchyNodeDashboard: "As dashboard for Hierarchy Graph Node",
valueMappings: "Value Mappings",
mappingTip: "Notice: The mapping key is a Regex string, e.g. ^([0-9])$",
valueDashboard: "Data Value Related Dashboard",
viewValueDashboard: "View Dashboard",
errorInstances: "Error Instances",
successInstances: "Success Instances",
profilingEvents: "Async Profiling Events",
execArgs: "Exec Args",
instances: "Instances",
snapshot: "Snapshot",
expression: "Expression",
};
export default msg;

View File

@ -138,6 +138,7 @@ const msg = {
"El nombre sólo admite chino e inglés, líneas horizontales y subrayado, y la longitud del nombre no excederá de 300 caracteres",
enableAssociate: "Activar asociación",
query: "Consulta",
endpointTips: "Aquí, la tabla muestra hasta 20 punto final.",
queryOrder: "Consulta por duración",
setOrder: "Orden de consulta",
latency: "Retraso",
@ -385,17 +386,5 @@ const msg = {
tabExpressions: "Tab Expressions",
hierarchyNodeMetrics: "Metrics for Hierarchy Graph Node",
hierarchyNodeDashboard: "As dashboard for Hierarchy Graph Node",
valueMappings: "Value Mappings",
mappingTip: "Aviso: La clave de mapeo es una cadena Regex, p. ej. ^([0-9])$",
valueDashboard: "Data Value Related Dashboard",
viewValueDashboard: "View Dashboard",
errorInstances: "Error Instances",
successInstances: "Success Instances",
profilingEvents: "Async Profiling Events",
execArgs: "Exec Args",
instances: "Instances",
snapshot: "Snapshot",
expression: "Expression",
asSelector: "As Selector",
};
export default msg;

View File

@ -128,20 +128,11 @@ const titles = {
self_observability_java_agent: "SkyWalking Java Agent",
self_observability_java_agent_desc:
"The self observability of SkyWalking Java Agent, which provides the abilities to measure the tracing performance and error statistics of plugins.",
self_observability_go_agent: "SkyWalking Go Agent",
self_observability_go_agent_desc:
"The self observability of SkyWalking Go Agent, which provides the abilities to measure the tracing performance and error statistics of plugins.",
cilium: "Cilium",
cilium_desc:
"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;

View File

@ -128,21 +128,12 @@ const titles = {
"Satellite: an open-source agent designed for the cloud-native infrastructures, which provides a low-cost, high-efficient, and more secure way to collect telemetry data. It is the recommended load balancer for telemetry collecting.",
self_observability_java_agent: "SkyWalking Java Agent",
self_observability_java_agent_desc:
"La auto-observabilidad de SkyWalking Java Agent, que proporciona la capacidad de medir el rendimiento del trazado y las estadísticas de errores de los plugins.",
self_observability_go_agent: "SkyWalking Go Agent",
self_observability_go_agent_desc:
"La auto-observabilidad de SkyWalking Go Agent, que proporciona la capacidad de medir el rendimiento del trazado y las estadísticas de errores de los plugins.",
"The self observability of SkyWalking Java Agent, which provides the abilities to measure the tracing performance and error statistics of plugins.",
cilium: "Cilium",
cilium_desc:
"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;

View File

@ -113,17 +113,10 @@ const titles = {
"Satellite为云原生基础设施设计的开源代理提供了一种低成本、高效、更安全的遥测数据收集方式。它是遥测采集的推荐负载均衡器。",
self_observability_java_agent: "SkyWalking Java Agent",
self_observability_java_agent_desc: "SkyWalking Java Agent 自监控提供了对 agent 插件的性能追踪和错误统计。",
self_observability_go_agent: "SkyWalking Go Agent",
self_observability_go_agent_desc: "SkyWalking Go Agent 自监控提供了对 agent 插件的性能追踪和错误统计。",
cilium: "Cilium",
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;

View File

@ -137,6 +137,7 @@ const msg = {
duplicateName: "重复的名称",
text: "文本",
query: "查询",
endpointTips: "这里最多展示20条endpoints。",
viewTrace: "查看相关Trace",
relatedTraceOptions: "相关的Trace选项",
setLatencyDuration: "延迟相关指标",
@ -151,7 +152,6 @@ const msg = {
legendOptions: "图例选项",
showLegend: "显示图例",
asTable: "作为表格",
asSelector: "作为选择器",
toTheRight: "在右边",
legendValues: "图例值",
minDuration: "最小请求持续时间",
@ -384,16 +384,5 @@ const msg = {
tabExpressions: "Tab表达式",
hierarchyNodeMetrics: "层次图节点的指标",
hierarchyNodeDashboard: "作为层次图节点的dashboard",
valueMappings: "值映射",
mappingTip: "注意: 映射键是一个正则表达式字符串,比如 ^([0-9])$",
valueDashboard: "数据值相关的仪表板",
viewValueDashboard: "查看仪表板",
errorInstances: "错误的实例",
successInstances: "成功的实例",
profilingEvents: "异步分析事件",
execArgs: "String任务扩展",
instances: "实例",
snapshot: "快照",
expression: "表达式",
};
export default msg;

View File

@ -33,7 +33,7 @@ export const routesAlarm: Array<RouteRecordRaw> = [
children: [
{
path: "/alerting",
name: "ViewAlarm",
name: "Alarm",
component: Alarm,
},
],

View File

@ -100,7 +100,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
path: "",
redirect: "/dashboard/related/:layerId/:entity/:serviceId/:destServiceId/:name",
component: Edit,
name: "ServiceRelations",
name: "ViewServiceRelation",
meta: {
notShow: true,
},
@ -121,7 +121,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
path: "",
redirect: "/dashboard/:layerId/:entity/:serviceId/:podId/:name",
component: Edit,
name: "Pods",
name: "ViewPod",
meta: {
notShow: true,
},
@ -142,7 +142,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
path: "",
redirect: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:name",
component: Edit,
name: "Processes",
name: "ViewProcess",
meta: {
notShow: true,
},
@ -163,7 +163,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
path: "",
redirect: "/dashboard/:layerId/:entity/:serviceId/:podId/:destServiceId/:destPodId/:name",
component: Edit,
name: "PodRelations",
name: "PodRelation",
meta: {
notShow: true,
},
@ -185,7 +185,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
redirect:
"/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name",
component: Edit,
name: "ProcessRelations",
name: "ProcessRelation",
meta: {
notShow: true,
},

View File

@ -37,8 +37,16 @@ const router = createRouter({
routes,
});
router.beforeEach((to, _, next) => {
(window as any).axiosCancel = [];
router.beforeEach((to, from, 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 = "";

View File

@ -33,7 +33,7 @@ export const routesSettings: Array<RouteRecordRaw> = [
children: [
{
path: "/settings",
name: "ViewSettings",
name: "Settings",
component: Settings,
},
],

View File

@ -47,7 +47,6 @@ export const ControlsTypes = [
WidgetType.DemandLog,
WidgetType.Ebpf,
WidgetType.NetworkProfiling,
WidgetType.AsyncProfiling,
WidgetType.ThirdPartyApp,
WidgetType.ContinuousProfiling,
WidgetType.TaskTimeline,
@ -56,5 +55,3 @@ export enum EBPFProfilingTriggerType {
FIXED_TIME = "FIXED_TIME",
CONTINUOUS_PROFILING = "CONTINUOUS_PROFILING",
}
export const EndpointsTopNDefault = 20;

View File

@ -17,6 +17,7 @@
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";
@ -36,22 +37,30 @@ export const alarmStore = defineStore({
actions: {
async getAlarms(params: Recordable) {
this.loading = true;
const res = await graphql.query("queryAlarms").params(params);
const res: AxiosResponse = await graphql.query("queryAlarms").params(params);
this.loading = false;
if (res.errors) {
return res;
if (res.data.errors) {
return res.data;
}
if (res.data.getAlarm.items) {
this.alarms = res.data.getAlarm.items;
this.total = res.data.getAlarm.total;
if (res.data.data.getAlarm.items) {
this.alarms = res.data.data.getAlarm.items;
this.total = res.data.data.getAlarm.total;
}
return res.data;
},
async getAlarmTagKeys() {
return await graphql.query("queryAlarmTagKeys").params({ duration: useAppStoreWithOut().durationTime });
const res: AxiosResponse = await graphql
.query("queryAlarmTagKeys")
.params({ duration: useAppStoreWithOut().durationTime });
return res.data;
},
async getAlarmTagValues(tagKey: string) {
return await graphql.query("queryAlarmTagValues").params({ tagKey, duration: useAppStoreWithOut().durationTime });
const res: AxiosResponse = await graphql
.query("queryAlarmTagValues")
.params({ tagKey, duration: useAppStoreWithOut().durationTime });
return res.data;
},
},
});

View File

@ -19,6 +19,7 @@ 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";
@ -117,6 +118,12 @@ 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) {
@ -178,11 +185,11 @@ export const appStore = defineStore({
});
},
async queryOAPTimeInfo() {
const res = await graphql.query("queryOAPTimeInfo").params({});
if (res.errors) {
const res: AxiosResponse = await graphql.query("queryOAPTimeInfo").params({});
if (res.data.errors) {
this.utc = -(new Date().getTimezoneOffset() / 60) + ":0";
} else {
this.utc = res.data.getTimeInfo.timezone / 100 + ":0";
this.utc = res.data.data.getTimeInfo.timezone / 100 + ":0";
}
const utcArr = this.utc.split(":");
this.utcHour = isNaN(Number(utcArr[0])) ? 0 : Number(utcArr[0]);
@ -190,21 +197,21 @@ export const appStore = defineStore({
return res.data;
},
async fetchVersion() {
const res = await graphql.query("queryOAPVersion").params({});
if (res.errors) {
return res;
async fetchVersion(): Promise<void> {
const res: AxiosResponse = await graphql.query("queryOAPVersion").params({});
if (res.data.errors) {
return res.data;
}
this.version = res.data.version;
this.version = res.data.data.version;
return res.data;
},
async queryMenuItems() {
const res = await graphql.query("queryMenuItems").params({});
if (res.errors) {
return res;
const res: AxiosResponse = await graphql.query("queryMenuItems").params({});
if (res.data.errors) {
return res.data;
}
return res.data;
return res.data.data;
},
setReloadTimer(timer: IntervalHandle) {
this.reloadTimer = timer;

View File

@ -1,138 +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.
*/
import { defineStore } from "pinia";
import type {
AsyncProfilingTask,
AsyncProfileTaskCreationRequest,
AsyncProfilerStackElement,
AsyncProfilerTaskProgress,
} from "@/types/async-profiling";
import { store } from "@/store";
import graphql from "@/graphql";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useSelectorStore } from "@/store/modules/selectors";
import type { Instance } from "@/types/selector";
interface AsyncProfilingState {
taskList: Array<Recordable<AsyncProfilingTask>>;
selectedTask: Recordable<AsyncProfilingTask>;
taskProgress: Recordable<AsyncProfilerTaskProgress>;
instances: Instance[];
analyzeTrees: AsyncProfilerStackElement[];
loadingTree: boolean;
loadingTasks: boolean;
}
export const asyncProfilingStore = defineStore({
id: "asyncProfiling",
state: (): AsyncProfilingState => ({
taskList: [],
selectedTask: {},
taskProgress: {},
instances: [],
analyzeTrees: [],
loadingTree: false,
loadingTasks: false,
}),
actions: {
setSelectedTask(task: Recordable<AsyncProfilingTask>) {
this.selectedTask = task || {};
},
setAnalyzeTrees(tree: AsyncProfilerStackElement[]) {
this.analyzeTrees = tree;
},
async getTaskList() {
const selectorStore = useSelectorStore();
this.loadingTasks = true;
const response = await graphql.query("getAsyncTaskList").params({
request: {
serviceId: selectorStore.currentService.id,
limit: 10000,
},
});
this.loadingTasks = false;
if (response.errors) {
return response;
}
this.taskList = response.data.asyncTaskList.tasks || [];
this.selectedTask = this.taskList[0] || {};
this.setAnalyzeTrees([]);
this.setSelectedTask(this.selectedTask);
if (!this.taskList.length) {
return response;
}
return response;
},
async getTaskLogs(param: { taskID: string }) {
const response = await graphql.query("getAsyncProfileTaskProcess").params(param);
if (response.errors) {
return response;
}
this.taskProgress = response.data.taskProgress;
return response;
},
async getServiceInstances(param: { serviceId: string; isRelation: boolean }) {
if (!param.serviceId) {
return null;
}
const response = await graphql.query("queryInstances").params({
serviceId: param.serviceId,
duration: useAppStoreWithOut().durationTime,
});
if (!response.errors) {
this.instances = (response.data.pods || []).map((d: Instance) => {
d.value = d.id || "";
return d;
});
}
return response;
},
async createTask(param: AsyncProfileTaskCreationRequest) {
const response = await graphql.query("saveAsyncProfileTask").params({ asyncProfilerTaskCreationRequest: param });
if (response.errors) {
return response;
}
this.getTaskList();
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 response = await graphql.query("getAsyncProfileAnalyze").params({ request: params });
this.loadingTree = false;
if (response.errors) {
this.analyzeTrees = [];
return response;
}
const { analysisResult } = response.data;
if (!analysisResult) {
this.analyzeTrees = [];
return response;
}
this.analyzeTrees = [analysisResult.tree];
return response;
},
},
});
export function useAsyncProfilingStore(): Recordable {
return asyncProfilingStore(store);
}

View File

@ -21,6 +21,7 @@ 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 {
@ -83,37 +84,37 @@ export const continousProfilingStore = defineStore({
checkItems: CheckItems[];
}[],
) {
const response = await graphql.query("editStrategy").params({
const res: AxiosResponse = await graphql.query("editStrategy").params({
request: {
serviceId,
targets,
},
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
return response;
return res.data;
},
async getStrategyList(params: { serviceId: string }) {
if (!params.serviceId) {
return new Promise((resolve) => resolve({}));
}
this.policyLoading = true;
const response = await graphql.query("getStrategyList").params(params);
const res: AxiosResponse = await graphql.query("getStrategyList").params(params);
this.policyLoading = false;
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
const list = response.data.strategyList || [];
const list = res.data.data.strategyList || [];
if (!list.length) {
this.taskList = [];
this.instances = [];
this.instance = null;
}
const arr = list.length ? response.data.strategyList : [{ type: "", checkItems: [{ type: "" }] }];
const arr = list.length ? res.data.data.strategyList : [{ type: "", checkItems: [{ type: "" }] }];
this.strategyList = arr.map((d: StrategyItem, index: number) => {
return {
...d,
@ -122,25 +123,25 @@ export const continousProfilingStore = defineStore({
});
this.setSelectedStrategy(this.strategyList[0]);
if (!this.selectedStrategy.type) {
return response;
return res.data;
}
this.getMonitoringInstances(params.serviceId);
return response;
return res.data;
},
async getMonitoringInstances(serviceId: string) {
async getMonitoringInstances(serviceId: string): Promise<Nullable<AxiosResponse>> {
this.instancesLoading = true;
if (!serviceId) {
return null;
}
const response = await graphql.query("getMonitoringInstances").params({
const res: AxiosResponse = await graphql.query("getMonitoringInstances").params({
serviceId,
target: this.selectedStrategy.type,
});
this.instancesLoading = false;
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.instances = (response.data.instances || [])
this.instances = (res.data.data.instances || [])
.map((d: MonitorInstance) => {
const processes = (d.processes || [])
.sort((c: MonitorProcess, d: MonitorProcess) => d.lastTriggerTimestamp - c.lastTriggerTimestamp)
@ -160,7 +161,7 @@ export const continousProfilingStore = defineStore({
})
.sort((a: MonitorInstance, b: MonitorInstance) => b.lastTriggerTimestamp - a.lastTriggerTimestamp);
this.instance = this.instances[0] || null;
return response;
return res.data;
},
},
});

View File

@ -18,10 +18,11 @@ import { defineStore } from "pinia";
import { store } from "@/store";
import type { LayoutConfig } from "@/types/dashboard";
import graphql from "@/graphql";
import fetchQuery from "@/graphql/fetch";
import query from "@/graphql/fetch";
import type { DashboardItem } from "@/types/dashboard";
import { useSelectorStore } from "@/store/modules/selectors";
import { NewControl, TextConfig, TimeRangeConfig, ControlsTypes } from "../data";
import type { AxiosResponse } from "axios";
import { ElMessage } from "element-plus";
import { EntityType, WidgetType } from "@/views/dashboard/data";
interface DashboardState {
@ -298,20 +299,37 @@ 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 } }) {
return await fetchQuery(param);
const res: AxiosResponse = await query(param);
return res.data;
},
async fetchTemplates() {
const res = await graphql.query("getTemplates").params({});
const res: AxiosResponse = await graphql.query("getTemplates").params({});
if (res.errors) {
return res;
if (res.data.errors) {
return res.data;
}
const data = res.data.getAllTemplates;
const data = res.data.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,
@ -354,20 +372,20 @@ export const dashboardStore = defineStore({
this.dashboards = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
},
async updateDashboard(setting: { id: string; configuration: string }) {
const resp = await graphql.query("updateTemplate").params({
const res: AxiosResponse = await graphql.query("updateTemplate").params({
setting,
});
if (resp.errors) {
ElMessage.error(resp.errors);
return resp;
if (res.data.errors) {
ElMessage.error(res.data.errors);
return res.data;
}
const json = resp.data.changeTemplate;
const json = res.data.data.changeTemplate;
if (!json.status) {
ElMessage.error(json.message);
return resp;
return res.data;
}
ElMessage.success("Saved successfully");
return resp;
return res.data;
},
async saveDashboard() {
if (!this.currentDashboard?.name) {
@ -401,14 +419,14 @@ export const dashboardStore = defineStore({
}
res = await graphql.query("addNewTemplate").params({ setting: { configuration: JSON.stringify(c) } });
json = res.data.addTemplate;
json = res.data.data.addTemplate;
if (!json.status) {
ElMessage.error(json.message);
}
}
if (res.errors) {
ElMessage.error(res.errors);
return res;
if (res.data.errors || res.errors) {
ElMessage.error(res.data.errors);
return res.data;
}
if (!json.status) {
return json;
@ -430,16 +448,16 @@ export const dashboardStore = defineStore({
return json;
},
async deleteDashboard() {
const res = await graphql.query("removeTemplate").params({ id: this.currentDashboard?.id });
const res: AxiosResponse = await graphql.query("removeTemplate").params({ id: this.currentDashboard?.id });
if (res.errors) {
ElMessage.error(res.errors);
return res;
if (res.data.errors) {
ElMessage.error(res.data.errors);
return res.data;
}
const json = res.data.disableTemplate;
const json = res.data.data.disableTemplate;
if (!json.status) {
ElMessage.error(json.message);
return res;
return res.data;
}
this.dashboards = this.dashboards.filter((d: Recordable) => d.id !== this.currentDashboard?.id);
const key = [this.currentDashboard?.layer, this.currentDashboard?.entity, this.currentDashboard?.name].join("_");

View File

@ -18,6 +18,7 @@ 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";
@ -59,16 +60,16 @@ export const demandLogStore = defineStore({
},
async getInstances(id: string) {
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
const response = await graphql.query("queryInstances").params({
const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.instances = response.data.pods || [];
return response;
this.instances = res.data.data.pods || [];
return res.data;
},
async getContainers(serviceInstanceId: string) {
if (!serviceInstanceId) {
@ -77,35 +78,35 @@ export const demandLogStore = defineStore({
const condition = {
serviceInstanceId,
};
const response = await graphql.query("fetchContainers").params({ condition });
const res: AxiosResponse = await graphql.query("fetchContainers").params({ condition });
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
if (response.data.containers.errorReason) {
if (res.data.data.containers.errorReason) {
this.containers = [{ label: "", value: "" }];
return response;
return res.data;
}
this.containers = response.data.containers.containers.map((d: string) => {
this.containers = res.data.data.containers.containers.map((d: string) => {
return { label: d, value: d };
});
return response;
return res.data;
},
async getDemandLogs() {
this.loadLogs = true;
const response = await graphql.query("fetchDemandPodLogs").params({ condition: this.conditions });
const res: AxiosResponse = await graphql.query("fetchDemandPodLogs").params({ condition: this.conditions });
this.loadLogs = false;
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
if (response.data.logs.errorReason) {
this.setLogs([], response.data.logs.errorReason);
return response;
if (res.data.data.logs.errorReason) {
this.setLogs([], res.data.data.logs.errorReason);
return res.data;
}
this.total = response.data.logs.logs.length;
const logs = response.data.logs.logs.map((d: Log) => d.content).join("\n");
this.total = res.data.data.logs.logs.length;
const logs = res.data.data.logs.logs.map((d: Log) => d.content).join("\n");
this.setLogs(logs);
return response;
return res.data;
},
},
});

View File

@ -19,6 +19,7 @@ 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>>;
@ -56,70 +57,70 @@ export const ebpfStore = defineStore({
this.analyzeTrees = tree;
},
async getCreateTaskData(serviceId: string) {
const response = await graphql.query("getCreateTaskData").params({ serviceId });
const res: AxiosResponse = await graphql.query("getCreateTaskData").params({ serviceId });
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
const json = response.data.createTaskData;
const json = res.data.data.createTaskData;
this.couldProfiling = json.couldProfiling || false;
this.labels = json.processLabels.map((d: string) => {
return { label: d, value: d };
});
return response;
return res.data;
},
async createTask(param: EBPFTaskCreationRequest) {
const response = await graphql.query("saveEBPFTask").params({ request: param });
const res: AxiosResponse = await graphql.query("saveEBPFTask").params({ request: param });
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.getTaskList({
serviceId: param.serviceId,
targets: ["ON_CPU", "OFF_CPU"],
triggerType: EBPFProfilingTriggerType.FIXED_TIME,
});
return response;
return res.data;
},
async getTaskList(params: { serviceId: string; targets: string[] }) {
if (!params.serviceId) {
return new Promise((resolve) => resolve({}));
}
const response = await graphql.query("getEBPFTasks").params(params);
const res: AxiosResponse = await graphql.query("getEBPFTasks").params(params);
this.ebpfTips = "";
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.taskList = response.data.queryEBPFTasks || [];
this.taskList = res.data.data.queryEBPFTasks || [];
this.selectedTask = this.taskList[0] || {};
this.setSelectedTask(this.selectedTask);
if (!this.taskList.length) {
return response;
return res.data;
}
this.getEBPFSchedules({ taskId: String(this.taskList[0].taskId) });
return response;
return res.data;
},
async getEBPFSchedules(params: { taskId: string }) {
if (!params.taskId) {
return new Promise((resolve) => resolve({}));
}
const response = await graphql.query("getEBPFSchedules").params({ ...params });
const res: AxiosResponse = await graphql.query("getEBPFSchedules").params({ ...params });
if (response.errors) {
if (res.data.errors) {
this.eBPFSchedules = [];
return response;
return res.data;
}
this.ebpfTips = "";
const { eBPFSchedules } = response.data;
const { eBPFSchedules } = res.data.data;
this.eBPFSchedules = eBPFSchedules;
if (!eBPFSchedules.length) {
this.eBPFSchedules = [];
this.analyzeTrees = [];
}
return response;
return res.data;
},
async getEBPFAnalyze(params: {
scheduleIdList: string[];
@ -133,24 +134,24 @@ export const ebpfStore = defineStore({
if (!params.timeRanges.length) {
return new Promise((resolve) => resolve({}));
}
const response = await graphql.query("getEBPFResult").params(params);
const res: AxiosResponse = await graphql.query("getEBPFResult").params(params);
if (response.errors) {
if (res.data.errors) {
this.analyzeTrees = [];
return response;
return res.data;
}
const { analysisEBPFResult } = response.data;
const { analysisEBPFResult } = res.data.data;
this.ebpfTips = analysisEBPFResult.tip;
if (!analysisEBPFResult) {
this.analyzeTrees = [];
return response;
return res.data;
}
if (analysisEBPFResult.tip) {
this.analyzeTrees = [];
return response;
return res.data;
}
this.analyzeTrees = analysisEBPFResult.trees;
return response;
return res.data;
},
},
});

View File

@ -17,11 +17,11 @@
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";
import { useSelectorStore } from "@/store/modules/selectors";
import { EndpointsTopNDefault } from "../data";
interface eventState {
loading: boolean;
@ -46,48 +46,47 @@ export const eventStore = defineStore({
},
async getInstances() {
const serviceId = useSelectorStore().currentService ? useSelectorStore().currentService.id : "";
const response = await graphql.query("queryInstances").params({
const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.instances = [{ value: "", label: "All" }, ...response.data.pods];
return response;
this.instances = [{ value: "", label: "All" }, ...res.data.data.pods] || [{ value: "", label: "All" }];
return res.data;
},
async getEndpoints(keyword: string) {
const serviceId = useSelectorStore().currentService ? useSelectorStore().currentService.id : "";
if (!serviceId) {
return;
}
const response = await graphql.query("queryEndpoints").params({
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
keyword: keyword || "",
limit: EndpointsTopNDefault,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.endpoints = [{ value: "", label: "All" }, ...response.data.pods];
return response;
this.endpoints = [{ value: "", label: "All" }, ...res.data.data.pods] || [{ value: "", label: "All" }];
return res.data;
},
async getEvents() {
this.loading = true;
const response = await graphql.query("queryEvents").params({
const res: AxiosResponse = await graphql.query("queryEvents").params({
condition: {
...this.condition,
time: useAppStoreWithOut().durationTime,
},
});
this.loading = false;
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
if (response.data.fetchEvents) {
this.events = (response.data.fetchEvents.events || []).map((item: Event) => {
if (res.data.data.fetchEvents) {
this.events = (res.data.data.fetchEvents.events || []).map((item: Event) => {
let scope = "Service";
if (item.source.serviceInstance) {
scope = "ServiceInstance";
@ -102,7 +101,7 @@ export const eventStore = defineStore({
return item;
});
}
return response;
return res.data;
},
},
});

View File

@ -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 { EndpointsTopNDefault } from "../data";
interface LogState {
services: Service[];
@ -61,51 +61,50 @@ export const logStore = defineStore({
};
},
async getServices(layer: string) {
const response = await graphql.query("queryServices").params({
const res: AxiosResponse = await graphql.query("queryServices").params({
layer,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.services = response.data.services;
return response;
this.services = res.data.data.services;
return res.data;
},
async getInstances(id: string) {
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
const response = await graphql.query("queryInstances").params({
const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.instances = [{ value: "0", label: "All" }, ...response.data.pods];
return response;
this.instances = [{ value: "0", label: "All" }, ...res.data.data.pods] || [{ value: " 0", label: "All" }];
return res.data;
},
async getEndpoints(id: string, keyword?: string) {
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
const response = await graphql.query("queryEndpoints").params({
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
keyword: keyword || "",
limit: EndpointsTopNDefault,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.endpoints = [{ value: "0", label: "All" }, ...response.data.pods];
return response;
this.endpoints = [{ value: "0", label: "All" }, ...res.data.data.pods] || [{ value: "0", label: "All" }];
return res.data;
},
async getLogsByKeywords() {
const response = await graphql.query("queryLogsByKeywords").params({});
const res: AxiosResponse = await graphql.query("queryLogsByKeywords").params({});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.supportQueryLogsByKeywords = response.data.support;
return response;
this.supportQueryLogsByKeywords = res.data.data.support;
return res.data;
},
async getLogs() {
const dashboardStore = useDashboardStore();
@ -116,31 +115,39 @@ export const logStore = defineStore({
},
async getServiceLogs() {
this.loadLogs = true;
const response = await graphql.query("queryServiceLogs").params({ condition: this.conditions });
const res: AxiosResponse = await graphql.query("queryServiceLogs").params({ condition: this.conditions });
this.loadLogs = false;
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.logs = response.data.queryLogs.logs;
return response;
this.logs = res.data.data.queryLogs.logs;
return res.data;
},
async getBrowserLogs() {
this.loadLogs = true;
const response = await graphql.query("queryBrowserErrorLogs").params({ condition: this.conditions });
const res: AxiosResponse = await graphql.query("queryBrowserErrorLogs").params({ condition: this.conditions });
this.loadLogs = false;
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.logs = response.data.queryBrowserErrorLogs.logs;
return response;
this.logs = res.data.data.queryBrowserErrorLogs.logs;
return res.data;
},
async getLogTagKeys() {
return await graphql.query("queryLogTagKeys").params({ duration: useAppStoreWithOut().durationTime });
const res: AxiosResponse = await graphql
.query("queryLogTagKeys")
.params({ duration: useAppStoreWithOut().durationTime });
return res.data;
},
async getLogTagValues(tagKey: string) {
return await graphql.query("queryLogTagValues").params({ tagKey, duration: useAppStoreWithOut().durationTime });
const res: AxiosResponse = await graphql
.query("queryLogTagValues")
.params({ tagKey, duration: useAppStoreWithOut().durationTime });
return res.data;
},
},
});

View File

@ -18,6 +18,7 @@ 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";
@ -125,65 +126,65 @@ export const networkProfilingStore = defineStore({
minDuration: number;
}[],
) {
const response = await graphql.query("newNetworkProfiling").params({
const res: AxiosResponse = await graphql.query("newNetworkProfiling").params({
request: {
instanceId,
samplings: params,
},
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
return response;
return res.data;
},
async getTaskList(params: { serviceId: string; serviceInstanceId: string; targets: string[] }) {
if (!params.serviceId) {
return new Promise((resolve) => resolve({}));
}
const response = await graphql.query("getEBPFTasks").params(params);
const res: AxiosResponse = await graphql.query("getEBPFTasks").params(params);
this.networkTip = "";
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.networkTasks = response.data.queryEBPFTasks || [];
this.networkTasks = res.data.data.queryEBPFTasks || [];
this.selectedNetworkTask = this.networkTasks[0] || {};
this.setSelectedNetworkTask(this.selectedNetworkTask);
if (!this.networkTasks.length) {
this.nodes = [];
this.calls = [];
}
return response;
return res.data;
},
async keepNetworkProfiling(taskId: string) {
if (!taskId) {
return new Promise((resolve) => resolve({}));
}
const response = await graphql.query("aliveNetworkProfiling").params({ taskId });
const res: AxiosResponse = await graphql.query("aliveNetworkProfiling").params({ taskId });
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.aliveNetwork = response.data.keepEBPFNetworkProfiling.status;
this.aliveNetwork = res.data.data.keepEBPFNetworkProfiling.status;
if (!this.aliveNetwork) {
ElMessage.warning(response.data.keepEBPFNetworkProfiling.errorReason);
ElMessage.warning(res.data.data.keepEBPFNetworkProfiling.errorReason);
}
return response;
return res.data;
},
async getProcessTopology(params: { duration: DurationTime; serviceInstanceId: string }) {
this.loadNodes = true;
const response = await graphql.query("getProcessTopology").params(params);
const res: AxiosResponse = await graphql.query("getProcessTopology").params(params);
this.loadNodes = false;
if (response.errors) {
if (res.data.errors) {
this.nodes = [];
this.calls = [];
return response;
return res.data;
}
const { topology } = response.data;
const { topology } = res.data.data;
this.setTopology(topology);
return response;
return res.data;
},
},
});

View File

@ -26,8 +26,8 @@ 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";
interface ProfileState {
endpoints: Endpoint[];
@ -80,7 +80,7 @@ export const profileStore = defineStore({
this.analyzeTrees = [];
},
setCurrentSegment(segment: Trace) {
this.currentSegment = segment || {};
this.currentSegment = segment;
this.segmentSpans = segment.spans || [];
if (segment.spans) {
this.currentSpan = segment.spans[0] || {};
@ -93,38 +93,36 @@ export const profileStore = defineStore({
this.highlightTop = !this.highlightTop;
},
async getEndpoints(serviceId: string, keyword?: string) {
const response = await graphql.query("queryEndpoints").params({
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
keyword: keyword || "",
limit: EndpointsTopNDefault,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.endpoints = response.data.pods || [];
return response.data;
this.endpoints = res.data.data.pods || [];
return res.data;
},
async getTaskEndpoints(serviceId: string, keyword?: string) {
const response = await graphql.query("queryEndpoints").params({
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
keyword: keyword || "",
limit: EndpointsTopNDefault,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.taskEndpoints = [{ value: "", label: "All" }, ...response.data.pods];
return response;
this.taskEndpoints = [{ value: "", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getTaskList() {
const response = await graphql.query("getProfileTaskList").params(this.condition);
const res: AxiosResponse = await graphql.query("getProfileTaskList").params(this.condition);
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
const list = response.data.taskList || [];
const list = res.data.data.taskList || [];
this.taskList = list;
this.currentTask = list[0] || {};
if (!list.length) {
@ -132,52 +130,52 @@ export const profileStore = defineStore({
this.segmentSpans = [];
this.analyzeTrees = [];
return response;
return res.data;
}
this.getSegmentList({ taskID: list[0].id });
return response;
return res.data;
},
async getSegmentList(params: { taskID: string }) {
if (!params.taskID) {
return new Promise((resolve) => resolve({}));
}
const response = await graphql.query("getProfileTaskSegmentList").params(params);
const res: AxiosResponse = await graphql.query("getProfileTaskSegmentList").params(params);
if (response.errors) {
if (res.data.errors) {
this.segmentList = [];
return response;
return res.data;
}
const { segmentList } = response.data;
const { segmentList } = res.data.data;
this.segmentList = segmentList || [];
if (!segmentList.length) {
this.segmentSpans = [];
this.analyzeTrees = [];
return response;
return res.data;
}
if (segmentList[0]) {
this.setCurrentSegment(segmentList[0]);
this.currentSegment = segmentList[0];
this.getSegmentSpans(segmentList[0].segmentId);
} else {
this.setCurrentSegment({});
this.currentSegment = {};
}
return response;
return res.data;
},
async getSegmentSpans(params: { segmentId: string }) {
if (!(params && params.segmentId)) {
return new Promise((resolve) => resolve({}));
}
const response = await graphql.query("queryProfileSegment").params(params);
if (response.errors) {
const res: AxiosResponse = await graphql.query("queryProfileSegment").params(params);
if (res.data.errors) {
this.segmentSpans = [];
return response;
return res.data;
}
const { segment } = response.data;
const { segment } = res.data.data;
if (!segment) {
this.segmentSpans = [];
this.analyzeTrees = [];
return response;
return res.data;
}
this.segmentSpans = segment.spans.map((d: SegmentSpan) => {
return {
@ -188,52 +186,52 @@ export const profileStore = defineStore({
});
if (!(segment.spans && segment.spans.length)) {
this.analyzeTrees = [];
return response;
return res.data;
}
const index = segment.spans.length - 1 || 0;
this.currentSpan = segment.spans[index];
return response;
return res.data;
},
async getProfileAnalyze(params: Array<{ segmentId: string; timeRange: { start: number; end: number } }>) {
if (!params.length) {
return new Promise((resolve) => resolve({}));
}
const response = await graphql.query("getProfileAnalyze").params({ queries: params });
const res: AxiosResponse = await graphql.query("getProfileAnalyze").params({ queries: params });
if (response.errors) {
if (res.data.errors) {
this.analyzeTrees = [];
return response;
return res.data;
}
const { analyze, tip } = response.data;
const { analyze, tip } = res.data.data;
if (tip) {
this.analyzeTrees = [];
return response;
return res.data;
}
if (!analyze) {
this.analyzeTrees = [];
return response;
return res.data;
}
this.analyzeTrees = analyze.trees;
return response;
return res.data;
},
async createTask(param: ProfileTaskCreationRequest) {
const response = await graphql.query("saveProfileTask").params({ creationRequest: param });
const res: AxiosResponse = await graphql.query("saveProfileTask").params({ creationRequest: param });
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.getTaskList();
return response;
return res.data;
},
async getTaskLogs(param: { taskID: string }) {
const response = await graphql.query("getProfileTaskLogs").params(param);
const res: AxiosResponse = await graphql.query("getProfileTaskLogs").params(param);
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.taskLogs = response.data.taskLogs;
return response;
this.taskLogs = res.data.data.taskLogs;
return res.data;
},
},
});

View File

@ -18,8 +18,8 @@ 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 {
services: Service[];
destServices: Service[];
@ -76,55 +76,62 @@ export const selectorStore = defineStore({
setDestProcesses(processes: Array<Process>) {
this.destProcesses = processes;
},
async fetchLayers() {
return await graphql.query("queryLayers").params({});
},
async fetchServices(layer: string) {
const res = await graphql.query("queryServices").params({ layer });
async fetchLayers(): Promise<AxiosResponse> {
const res: AxiosResponse = await graphql.query("queryLayers").params({});
if (!res.errors) {
this.services = res.data.services || [];
this.destServices = res.data.services || [];
return res.data || {};
},
async fetchServices(layer: string): Promise<AxiosResponse> {
const res: AxiosResponse = await graphql.query("queryServices").params({ layer });
if (!res.data.errors) {
this.services = res.data.data.services || [];
this.destServices = res.data.data.services || [];
}
return res.data;
},
async getServiceInstances(param?: { serviceId: string; isRelation: boolean }) {
async getServiceInstances(param?: { serviceId: string; isRelation: boolean }): Promise<Nullable<AxiosResponse>> {
const serviceId = param ? param.serviceId : this.currentService?.id;
if (!serviceId) {
return null;
}
const resp = await graphql.query("queryInstances").params({
const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
});
if (!resp.errors) {
if (!res.data.errors) {
if (param && param.isRelation) {
this.destPods = resp.data.pods || [];
return resp;
this.destPods = res.data.data.pods || [];
return res.data;
}
this.pods = resp.data.pods || [];
this.pods = res.data.data.pods || [];
}
return resp;
return res.data;
},
async getProcesses(param?: { instanceId: string; isRelation: boolean }) {
async getProcesses(param?: { instanceId: string; isRelation: boolean }): Promise<Nullable<AxiosResponse>> {
const instanceId = param ? param.instanceId : this.currentPod?.id;
if (!instanceId) {
return null;
}
const res = await graphql.query("queryProcesses").params({
const res: AxiosResponse = await graphql.query("queryProcesses").params({
instanceId,
duration: useAppStoreWithOut().durationTime,
});
if (!res.errors) {
if (!res.data.errors) {
if (param && param.isRelation) {
this.destProcesses = res.data.processes || [];
return res;
this.destProcesses = res.data.data.processes || [];
return res.data;
}
this.processes = res.data.processes || [];
this.processes = res.data.data.processes || [];
}
return res;
return res.data;
},
async getEndpoints(params: { keyword?: string; serviceId?: string; isRelation?: boolean; limit?: number }) {
async getEndpoints(params: {
keyword?: string;
serviceId?: string;
isRelation?: boolean;
limit?: number;
}): Promise<Nullable<AxiosResponse>> {
if (!params) {
params = {};
}
@ -132,96 +139,96 @@ export const selectorStore = defineStore({
if (!serviceId) {
return null;
}
const res = await graphql.query("queryEndpoints").params({
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
keyword: params.keyword || "",
limit: params.limit || EndpointsTopNDefault,
limit: params.limit,
});
if (!res.errors) {
if (!res.data.errors) {
if (params.isRelation) {
this.destPods = res.data.pods || [];
return res;
this.destPods = res.data.data.pods || [];
return res.data;
}
this.pods = res.data.pods || [];
this.pods = res.data.data.pods || [];
}
return res;
return res.data;
},
async getService(serviceId: string, isRelation: boolean) {
if (!serviceId) {
return;
}
const res = await graphql.query("queryService").params({
const res: AxiosResponse = await graphql.query("queryService").params({
serviceId,
});
if (!res.errors) {
if (!res.data.errors) {
if (isRelation) {
this.setCurrentDestService(res.data.service);
this.destServices = [res.data.service];
return res;
this.setCurrentDestService(res.data.data.service);
this.destServices = [res.data.data.service];
return res.data;
}
this.setCurrentService(res.data.service);
this.services = [res.data.service];
this.setCurrentService(res.data.data.service);
this.services = [res.data.data.service];
}
return res;
return res.data;
},
async getInstance(instanceId: string, isRelation?: boolean) {
if (!instanceId) {
return;
}
const res = await graphql.query("queryInstance").params({
const res: AxiosResponse = await graphql.query("queryInstance").params({
instanceId,
});
if (!res.errors) {
if (!res.data.errors) {
if (isRelation) {
this.currentDestPod = res.data.instance || null;
this.destPods = [res.data.instance];
return res;
this.currentDestPod = res.data.data.instance || null;
this.destPods = [res.data.data.instance];
return res.data;
}
this.currentPod = res.data.instance || null;
this.pods = [res.data.instance];
this.currentPod = res.data.data.instance || null;
this.pods = [res.data.data.instance];
}
return res;
return res.data;
},
async getEndpoint(endpointId: string, isRelation?: string) {
if (!endpointId) {
return;
}
const res = await graphql.query("queryEndpoint").params({
const res: AxiosResponse = await graphql.query("queryEndpoint").params({
endpointId,
});
if (res.errors) {
return res;
if (res.data.errors) {
return res.data;
}
if (isRelation) {
this.currentDestPod = res.data.endpoint || null;
this.destPods = [res.data.endpoint];
return res;
this.currentDestPod = res.data.data.endpoint || null;
this.destPods = [res.data.data.endpoint];
return res.data;
}
this.currentPod = res.data.endpoint || null;
this.pods = [res.data.endpoint];
return res;
this.currentPod = res.data.data.endpoint || null;
this.pods = [res.data.data.endpoint];
return res.data;
},
async getProcess(processId: string, isRelation?: boolean) {
if (!processId) {
return;
}
const res = await graphql.query("queryProcess").params({
const res: AxiosResponse = await graphql.query("queryProcess").params({
processId,
});
if (!res.errors) {
if (!res.data.errors) {
if (isRelation) {
this.currentDestProcess = res.data.process || null;
this.destProcesses = [res.data.process];
this.currentDestProcess = res.data.data.process || null;
this.destProcesses = [res.data.data.process];
return res.data;
}
this.currentProcess = res.data.process || null;
this.processes = [res.data.process];
this.currentProcess = res.data.data.process || null;
this.processes = [res.data.data.process];
}
return res;
return res.data;
},
},
});

View File

@ -18,6 +18,7 @@ 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";
@ -56,18 +57,20 @@ export const taskTimelineStore = defineStore({
return new Promise((resolve) => resolve({}));
}
this.loading = true;
const response = await graphql.query("getEBPFTasks").params(params);
const res: AxiosResponse = await graphql.query("getEBPFTasks").params(params);
this.loading = false;
this.errorTip = "";
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
const selectorStore = useSelectorStore();
this.taskList = (response.data.queryEBPFTasks || []).filter(
this.taskList = (res.data.data.queryEBPFTasks || []).filter(
(d: EBPFTaskList) => selectorStore.currentProcess && d.processId === selectorStore.currentProcess.id,
);
return response;
// this.selectedTask = this.taskList[0] || {};
// await this.getGraphData();
return res.data;
},
async getGraphData() {
let res: any = {};

View File

@ -21,8 +21,10 @@ import graphql from "@/graphql";
import { useSelectorStore } from "@/store/modules/selectors";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import fetchQuery from "@/graphql/fetch";
import type { AxiosResponse } from "axios";
import query from "@/graphql/fetch";
import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor";
import { ElMessage } from "element-plus";
interface MetricVal {
[key: string]: { values: { id: string; value: unknown }[] };
@ -304,14 +306,14 @@ export const topologyStore = defineStore({
return new Promise((resolve) => resolve({}));
}
const duration = useAppStoreWithOut().durationTime;
const res = await graphql.query("getServicesTopology").params({
const res: AxiosResponse = await graphql.query("getServicesTopology").params({
serviceIds,
duration,
});
if (res.errors) {
return res;
if (res.data.errors) {
return res.data;
}
return res.data.topology;
return res.data.data.topology;
},
async getInstanceTopology() {
const { currentService, currentDestService } = useSelectorStore();
@ -321,15 +323,15 @@ export const topologyStore = defineStore({
if (!(serverServiceId && clientServiceId)) {
return new Promise((resolve) => resolve({}));
}
const res = await graphql.query("getInstanceTopology").params({
const res: AxiosResponse = await graphql.query("getInstanceTopology").params({
clientServiceId,
serverServiceId,
duration,
});
if (!res.errors) {
this.setInstanceTopology(res.data.topology);
if (!res.data.errors) {
this.setInstanceTopology(res.data.data.topology);
}
return res;
return res.data;
},
async updateEndpointTopology(endpointIds: string[], depth: number) {
if (!endpointIds.length) {
@ -337,10 +339,7 @@ export const topologyStore = defineStore({
}
const res = await this.getEndpointTopology(endpointIds);
if (depth > 1) {
const userNodeName = "User";
const ids = res.nodes
.filter((d: Node) => !endpointIds.includes(d.id) && d.name !== userNodeName)
.map((item: Node) => item.id);
const ids = res.nodes.map((item: Node) => item.id).filter((d: string) => !endpointIds.includes(d));
if (!ids.length) {
this.setTopology(res);
return;
@ -348,8 +347,8 @@ export const topologyStore = defineStore({
const json = await this.getEndpointTopology(ids);
if (depth > 2) {
const pods = json.nodes
.filter((d: Node) => ![...ids, ...endpointIds].includes(d.id) && d.name !== userNodeName)
.map((item: Node) => item.id);
.map((item: Node) => item.id)
.filter((d: string) => ![...ids, ...endpointIds].includes(d));
if (!pods.length) {
const nodes = [...res.nodes, ...json.nodes];
const calls = [...res.calls, ...json.calls];
@ -359,8 +358,8 @@ export const topologyStore = defineStore({
const topo = await this.getEndpointTopology(pods);
if (depth > 3) {
const endpoints = topo.nodes
.filter((d: Node) => ![...ids, ...pods, ...endpointIds].includes(d.id) && d.name !== userNodeName)
.map((item: Node) => item.id);
.map((item: Node) => item.id)
.filter((d: string) => ![...ids, ...pods, ...endpointIds].includes(d));
if (!endpoints.length) {
const nodes = [...res.nodes, ...json.nodes, ...topo.nodes];
const calls = [...res.calls, ...json.calls, ...topo.calls];
@ -370,11 +369,8 @@ export const topologyStore = defineStore({
const data = await this.getEndpointTopology(endpoints);
if (depth > 4) {
const nodeIds = data.nodes
.filter(
(d: Node) =>
![...endpoints, ...ids, ...pods, ...endpointIds].includes(d.id) && d.name !== userNodeName,
)
.map((item: Node) => item.id);
.map((item: Node) => item.id)
.filter((d: string) => ![...endpoints, ...ids, ...pods, ...endpointIds].includes(d));
if (!nodeIds.length) {
const nodes = [...res.nodes, ...json.nodes, ...topo.nodes, ...data.nodes];
const calls = [...res.calls, ...json.calls, ...topo.calls, ...data.calls];
@ -431,12 +427,12 @@ export const topologyStore = defineStore({
});
const queryStr = `query queryData(${variables}) {${fragment}}`;
const conditions = { duration };
const res = await fetchQuery({ queryStr, conditions });
const res: AxiosResponse = await query({ queryStr, conditions });
if (res.errors) {
return res;
if (res.data.errors) {
return res.data;
}
const topo = res.data;
const topo = res.data.data;
const calls = [] as Call[];
const nodes = [] as Node[];
for (const key of Object.keys(topo)) {
@ -447,14 +443,14 @@ export const topologyStore = defineStore({
return { calls, nodes };
},
async getTopologyExpressionValue(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
const res = await fetchQuery(param);
async getNodeExpressionValue(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
const res: AxiosResponse = await query(param);
if (res.errors) {
return res;
if (res.data.errors) {
return res.data;
}
return res;
return res.data;
},
async getLinkExpressions(expressions: string[], type: string) {
if (!expressions.length) {
@ -465,8 +461,14 @@ export const topologyStore = defineStore({
if (!calls.length) {
return;
}
const { getMetrics } = useQueryTopologyExpressionsProcessor(expressions, calls);
const metrics = await getMetrics();
const { getExpressionQuery, handleExpressionValues } = useQueryTopologyExpressionsProcessor(expressions, calls);
const param = getExpressionQuery();
const res = await this.getNodeExpressionValue(param);
if (res.errors) {
ElMessage.error(res.errors);
return;
}
const metrics = handleExpressionValues(res.data);
if (type === "SERVER") {
this.setLinkServerMetrics(metrics);
} else {
@ -482,11 +484,17 @@ export const topologyStore = defineStore({
this.setNodeMetricValue({});
return;
}
const { getMetrics } = useQueryTopologyExpressionsProcessor(
const { getExpressionQuery, handleExpressionValues } = useQueryTopologyExpressionsProcessor(
expressions,
this.nodes.filter((d: Node) => d.isReal),
);
const metrics = await getMetrics();
const param = getExpressionQuery();
const res = await this.getNodeExpressionValue(param);
if (res.errors) {
ElMessage.error(res.errors);
return;
}
const metrics = handleExpressionValues(res.data);
this.setNodeMetricValue(metrics);
},
async getHierarchyServiceTopology() {
@ -502,20 +510,22 @@ export const topologyStore = defineStore({
if (!(id && layer)) {
return new Promise((resolve) => resolve({}));
}
const res = await graphql.query("getHierarchyServiceTopology").params({ serviceId: id, layer: layer });
if (res.errors) {
return res;
const res: AxiosResponse = await graphql
.query("getHierarchyServiceTopology")
.params({ serviceId: id, layer: layer });
if (res.data.errors) {
return res.data;
}
const resp = await this.getListLayerLevels();
if (resp.errors) {
return resp;
}
const levels = resp.levels || [];
this.setHierarchyServiceTopology(res.data.hierarchyServiceTopology || {}, levels);
return res;
const levels = resp.data.levels || [];
this.setHierarchyServiceTopology(res.data.data.hierarchyServiceTopology || {}, levels);
return res.data;
},
async getListLayerLevels() {
const res = await graphql.query("queryListLayerLevels").params({});
const res: AxiosResponse = await graphql.query("queryListLayerLevels").params({});
return res.data;
},
@ -526,19 +536,30 @@ export const topologyStore = defineStore({
if (!(currentPod && dashboardStore.layerId)) {
return new Promise((resolve) => resolve({}));
}
const res = await graphql
const res: AxiosResponse = await graphql
.query("getHierarchyInstanceTopology")
.params({ instanceId: currentPod.id, layer: dashboardStore.layerId });
if (res.errors) {
return res;
if (res.data.errors) {
return res.data;
}
const resp = await this.getListLayerLevels();
if (resp.errors) {
return resp;
}
const levels = resp.levels || [];
this.setHierarchyInstanceTopology(res.data.hierarchyInstanceTopology || {}, levels);
return res;
const levels = resp.data.levels || [];
this.setHierarchyInstanceTopology(res.data.data.hierarchyInstanceTopology || {}, levels);
return res.data;
},
async queryHierarchyExpressions(expressions: string[], nodes: Node[]) {
const { getExpressionQuery, handleExpressionValues } = useQueryTopologyExpressionsProcessor(expressions, nodes);
const param = getExpressionQuery();
const res = await this.getNodeExpressionValue(param);
if (res.errors) {
ElMessage.error(res.errors);
return;
}
const metrics = handleExpressionValues(res.data);
return metrics;
},
async queryHierarchyNodeExpressions(expressions: string[], layer: string) {
const nodes = this.hierarchyServiceNodes.filter((n: HierarchyNode) => n.layer === layer);
@ -550,8 +571,7 @@ export const topologyStore = defineStore({
this.setHierarchyNodeMetricValue({}, layer);
return;
}
const { getMetrics } = useQueryTopologyExpressionsProcessor(expressions, nodes);
const metrics = await getMetrics();
const metrics = await this.queryHierarchyExpressions(expressions, nodes);
this.setHierarchyNodeMetricValue(metrics, layer);
},
async queryHierarchyInstanceNodeExpressions(expressions: string[], layer: string) {
@ -565,8 +585,7 @@ export const topologyStore = defineStore({
this.setHierarchyInstanceNodeMetricValue({}, layer);
return;
}
const { getMetrics } = useQueryTopologyExpressionsProcessor(expressions, nodes);
const metrics = await getMetrics();
const metrics = await this.queryHierarchyExpressions(expressions, nodes);
this.setHierarchyInstanceNodeMetricValue(metrics, layer);
},
},

View File

@ -19,10 +19,10 @@ 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";
interface TraceState {
services: Service[];
instances: Instance[];
@ -33,7 +33,6 @@ interface TraceState {
conditions: Recordable;
traceSpanLogs: Recordable[];
selectorStore: Recordable;
selectedSpan: Recordable<Span>;
}
export const traceStore = defineStore({
@ -45,7 +44,6 @@ export const traceStore = defineStore({
traceList: [],
traceSpans: [],
currentTrace: {},
selectedSpan: {},
conditions: {
queryDuration: useAppStoreWithOut().durationTime,
traceState: "ALL",
@ -65,9 +63,6 @@ export const traceStore = defineStore({
setTraceSpans(spans: Span[]) {
this.traceSpans = spans;
},
setSelectedSpan(span: Span) {
this.selectedSpan = span;
},
resetState() {
this.traceSpans = [];
this.traceList = [];
@ -80,115 +75,124 @@ export const traceStore = defineStore({
};
},
async getServices(layer: string) {
const response = await graphql.query("queryServices").params({
const res: AxiosResponse = await graphql.query("queryServices").params({
layer,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.services = response.data.services;
return response;
this.services = res.data.data.services;
return res.data;
},
async getService(serviceId: string) {
if (!serviceId) {
return;
}
const response = await graphql.query("queryService").params({
const res: AxiosResponse = await graphql.query("queryService").params({
serviceId,
});
return response;
return res.data;
},
async getInstance(instanceId: string) {
if (!instanceId) {
return;
}
const response = await graphql.query("queryInstance").params({
const res: AxiosResponse = await graphql.query("queryInstance").params({
instanceId,
});
return response;
return res.data;
},
async getEndpoint(endpointId: string) {
if (!endpointId) {
return;
}
return await graphql.query("queryEndpoint").params({
const res: AxiosResponse = await graphql.query("queryEndpoint").params({
endpointId,
});
return res.data;
},
async getInstances(id: string) {
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
const response = await graphql.query("queryInstances").params({
const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId: serviceId,
duration: useAppStoreWithOut().durationTime,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.instances = [{ value: "0", label: "All" }, ...response.data.pods];
return response;
this.instances = [{ value: "0", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getEndpoints(id: string, keyword?: string) {
const serviceId = this.selectorStore.currentService ? this.selectorStore.currentService.id : id;
const response = await graphql.query("queryEndpoints").params({
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
keyword: keyword || "",
limit: EndpointsTopNDefault,
});
if (response.errors) {
return response;
if (res.data.errors) {
return res.data;
}
this.endpoints = [{ value: "0", label: "All" }, ...response.data.pods];
return response;
this.endpoints = [{ value: "0", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getTraces() {
const response = await graphql.query("queryTraces").params({ condition: this.conditions });
if (response.errors) {
return response;
const res: AxiosResponse = await graphql.query("queryTraces").params({ condition: this.conditions });
if (res.data.errors) {
return res.data;
}
if (!response.data.data.traces.length) {
if (!res.data.data.data.traces.length) {
this.traceList = [];
this.setCurrentTrace({});
this.setTraceSpans([]);
return response;
return res.data;
}
this.getTraceSpans({ traceId: response.data.data.traces[0].traceIds[0] });
this.traceList = response.data.data.traces.map((d: Trace) => {
this.getTraceSpans({ traceId: res.data.data.data.traces[0].traceIds[0] });
this.traceList = res.data.data.data.traces.map((d: Trace) => {
d.traceIds = d.traceIds.map((id: string) => {
return { value: id, label: id };
});
return d;
});
this.setCurrentTrace(response.data.data.traces[0] || {});
return response;
this.setCurrentTrace(res.data.data.data.traces[0] || {});
return res.data;
},
async getTraceSpans(params: { traceId: string }) {
const response = await graphql.query("queryTrace").params(params);
if (response.errors) {
return response;
const res: AxiosResponse = await graphql.query("queryTrace").params(params);
if (res.data.errors) {
return res.data;
}
const data = response.data.trace.spans;
const data = res.data.data.trace.spans;
this.setTraceSpans(data || []);
return response;
return res.data;
},
async getSpanLogs(params: Recordable) {
const response = await graphql.query("queryServiceLogs").params(params);
if (response.errors) {
const res: AxiosResponse = await graphql.query("queryServiceLogs").params(params);
if (res.data.errors) {
this.traceSpanLogs = [];
return response;
return res.data;
}
this.traceSpanLogs = response.data.queryLogs.logs || [];
return response;
this.traceSpanLogs = res.data.data.queryLogs.logs || [];
return res.data;
},
async getTagKeys() {
return await graphql.query("queryTraceTagKeys").params({ duration: useAppStoreWithOut().durationTime });
const res: AxiosResponse = await graphql
.query("queryTraceTagKeys")
.params({ duration: useAppStoreWithOut().durationTime });
return res.data;
},
async getTagValues(tagKey: string) {
return await graphql.query("queryTraceTagValues").params({ tagKey, duration: useAppStoreWithOut().durationTime });
const res: AxiosResponse = await graphql
.query("queryTraceTagValues")
.params({ tagKey, duration: useAppStoreWithOut().durationTime });
return res.data;
},
},
});

View File

@ -139,10 +139,6 @@
margin-bottom: 10px;
}
.mb-20 {
margin-bottom: 20px;
}
.mr-5 {
margin-right: 5px;
}

View File

@ -32,7 +32,6 @@
html {
--el-color-primary: #409eff;
--el-color-info-light-9: #666;
--theme-background: #fff;
--font-color: #3d444f;
--disabled-color: #ccc;
@ -70,12 +69,10 @@ html {
--sw-drawer-header: #72767b;
--sw-marketplace-border: #dedfe0;
--sw-grid-item-active: #d4d7de;
--sw-trace-line: #999;
}
html.dark {
--el-color-primary: #409eff;
--el-color-info-light-9: #333;
--theme-background: #212224;
--font-color: #fafbfc;
--disabled-color: #999;
@ -113,16 +110,14 @@ html.dark {
--sw-drawer-header: #e9e9eb;
--sw-marketplace-border: #606266;
--sw-grid-item-active: #73767a;
--sw-trace-line: #e8e8e8;
}
.el-drawer__header {
color: var(--sw-drawer-header);
}
.el-table {
--el-table-tr-bg-color: var(--theme-background);
--el-table-header-bg-color: var(--theme-background);
.el-table tr {
background-color: var(--el-table-tr-bg-color);
}
.el-popper.is-light {
@ -134,6 +129,27 @@ html.dark {
--el-switch-off-color: #aaa;
}
.el-table__body-wrapper tr td.el-table-fixed-column--left,
.el-table__body-wrapper tr td.el-table-fixed-column--right,
.el-table__body-wrapper tr th.el-table-fixed-column--left,
.el-table__body-wrapper tr th.el-table-fixed-column--right,
.el-table__footer-wrapper tr td.el-table-fixed-column--left,
.el-table__footer-wrapper tr td.el-table-fixed-column--right,
.el-table__footer-wrapper tr th.el-table-fixed-column--left,
.el-table__footer-wrapper tr th.el-table-fixed-column--right,
.el-table__header-wrapper tr td.el-table-fixed-column--left,
.el-table__header-wrapper tr td.el-table-fixed-column--right,
.el-table__header-wrapper tr th.el-table-fixed-column--left,
.el-table__header-wrapper tr th.el-table-fixed-column--right {
background-color: var(--sw-table-col);
}
.el-table.is-scrolling-none th.el-table-fixed-column--left,
.el-table.is-scrolling-none th.el-table-fixed-column--right,
.el-table th.el-table__cell {
background-color: var(--sw-table-col);
}
$tool-icon-btn-bg: var(--sw-icon-btn-bg);
$tool-icon-btn-color: var(--sw-icon-btn-color);
$popper-hover-bg-color: var(--popper-hover-bg);
@ -245,10 +261,6 @@ div:has(> a.menu-title) {
text-align: left !important;
}
.el-input--small .el-input__inner {
--el-input-inner-height: calc(var(--el-input-height, 24px));
}
html {
&::view-transition-old(root),
&::view-transition-new(root) {

View File

@ -27,7 +27,6 @@ export interface Alarm {
scope: string;
tags: Array<{ key: string; value: string }>;
events: Event[];
snapshot: Indexable;
}
export interface Event {

View File

@ -1,68 +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.
*/
export type AsyncProfilingTask = {
id: string;
serviceId: string;
serviceInstanceIds: string[];
createTime: number;
events: string;
duration: number;
execArgs: string;
};
export type AsyncProfileTaskCreationRequest = {
serviceId: string;
serviceInstanceIds: string[];
duration: number;
events: string[];
execArgs: string;
};
export type AsyncProfilerStackElement = {
id: string;
parentId: string;
codeSignature: string;
total: number;
self: number;
};
export type AsyncProfilerTaskProgress = {
errorInstanceIds: string[];
successInstanceIds: string[];
logs: AsyncProfilerTaskLog[];
};
type AsyncProfilerTaskLog = {
id: string;
instanceId: string;
instanceName: string;
operationType: string;
operationTime: number;
};
export type StackElement = {
id: string;
originId: string;
name: string;
parentId: string;
codeSignature: string;
total: number;
self: number;
value: number;
children?: StackElement[];
};

View File

@ -12,8 +12,6 @@ declare module 'vue' {
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElDialog: typeof import('element-plus/es')['ElDialog']
@ -44,9 +42,8 @@ declare module 'vue' {
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
Graph: typeof import('./../components/Graph/Graph.vue')['default']
Graph: typeof import('./../components/Graph.vue')['default']
Icon: typeof import('./../components/Icon.vue')['default']
Legend: typeof import('./../components/Graph/Legend.vue')['default']
Radio: typeof import('./../components/Radio.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']

View File

@ -46,7 +46,6 @@ export interface LayoutConfig {
relatedTrace?: RelatedTrace;
subExpressions?: string[];
subTypesOfMQE?: string[];
valueRelatedDashboard?: string;
}
export type RelatedTrace = {
duration: DurationTime;
@ -111,7 +110,6 @@ export interface LineConfig extends AreaConfig {
showYAxis?: boolean;
smallTips?: boolean;
showlabels?: boolean;
noTooltips?: boolean;
}
export interface AreaConfig {
@ -125,7 +123,6 @@ export interface CardConfig {
fontSize?: number;
showUnit?: boolean;
textAlign?: "center" | "right" | "left";
valueMappings?: { [key: string]: string };
}
export interface TextConfig {
@ -197,19 +194,4 @@ export type LegendOptions = {
asTable: boolean;
toTheRight: boolean;
width: number;
asSelector: boolean;
};
export type MetricsResults = {
metric: { labels: MetricLabel[] };
values: MetricValue[];
};
type MetricLabel = {
key: string;
value: string;
};
type MetricValue = {
name: string;
value: string;
owner: null | string;
refId: null | string;
};

View File

@ -41,9 +41,6 @@ export interface TaskListItem {
dumpPeriod: number;
maxSamplingCount: number;
logs: TaskLog[];
errorInstanceIds: string[];
successInstanceIds: string[];
serviceInstanceIds: string[];
}
export interface SegmentSpan {
spanId: string;

10
src/types/trace.d.ts vendored
View File

@ -48,10 +48,9 @@ export interface Span {
logs?: log[];
parentSegmentId?: string;
refs?: Ref[];
key?: string;
}
export type Ref = {
type?: string;
type: string;
parentSegmentId: string;
parentSpanId: number;
traceId: string;
@ -61,6 +60,13 @@ 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;

View File

@ -15,9 +15,11 @@
* limitations under the License.
*/
export enum TraceGraphType {
TREE = "Tree",
LIST = "List",
TABLE = "Table",
STATISTICS = "Statistics",
}
import axios from "axios";
const CancelToken = axios.CancelToken;
export const cancelToken = (): any =>
new CancelToken(function executor(c) {
const w = window as any;
(w.axiosCancel || []).push(c);
});

View File

@ -16,22 +16,18 @@
*/
import { ElNotification } from "element-plus";
export default (text: string): void => {
navigator.clipboard
.writeText(text)
.then(() => {
ElNotification({
title: "Success",
message: "Copied",
type: "success",
});
})
.catch((err) => {
ElNotification({
title: "Error",
message: err,
type: "warning",
});
});
export default (value: string): void => {
const input = document.createElement("input");
input.value = value;
document.body.appendChild(input);
input.select();
if (document.execCommand("Copy")) {
document.execCommand("Copy");
}
input.remove();
ElNotification({
title: "Success",
message: "Copied",
type: "success",
});
};

View File

@ -37,10 +37,8 @@ export const saveFile = (data: any, name: string) => {
tagA.download = name;
tagA.style.display = "none";
const blob = new Blob([newData]);
const url = URL.createObjectURL(blob);
tagA.href = url;
tagA.href = URL.createObjectURL(blob);
document.body.appendChild(tagA);
tagA.click();
document.body.removeChild(tagA);
URL.revokeObjectURL(url);
};

View File

@ -17,7 +17,7 @@
class Vec2 extends Float32Array {
constructor(v?: unknown, y?: unknown) {
super(2);
if (v instanceof Vec2 || v instanceof Float32Array || (v instanceof Array && v.length === 2)) {
if (v instanceof Vec2 || v instanceof Float32Array || (v instanceof Array && v.length == 2)) {
this[0] = v[0];
this[1] = v[1];
} else if (typeof v === "number" && typeof y === "number") {
@ -104,7 +104,7 @@ class Vec2 extends Float32Array {
}
norm(out?: number[] | Vec2): number[] | Vec2 | undefined {
const mag = Math.sqrt(this[0] * this[0] + this[1] * this[1]);
if (mag === 0) return this;
if (mag == 0) return this;
out = out || this;
out[0] = this[0] / mag;
out[1] = this[1] / mag;

View File

@ -17,7 +17,7 @@
class Vec3 extends Float32Array {
constructor(v?: unknown, y?: unknown, z?: unknown) {
super(3);
if (v instanceof Vec3 || v instanceof Float32Array || (v instanceof Array && v.length === 3)) {
if (v instanceof Vec3 || v instanceof Float32Array || (v instanceof Array && v.length == 3)) {
this[0] = v[0];
this[1] = v[1];
this[2] = v[2];
@ -150,7 +150,7 @@ class Vec3 extends Float32Array {
}
static norm(x: unknown, y: unknown, z: unknown): Vec3 {
const rtn = new Vec3();
if (x instanceof Vec3 || x instanceof Float32Array || (x instanceof Array && x.length === 3)) {
if (x instanceof Vec3 || x instanceof Float32Array || (x instanceof Array && x.length == 3)) {
rtn.copy(x);
} else if (typeof x === "number" && typeof y === "number" && typeof z === "number") {
rtn.xyz(x, y, z);

View File

@ -22,7 +22,7 @@ limitations under the License. -->
placeholder="Select a language"
@change="setLang"
size="small"
style="font-size: 14px; width: 180px"
style="font-size: 14px"
/>
</div>
<div class="flex-h item">

View File

@ -22,17 +22,15 @@ limitations under the License. -->
<div class="message mb-5 b">
{{ i.message }}
</div>
<div class="flex-h">
<div
class="timeline-table-i-scope"
:class="{
blue: i.scope === 'Service',
green: i.scope === 'Endpoint',
yellow: i.scope === 'ServiceInstance',
}"
>
{{ t(i.scope.toLowerCase()) }}
</div>
<div
class="timeline-table-i-scope mr-10 l sm"
:class="{
blue: i.scope === 'Service',
green: i.scope === 'Endpoint',
yellow: i.scope === 'ServiceInstance',
}"
>
{{ t(i.scope.toLowerCase()) }}
</div>
<div class="grey sm show-xs">
{{ dateFormat(parseInt(i.startTime)) }}
@ -48,7 +46,7 @@ limitations under the License. -->
:destroy-on-close="true"
@closed="isShowDetails = false"
>
<div class="mb-20 clear alarm-detail" v-for="(item, index) in AlarmDetailCol" :key="index">
<div class="mb-10 clear alarm-detail" v-for="(item, index) in AlarmDetailCol" :key="index">
<span class="g-sm-2 grey">{{ t(item.value) }}:</span>
<span v-if="item.label === 'startTime'">
{{ dateFormat(currentDetail[item.label]) }}
@ -56,7 +54,7 @@ limitations under the License. -->
<span v-else-if="item.label === 'tags'">
<div v-for="(d, index) in alarmTags" :key="index">{{ d }}</div>
</span>
<span v-else-if="item.label === 'events'">
<span v-else-if="item.label === 'events'" class="event-detail">
<div>
<ul>
<li>
@ -77,12 +75,6 @@ limitations under the License. -->
</ul>
</div>
</span>
<span v-else-if="item.label === 'expression'">
{{ currentDetail.snapshot.expression }}
</span>
<span v-else-if="item.label === 'snapshot'">
<Snapshot :snapshot="currentDetail.snapshot" />
</span>
<span v-else>{{ currentDetail[item.label] }}</span>
</div>
</el-dialog>
@ -93,7 +85,7 @@ limitations under the License. -->
:destroy-on-close="true"
@closed="showEventDetails = false"
>
<div>
<div class="event-detail">
<div class="mb-10" v-for="(eventKey, index) in EventsDetailKeys" :key="index">
<span class="keys">{{ t(eventKey.text) }}</span>
<span v-if="eventKey.class === 'parameters'">
@ -125,7 +117,6 @@ limitations under the License. -->
import { useAlarmStore } from "@/store/modules/alarm";
import { EventsDetailHeaders, AlarmDetailCol, EventsDetailKeys } from "./data";
import { dateFormat } from "@/utils/dateFormat";
import Snapshot from "./components/Snapshot.vue";
const { t } = useI18n();
const alarmStore = useAlarmStore();
@ -195,10 +186,11 @@ limitations under the License. -->
}
.timeline-table-i-scope {
padding: 0 5px;
border: 1px solid;
border-radius: 3px;
display: inline-block;
padding: 0 8px;
border: 1px solid;
margin-top: -1px;
border-radius: 4px;
}
.timeline-item {
@ -232,6 +224,9 @@ limitations under the License. -->
}
.alarm-detail {
max-height: 600px;
overflow: auto;
ul {
min-height: 100px;
overflow: auto;
@ -252,9 +247,4 @@ limitations under the License. -->
}
}
}
.mini-chart {
height: 20px;
width: 400px;
}
</style>

View File

@ -38,7 +38,7 @@ limitations under the License. -->
:total="total"
@current-change="changePage"
:pager-count="5"
size="small"
small
:style="
appStore.theme === Themes.Light
? `--el-pagination-bg-color: #f0f2f5; --el-pagination-button-disabled-bg-color: #f0f2f5;`

View File

@ -1,141 +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>
<Graph :option="option" @select="clickEvent" />
</template>
<script lang="ts" setup>
import { computed } from "vue";
import type { PropType } from "vue";
import type { EventParams } from "@/types/dashboard";
import useLegendProcess from "@/hooks/useLegendProcessor";
import { useAppStoreWithOut } from "@/store/modules/app";
import { Themes } from "@/constants/data";
/*global defineProps, defineEmits */
const emits = defineEmits(["click"]);
const props = defineProps({
data: {
type: Array as PropType<any>,
default: () => [],
},
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
});
const appStore = useAppStoreWithOut();
const option = computed(() => getOption());
function getOption() {
const { chartColors } = useLegendProcess();
const color: string[] = chartColors();
const series = [];
const grid = [];
const xAxis = [];
const yAxis = [];
for (const [index, metric] of props.data.entries()) {
grid.push({
top: 300 * index + 30,
left: 0,
right: 10,
bottom: 5,
containLabel: true,
height: 260,
});
xAxis.push({
type: "category",
show: true,
axisTick: {
lineStyle: { color: "#c1c5ca41" },
alignWithLabel: true,
},
splitLine: { show: false },
axisLine: { lineStyle: { color: "rgba(0,0,0,0)" } },
axisLabel: { color: "#9da5b2", fontSize: "11" },
gridIndex: index,
});
yAxis.push({
type: "value",
axisLine: { show: false },
axisTick: { show: false },
splitLine: { lineStyle: { color: "#c1c5ca41", type: "dashed" } },
axisLabel: {
color: "#9da5b2",
fontSize: "11",
show: true,
},
gridIndex: index,
});
for (const item of metric.values) {
series.push({
data: item.values.map((item: number, itemIndex: number) => [props.intervalTime[itemIndex], item]),
name: item.name || metric.name,
type: "line",
symbol: "circle",
symbolSize: 4,
xAxisIndex: index,
yAxisIndex: index,
lineStyle: {
width: 2,
type: "solid",
},
});
}
}
const tooltip = {
trigger: "axis",
backgroundColor: appStore.theme === Themes.Dark ? "#333" : "#fff",
borderColor: appStore.theme === Themes.Dark ? "#333" : "#fff",
textStyle: {
fontSize: 12,
color: appStore.theme === Themes.Dark ? "#eee" : "#333",
},
enterable: true,
confine: true,
extraCssText: "max-height:85%; overflow: auto;",
axisPointer: {
animation: false,
},
};
return {
color,
tooltip,
axisPointer: {
link: { xAxisIndex: "all" },
},
legend: {
type: "scroll",
icon: "circle",
top: -5,
left: 0,
itemWidth: 12,
textStyle: {
color: appStore.theme === Themes.Dark ? "#fff" : "#333",
},
},
grid,
xAxis,
yAxis,
series,
};
}
function clickEvent(params: EventParams) {
emits("click", params);
}
</script>
<style lang="scss" scoped>
.snapshot-charts {
width: 100%;
height: 100%;
}
</style>

View File

@ -1,35 +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>
<LineChart
:intervalTime="appStore.intervalTime"
:data="metrics"
:style="{ width: `800px`, height: `${metrics.length * 300}px` }"
/>
</template>
<script lang="ts" setup>
import { computed } from "vue";
import { useSnapshot } from "@/hooks/useSnapshot";
import { useAppStoreWithOut } from "@/store/modules/app";
import LineChart from "./Line.vue";
/*global defineProps */
const props = defineProps({
snapshot: { type: Object, default: () => {} },
});
const { processResults } = useSnapshot(props.snapshot.metrics);
const metrics = computed(() => processResults());
const appStore = useAppStoreWithOut();
</script>

View File

@ -52,14 +52,6 @@ export const AlarmDetailCol = [
label: "events",
value: "eventDetail",
},
{
label: "expression",
value: "expression",
},
{
label: "snapshot",
value: "snapshot",
},
];
export const EventsDetailKeys = [

View File

@ -612,7 +612,7 @@ limitations under the License. -->
}
function searchDashboards(pageIndex: number) {
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
const arr = list.filter((d: { name: string }) => d.name.toLowerCase().includes(searchText.value.toLowerCase()));
const arr = list.filter((d: { name: string }) => d.name.includes(searchText.value));
total.value = arr.length;
dashboards.value = arr.filter(

View File

@ -32,13 +32,11 @@ limitations under the License. -->
:config="{
i: 0,
...graph,
valueMappings: graph?.valueMappings,
metricConfig: config.metricConfig,
expressions: config.expressions || [],
typesOfMQE: typesOfMQE || [],
subExpressions: config.subExpressions || [],
subTypesOfMQE: config.subTypesOfMQE || [],
valueRelatedDashboard: config.valueRelatedDashboard,
}"
:needQuery="true"
/>

View File

@ -126,7 +126,7 @@ limitations under the License. -->
opt.auto = Number(f.value) * 60 * 60 * 1000;
}
if (f.step === TimeType.DAY_TIME) {
opt.auto = Number(f.value) * 24 * 60 * 60 * 1000;
opt.auto = Number(f.value) * 60 * 60 * 60 * 1000;
}
}
const config = JSON.stringify(opt);

View File

@ -32,8 +32,7 @@ limitations under the License. -->
:data="states.source"
:config="{
...graph,
decorations: dashboardStore.selectedGrid.graph?.decorations,
legend: dashboardStore.selectedGrid.graph?.legend,
legend: (dashboardStore.selectedGrid.graph || {}).legend,
i: dashboardStore.selectedGrid.i,
metricConfig: dashboardStore.selectedGrid.metricConfig,
relatedTrace: dashboardStore.selectedGrid.relatedTrace,
@ -41,7 +40,6 @@ limitations under the License. -->
typesOfMQE: dashboardStore.selectedGrid.typesOfMQE || [],
subExpressions: dashboardStore.selectedGrid.subExpressions || [],
subTypesOfMQE: dashboardStore.selectedGrid.subTypesOfMQE || [],
valueRelatedDashboard: dashboardStore.selectedGrid.valueRelatedDashboard,
}"
:needQuery="true"
@expressionTips="getErrors"

View File

@ -81,7 +81,7 @@ limitations under the License. -->
const latency = ref<boolean>(traceOpt.latency || false);
const enableRelate = ref<boolean>(traceOpt.enableRelate || false);
const type = ref<string>((graph && graph.type) || "");
const refIdType = ref<string>(traceOpt.refIdType || "");
const refIdType = ref<string>(traceOpt.refIdType || "traceId");
function updateConfig(param: { [key: string]: unknown }) {
const relatedTrace = {

View File

@ -13,9 +13,6 @@ 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>
<value-mappings />
</div>
<div>
<span class="label">{{ t("fontSize") }}</span>
<el-slider
@ -29,7 +26,7 @@ limitations under the License. -->
@change="updateConfig({ fontSize })"
/>
</div>
<div>
<div class="item">
<span class="label">{{ t("showUnit") }}</span>
<el-switch v-model="showUnit" active-text="Yes" inactive-text="No" @change="updateConfig({ showUnit })" />
</div>
@ -38,7 +35,6 @@ limitations under the License. -->
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { useDashboardStore } from "@/store/modules/dashboard";
import ValueMappings from "./components/ValueMappings.vue";
const { t } = useI18n();
const dashboardStore = useDashboardStore();

View File

@ -14,9 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div>
<value-mappings />
</div>
<div class="item">
<span class="label">{{ t("showValues") }}</span>
<el-switch
v-model="showTableValues"
@ -40,7 +37,6 @@ limitations under the License. -->
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { useDashboardStore } from "@/store/modules/dashboard";
import ValueMappings from "./components/ValueMappings.vue";
const { t } = useI18n();
const dashboardStore = useDashboardStore();

View File

@ -22,15 +22,6 @@ limitations under the License. -->
@change="updateLegendConfig({ show: legend.show })"
/>
</div>
<div>
<span class="label mr-5">{{ t("asSelector") }}</span>
<el-switch
v-model="legend.asSelector"
active-text="Yes"
inactive-text="No"
@change="updateLegendConfig({ asSelector: legend.asSelector })"
/>
</div>
<div>
<span class="label">{{ t("asTable") }}</span>
<el-switch
@ -106,7 +97,6 @@ limitations under the License. -->
max: false,
mean: false,
asTable: false,
asSelector: false,
toTheRight: false,
width: 130,
...graph.value.legend,

View File

@ -1,112 +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>
<span class="label">{{ t("valueMappings") }}</span>
<span class="label red">{{ t("mappingTip") }}</span>
</div>
<div v-for="(key, index) in keys" :key="index" class="mb-10 flex-h">
<div class="content-decoration" contenteditable="true" @blur="changeKeys($event, index)">
{{ key }}
</div>
<div class="ml-5 mr-10">:</div>
<div class="content-decoration" contenteditable="true" @blur="changeValues($event, key)">
{{ valueMappings[key] }}
</div>
<div v-if="index === keys.length - 1">
<Icon class="cp mr-5" iconName="add_circle_outlinecontrol_point" size="middle" @click="addDecoration" />
<Icon
v-if="index !== 0"
class="cp"
iconName="remove_circle_outline"
size="middle"
@click="deleteDecoration(index)"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const dashboardStore = useDashboardStore();
const graph = dashboardStore.selectedGrid.graph;
const valueMappings = ref<{ [key: string]: string }>(graph?.valueMappings || {});
const keys = ref<string[]>(graph.valueMappings ? Object.keys(valueMappings.value) : [""]);
function changeKeys(event: any, index: number) {
const params = event.target.textContent || "";
const list = Object.keys(valueMappings.value);
if (params) {
valueMappings.value[params] = valueMappings.value[list[index]];
}
delete valueMappings.value[list[index]];
keys.value = Object.keys(valueMappings.value);
if (!keys.value.length) {
keys.value = [""];
}
updateConfig();
}
function changeValues(event: any, key: string) {
valueMappings.value[key] = event.target.textContent || "";
updateConfig();
}
function addDecoration() {
keys.value.push("");
}
function deleteDecoration(index: number) {
if (!keys.value.length) {
return;
}
delete valueMappings.value[keys.value[index]];
keys.value.splice(index, 1);
updateConfig();
}
function updateConfig() {
const graph = {
...dashboardStore.selectedGrid.graph,
valueMappings: valueMappings.value,
};
dashboardStore.selectWidget({ ...dashboardStore.selectedGrid, graph });
}
</script>
<style lang="scss" scoped>
.content-decoration {
width: 350px;
border: 1px solid $border-color;
cursor: text;
padding: 0 5px;
border-radius: 3px;
outline: none;
margin-right: 5px;
min-height: 26px;
&:focus {
border-color: $active-color;
}
}
.label {
font-size: 13px;
font-weight: 500;
padding-right: 10px;
}
</style>

View File

@ -95,20 +95,6 @@ limitations under the License. -->
{{ type.label }}
</span>
</div>
<div v-if="states.isTopList" class="mt-10">
<div>{{ t("valueDashboard") }}</div>
<div>
<Selector
:value="states.valueRelatedDashboard || ''"
:options="states.dashboardList"
size="small"
placeholder="Please select a dashboard name"
@change="changeValueDashboard"
class="selectors"
:clearable="true"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, computed } from "vue";
@ -154,25 +140,21 @@ limitations under the License. -->
metricTypes: string[];
metricTypeList: Option[][];
isList: boolean;
isTopList: boolean;
dashboardName: string;
dashboardList: ((DashboardItem & { label: string; value: string }) | any)[];
tips: string[];
subTips: string[];
valueRelatedDashboard: string;
}>({
metrics: metrics.value.length ? metrics.value : [""],
metricTypes: typesOfMQE.value.length ? typesOfMQE.value : [""],
metricTypeList: [],
isList: false,
isTopList: false,
dashboardName: graph.value.dashboardName,
dashboardList: [{ label: "", value: "" }],
tips: [],
subTips: [],
subMetrics: subMetrics.value.length ? subMetrics.value : [""],
subMetricTypes: subMetricTypes.value.length ? subMetricTypes.value : [""],
valueRelatedDashboard: dashboardStore.selectedGrid.valueRelatedDashboard,
});
const currentMetricConfig = ref<MetricConfigOpt>({
unit: "",
@ -181,7 +163,6 @@ limitations under the License. -->
sortOrder: "DES",
});
states.isTopList = graph.value.type === ChartTypes[4].value;
states.isList = ListChartTypes.includes(graph.value.type);
const defaultLen = ref<number>(states.isList ? 5 : 20);
@ -206,10 +187,9 @@ limitations under the License. -->
const arr = list.reduce((prev: (DashboardItem & { label: string; value: string })[], d: DashboardItem) => {
if (d.layer === dashboardStore.layerId) {
if (
(d.entity === EntityType[0].value && chart === ChartTypes[8].value) ||
(d.entity === EntityType[2].value && chart === ChartTypes[9].value) ||
(d.entity === EntityType[3].value && chart === ChartTypes[10].value) ||
states.isTopList
(d.entity === EntityType[0].value && chart === "ServiceList") ||
(d.entity === EntityType[2].value && chart === "EndpointList") ||
(d.entity === EntityType[3].value && chart === "InstanceList")
) {
prev.push({
...d,
@ -274,28 +254,9 @@ limitations under the License. -->
typesOfMQE: states.metricTypes,
});
emit("update", params.source || {});
if (states.isTopList) {
const values: any = Object.values(params.source)[0];
if (!values) {
return;
}
states.dashboardList = states.dashboardList.filter((d) => d.entity === values[0].owner.scope);
}
}
function changeValueDashboard(opt: { value: string }[]) {
if (!opt[0]) {
states.valueRelatedDashboard = "";
} else {
states.valueRelatedDashboard = opt[0].value;
}
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
valueRelatedDashboard: states.valueRelatedDashboard,
});
}
function changeDashboard(opt: { value: string }[]) {
function changeDashboard(opt: any) {
if (!opt[0]) {
states.dashboardName = "";
} else {

View File

@ -1,85 +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="profile-wrapper flex-v">
<el-popover placement="bottom" trigger="click" :width="100" v-if="dashboardStore.editMode">
<template #reference>
<span class="operation cp">
<Icon iconName="ellipsis_v" size="middle" />
</span>
</template>
<div class="tools" @click="removeWidget">
<span>{{ t("delete") }}</span>
</div>
</el-popover>
<Header />
<Content :config="props.data" />
</div>
</template>
<script lang="ts" setup>
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
import { useDashboardStore } from "@/store/modules/dashboard";
import Content from "../related/async-profiling/Content.vue";
import Header from "../related/async-profiling/Header.vue";
/*global defineProps*/
const props = defineProps({
data: {
type: Object as PropType<any>,
default: () => ({ graph: {} }),
},
activeIndex: { type: String, default: "" },
});
const { t } = useI18n();
const dashboardStore = useDashboardStore();
function removeWidget() {
dashboardStore.removeControls(props.data);
}
</script>
<style lang="scss" scoped>
.profile-wrapper {
width: 100%;
height: 100%;
font-size: $font-size-smaller;
position: relative;
}
.operation {
position: absolute;
top: 8px;
right: 3px;
}
.header {
padding: 10px;
font-size: $font-size-smaller;
border-bottom: 1px solid $border-color;
}
.tools {
padding: 5px 0;
color: #999;
cursor: pointer;
position: relative;
text-align: center;
&:hover {
color: $active-color;
background-color: $popper-hover-bg-color;
}
}
</style>

View File

@ -184,7 +184,7 @@ limitations under the License. -->
position: relative;
width: 2px;
height: 100%;
background-color: var(--sw-trace-line);
background-color: #e8e8e8;
cursor: ew-resize;
&:hover {

View File

@ -61,7 +61,6 @@ limitations under the License. -->
typesOfMQE: typesOfMQE || [],
subExpressions: data.subExpressions || [],
subTypesOfMQE: data.subTypesOfMQE || [],
valueRelatedDashboard: data.valueRelatedDashboard,
}"
:needQuery="needQuery"
@click="clickHandle"

View File

@ -26,7 +26,6 @@ import DemandLog from "./DemandLog.vue";
import Event from "./Event.vue";
import NetworkProfiling from "./NetworkProfiling.vue";
import ContinuousProfiling from "./ContinuousProfiling.vue";
import AsyncProfiling from "./AsyncProfiling.vue";
import TimeRange from "./TimeRange.vue";
import ThirdPartyApp from "./ThirdPartyApp.vue";
import TaskTimeline from "./TaskTimeline.vue";
@ -44,7 +43,6 @@ export default {
Event,
NetworkProfiling,
ContinuousProfiling,
AsyncProfiling,
TimeRange,
ThirdPartyApp,
TaskTimeline,

View File

@ -25,7 +25,6 @@ import DemandLog from "./DemandLog.vue";
import Event from "./Event.vue";
import NetworkProfiling from "./NetworkProfiling.vue";
import ContinuousProfiling from "./ContinuousProfiling.vue";
import AsyncProfiling from "./AsyncProfiling.vue";
import TimeRange from "./TimeRange.vue";
import ThirdPartyApp from "./ThirdPartyApp.vue";
import TaskTimeline from "./TaskTimeline.vue";
@ -44,6 +43,5 @@ export default {
TimeRange,
ThirdPartyApp,
ContinuousProfiling,
AsyncProfiling,
TaskTimeline,
};

View File

@ -150,7 +150,6 @@ export enum WidgetType {
Event = "Event",
NetworkProfiling = "NetworkProfiling",
ContinuousProfiling = "ContinuousProfiling",
AsyncProfiling = "AsyncProfiling",
ThirdPartyApp = "ThirdPartyApp",
TaskTimeline = "TaskTimeline",
}
@ -172,7 +171,6 @@ export const ServiceTools = [
{ name: "timeline", content: "Add Trace Profiling", id: WidgetType.Profile },
{ name: "insert_chart", content: "Add eBPF Profiling", id: WidgetType.Ebpf },
{ name: "continuous_profiling", content: "Add Continuous Profiling", id: WidgetType.ContinuousProfiling },
{ name: "async_profiling", content: "Add Async Profiling", id: WidgetType.AsyncProfiling },
{ name: "assignment", content: "Add Log", id: WidgetType.Log },
{ name: "demand", content: "Add On Demand Log", id: WidgetType.DemandLog },
{ name: "event", content: "Add Event", id: WidgetType.Event },
@ -268,9 +266,8 @@ export const TextColors: { [key: string]: string } = {
};
export const RefIdTypes = [
{ label: "None", value: "none" },
{ label: "Trace ID", value: "traceId" },
{ label: "Owner", value: "owner" },
{ label: "None", value: "none" },
];
export const RefreshOptions = [
{ label: "Last 30 minutes", value: "30", step: "MINUTE" },

View File

@ -75,34 +75,6 @@ limitations under the License. -->
};
});
const color: string[] = chartColors();
const legend =
appStore.theme === Themes.Dark
? {
pageIconColor: "#ccc",
pageIconInactiveColor: "#444",
backgroundColor: "#333",
borderColor: "#fff",
textStyle: {
fontSize: 12,
color: "#eee",
},
pageTextStyle: {
color: "#eee",
},
}
: {
pageIconColor: "#666",
pageIconInactiveColor: "#ccc",
backgroundColor: appStore.theme === Themes.Dark ? "#333" : "#fff",
borderColor: appStore.theme === Themes.Dark ? "#333" : "#fff",
textStyle: {
fontSize: 12,
color: "#333",
},
pageTextStyle: {
color: "#333",
},
};
return {
color,
tooltip: {
@ -113,7 +85,7 @@ limitations under the License. -->
},
enterable: true,
confine: true,
extraCssText: "max-width: 100%; max-height: 75%; white-space: normal; overflow: auto;",
extraCssText: "max-height:85%; overflow: auto;",
},
legend: {
type: "scroll",
@ -122,10 +94,15 @@ limitations under the License. -->
top: 0,
left: 0,
itemWidth: 12,
...legend,
backgroundColor: appStore.theme === Themes.Dark ? "#333" : "#fff",
borderColor: appStore.theme === Themes.Dark ? "#333" : "#fff",
textStyle: {
fontSize: 12,
color: appStore.theme === Themes.Dark ? "#eee" : "#333",
},
},
grid: {
top: showEchartsLegend(keys) ? 35 : 10,
top: keys.length === 1 ? 15 : 40,
left: 0,
right: 10,
bottom: 5,

View File

@ -22,7 +22,7 @@ limitations under the License. -->
justifyContent: config.textAlign || 'center',
}"
>
{{ getValue() }}
{{ singleVal }}
<span class="unit" v-show="config.showUnit && unit">
{{ decodeURIComponent(unit) }}
</span>
@ -34,7 +34,6 @@ limitations under the License. -->
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
import type { CardConfig, MetricConfigOpt } from "@/types/dashboard";
import { ElMessage } from "element-plus";
/*global defineProps */
const props = defineProps({
@ -49,36 +48,16 @@ limitations under the License. -->
showUnit: true,
textAlign: "center",
metricConfig: [],
valueMappings: {},
}),
},
});
const { t } = useI18n();
const metricConfig = computed(() => props.config.metricConfig || []);
const valueMappings = computed(() => props.config.valueMappings || {});
const key = computed(() => Object.keys(props.data)[0]);
const singleVal = computed(() =>
Array.isArray(props.data[key.value]) ? props.data[key.value][0] : props.data[key.value],
);
const unit = computed(() => metricConfig.value[0] && encodeURIComponent(metricConfig.value[0].unit || ""));
function getValue() {
if (valueMappings.value[singleVal.value]) {
return valueMappings.value[singleVal.value];
}
const list = Object.keys(valueMappings.value);
for (const i of list) {
try {
if (new RegExp(i).test(String(singleVal.value))) {
return valueMappings.value[i] || singleVal.value;
}
} catch (e) {
ElMessage.error("invalid regex string");
return singleVal.value;
}
}
return singleVal.value;
}
</script>
<style lang="scss" scoped>
.chart-card {

View File

@ -16,18 +16,16 @@ limitations under the License. -->
<div class="table">
<div class="search">
<el-input v-model="searchText" placeholder="Search for more endpoints" @change="searchList" class="inputs">
<template #prepend>
<Selector style="width: 120px" v-model="topN" :options="topNList" placeholder="Select" />
</template>
<template #append>
<el-button @click="searchList">
<Icon size="middle" iconName="search" />
</el-button>
</template>
</el-input>
<span class="ml-5 tips">{{ t("endpointTips") }}</span>
</div>
<div class="list">
<el-table v-loading="chartLoading" :data="currentEndpoints" style="width: 100%">
<el-table v-loading="chartLoading" :data="endpoints" style="width: 100%">
<el-table-column label="Endpoints" fixed min-width="220">
<template #default="scope">
<span class="link" @click="clickEndpoint(scope)" :style="{ fontSize: `${config.fontSize}px` }">
@ -48,22 +46,13 @@ limitations under the License. -->
/>
</el-table>
</div>
<el-pagination
class="pagination flex-h"
layout="prev, pager, next"
:current-page="currentPage"
:page-size="pageSize"
:total="endpoints.length"
@current-change="handleCurrentChange"
@prev-click="changePage"
@next-click="changePage"
/>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import { useSelectorStore } from "@/store/modules/selectors";
import { ElMessage } from "element-plus";
import { useI18n } from "vue-i18n";
import type { PropType } from "vue";
import type { EndpointListConfig } from "@/types/dashboard";
import type { Endpoint } from "@/types/selector";
@ -101,26 +90,16 @@ limitations under the License. -->
});
const emit = defineEmits(["expressionTips"]);
const { t } = useI18n();
const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore();
const chartLoading = ref<boolean>(false);
const endpoints = ref<Endpoint[]>([]); // all of endpoints
const currentEndpoints = ref<Endpoint[]>([]); // current page of endpoints
const endpoints = ref<Endpoint[]>([]);
const searchText = ref<string>("");
const colMetrics = ref<string[]>([]);
const colSubMetrics = ref<string[]>([]);
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
const typesOfMQE = ref<string[]>(props.config.typesOfMQE || []);
const topN = ref<number>(20);
const currentPage = ref<number>(1);
const pageSize = 10;
const topNList = [
{ label: "TopN20", value: 20 },
{ label: "TopN50", value: 50 },
{ label: "TopN100", value: 100 },
{ label: "TopN150", value: 150 },
{ label: "TopN200", value: 200 },
];
if (props.needQuery) {
queryEndpoints();
@ -129,7 +108,6 @@ limitations under the License. -->
chartLoading.value = true;
const resp = await selectorStore.getEndpoints({
keyword: searchText.value,
limit: topN.value,
});
chartLoading.value = false;
@ -138,8 +116,7 @@ limitations under the License. -->
return;
}
endpoints.value = resp.data.pods || [];
currentEndpoints.value = endpoints.value.filter((d: unknown, index: number) => index < pageSize);
queryEndpointMetrics(currentEndpoints.value);
queryEndpointMetrics(endpoints.value);
}
async function queryEndpointMetrics(arr: Endpoint[]) {
if (!arr.length) {
@ -165,7 +142,7 @@ limitations under the License. -->
{ metricConfig: metricConfig.value || [], expressions, subExpressions },
EntityType[2].value,
);
currentEndpoints.value = params.data;
endpoints.value = params.data;
colMetrics.value = params.names;
colSubMetrics.value = params.subNames;
metricConfig.value = params.metricConfigArr;
@ -174,7 +151,7 @@ limitations under the License. -->
return;
}
currentEndpoints.value = currentPods;
endpoints.value = currentPods;
colMetrics.value = [];
colSubMetrics.value = [];
metricConfig.value = [];
@ -198,16 +175,6 @@ limitations under the License. -->
async function searchList() {
await queryEndpoints();
}
function changePage() {
currentEndpoints.value = endpoints.value.filter(
(_, index: number) => index >= (currentPage.value - 1) * pageSize && index < currentPage.value * pageSize,
);
queryEndpointMetrics(currentEndpoints.value);
}
function handleCurrentChange(val: number) {
currentPage.value = val;
changePage();
}
watch(
() => [
...(props.config.metricConfig || []),

View File

@ -16,11 +16,10 @@ limitations under the License. -->
<div class="graph flex-v" :class="setRight ? 'flex-h' : 'flex-v'">
<Graph
:option="option"
@select="clickEvent"
:filters="config.filters"
:relatedTrace="config.relatedTrace"
:associate="config.associate || []"
:legendSelector="{ isSelector: legendSelector, sConfigPage: dashboardStore.showConfig }"
@select="clickEvent"
/>
<Legend :config="config.legend" :data="data" :intervalTime="intervalTime" />
</div>
@ -33,7 +32,6 @@ limitations under the License. -->
import useLegendProcess from "@/hooks/useLegendProcessor";
import { isDef } from "@/utils/is";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useDashboardStore } from "@/store/modules/dashboard";
import { Themes } from "@/constants/data";
/*global defineProps, defineEmits */
@ -62,24 +60,19 @@ limitations under the License. -->
showYAxis: true,
smallTips: false,
showlabels: true,
noTooltips: false,
}),
},
});
const appStore = useAppStoreWithOut();
const dashboardStore = useDashboardStore();
const setRight = ref<boolean>(false);
const legendSelector = computed(() => props.config.legend?.asSelector);
const option = computed(() => getOption());
function getOption() {
const { showEchartsLegend, isRight, chartColors } = useLegendProcess(props.config.legend);
setRight.value = isRight;
const keys = Object.keys(props.data || {}).filter(
(i: string) => Array.isArray(props.data[i]) && props.data[i].length,
);
const temp = keys.map((i: string) => {
const keys = Object.keys(props.data || {}).filter((i: any) => Array.isArray(props.data[i]) && props.data[i].length);
const temp = keys.map((i: any) => {
const serie: any = {
data: props.data[i].map((item: number, itemIndex: number) => [props.intervalTime[itemIndex], item]),
data: props.data[i].map((item: any, itemIndex: number) => [props.intervalTime[itemIndex], item]),
name: i,
type: "line",
symbol: "circle",
@ -102,7 +95,6 @@ limitations under the License. -->
const color: string[] = chartColors();
const tooltip = {
trigger: "axis",
show: !props.config.noTooltips,
backgroundColor: appStore.theme === Themes.Dark ? "#333" : "#fff",
borderColor: appStore.theme === Themes.Dark ? "#333" : "#fff",
textStyle: {
@ -111,13 +103,13 @@ limitations under the License. -->
},
enterable: true,
confine: true,
extraCssText: "max-width: 100%; max-height: 75%; white-space: normal; overflow: auto;",
extraCssText: "max-height:85%; overflow: auto;",
};
const tips = {
show: !props.config.noTooltips,
formatter(params: any) {
return `${params[0].value[1]}`;
},
confine: true,
extraCssText: `height: 20px; padding:0 2px;`,
trigger: "axis",
backgroundColor: appStore.theme === Themes.Dark ? "#666" : "#eee",
@ -127,30 +119,7 @@ limitations under the License. -->
color: appStore.theme === Themes.Dark ? "#eee" : "#333",
},
};
const legend =
appStore.theme === Themes.Dark
? {
pageIconColor: "#ccc",
pageIconInactiveColor: "#444",
textStyle: {
fontSize: 12,
color: "#eee",
},
pageTextStyle: {
color: "#eee",
},
}
: {
pageIconColor: "#666",
pageIconInactiveColor: "#ccc",
textStyle: {
fontSize: 12,
color: "#333",
},
pageTextStyle: {
color: "#333",
},
};
return {
color,
tooltip: props.config.smallTips ? tips : tooltip,
@ -161,15 +130,16 @@ limitations under the License. -->
top: 0,
left: 0,
itemWidth: 12,
data: keys.map((d: string) => ({ name: d })),
...legend,
textStyle: {
color: appStore.theme === Themes.Dark ? "#fff" : "#333",
},
},
grid: {
top: showEchartsLegend(keys) ? 35 : 10,
left: 0,
right: 10,
bottom: 5,
containLabel: isDef(props.config.showlabels) ? props.config.showlabels : true,
containLabel: props.config.showlabels === undefined ? true : props.config.showlabels,
},
xAxis: {
type: "category",

View File

@ -37,7 +37,7 @@ limitations under the License. -->
>{{ k.split("=")[1] }}</div
>
<div class="value-col" v-if="config.showTableValues">
{{ getColValue(keys) }}
{{ data[(keys as string[]).join(",")][data[(keys as string[]).join(",")].length - 1 || 0] }}
</div>
</div>
</div>
@ -47,8 +47,6 @@ limitations under the License. -->
import { computed } from "vue";
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
import { ElMessage } from "element-plus";
/*global defineProps */
const props = defineProps({
data: {
@ -60,14 +58,12 @@ limitations under the License. -->
showTableValues: boolean;
tableHeaderCol2: string;
typesOfMQE: string[];
valueMappings: {};
}>,
default: () => ({ showTableValues: true }),
},
});
const { t } = useI18n();
const valueMappings = computed<{ [key: string]: string }>(() => props.config.valueMappings || {});
const nameWidth = computed(() => (props.config.showTableValues ? 80 : 100));
const dataKeys = computed(() => {
const keys = Object.keys(props.data || {}).filter(
@ -77,25 +73,6 @@ limitations under the License. -->
return list;
});
function getColValue(keys: string[]) {
const source = props.data[(keys as string[]).join(",")][props.data[(keys as string[]).join(",")].length - 1 || 0];
if (valueMappings.value[source]) {
return valueMappings.value[source];
}
const list = Object.keys(valueMappings.value);
for (const i of list) {
try {
if (new RegExp(i).test(String(source))) {
return valueMappings.value[i] || source;
}
} catch (e) {
ElMessage.error("invalid regex string");
return source;
}
}
return source;
}
</script>
<style lang="scss" scoped>
.chart-table {

View File

@ -32,12 +32,9 @@ limitations under the License. -->
<div class="operation" @click="handleClick(i.name)">
<span>{{ t("copy") }}</span>
</div>
<div class="operation" @click="viewTrace(i)" v-show="![RefIdTypes[0].value].includes(refIdType)">
<div class="operation" @click="viewTrace(i)" v-show="refIdType === RefIdTypes[0].value">
<span>{{ t("viewTrace") }}</span>
</div>
<div class="operation" @click="viewDashboard(i)" v-show="props.config.valueRelatedDashboard">
<span>{{ t("viewValueDashboard") }}</span>
</div>
</el-popover>
</div>
<el-progress
@ -64,18 +61,16 @@ limitations under the License. -->
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
import { computed, ref } from "vue";
import router from "@/router";
import { useDashboardStore } from "@/store/modules/dashboard";
import copy from "@/utils/copy";
import { TextColors, MetricCatalog } from "@/views/dashboard/data";
import { TextColors } from "@/views/dashboard/data";
import Trace from "@/views/dashboard/related/trace/Index.vue";
import { WidgetType, QueryOrders, Status, RefIdTypes, ExpressionResultType } from "@/views/dashboard/data";
/*global defineProps, Recordable*/
/*global defineProps */
const props = defineProps({
data: {
type: Object as PropType<{
[key: string]: { name: string; value: number; refId: string; owner: object }[];
[key: string]: { name: string; value: number; refId: string }[];
}>,
default: () => ({}),
},
@ -85,14 +80,12 @@ limitations under the License. -->
expressions: string[];
typesOfMQE: string[];
relatedTrace: any;
valueRelatedDashboard: string;
}>,
default: () => ({ color: "purple" }),
},
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
});
const { t } = useI18n();
const dashboardStore = useDashboardStore();
const showTrace = ref<boolean>(false);
const traceOptions = ref<{ type: string; filters?: unknown }>({
type: WidgetType.Trace,
@ -114,15 +107,14 @@ limitations under the License. -->
function handleClick(i: string) {
copy(i);
}
function viewTrace(item: { name: string; refId: string; value: unknown; owner: object }) {
function viewTrace(item: { name: string; refId: string; value: unknown }) {
const filters = {
...item,
queryOrder: QueryOrders[1].value,
status: Status[2].value,
id: refIdType.value === RefIdTypes[1].value ? item.refId : undefined,
id: item.refId,
metricValue: [{ label: props.config.expressions[0], data: item.value, value: item.name }],
isReadRecords: props.config.typesOfMQE.includes(ExpressionResultType.RECORD_LIST) || undefined,
owner: refIdType.value === RefIdTypes[2].value ? item.owner : null,
};
traceOptions.value = {
...traceOptions.value,
@ -130,23 +122,6 @@ limitations under the License. -->
};
showTrace.value = true;
}
function viewDashboard(item: Recordable) {
const { owner } = item;
let path;
if (owner.scope === MetricCatalog.SERVICE) {
path = `/dashboard/${dashboardStore.layerId}/${owner.scope}/${owner.serviceID}/${props.config.valueRelatedDashboard}`;
}
if (owner.scope === MetricCatalog.SERVICE_INSTANCE) {
path = `/dashboard/${dashboardStore.layerId}/${owner.scope}/${owner.serviceID}/${owner.serviceInstanceID}/${props.config.valueRelatedDashboard}`;
}
if (owner.scope === MetricCatalog.ENDPOINT) {
path = `/dashboard/${dashboardStore.layerId}/${owner.scope}/${owner.serviceID}/${owner.endpointID}/${props.config.valueRelatedDashboard}`;
}
if (!path) {
return;
}
router.push(path);
}
</script>
<style lang="scss" scoped>
.top-list {
@ -212,10 +187,11 @@ limitations under the License. -->
}
.operation {
padding: 5px;
padding: 5px 0;
color: $font-color;
cursor: pointer;
position: relative;
text-align: center;
font-size: $font-size-smaller;
&:hover {

View File

@ -53,5 +53,5 @@
}
.inputs {
width: 400px;
width: 300px;
}

View File

@ -77,20 +77,16 @@ limitations under the License. -->
}
}
async function queryMetrics() {
const widgets: LayoutConfig[] = [];
const widgets = [];
for (const item of dashboardStore.layout) {
if (item.type === WidgetType.Widget) {
if (!ListChartTypes.includes(item.graph?.type || "")) {
widgets.push(item);
}
const isList = ListChartTypes.includes(item.type || "");
if (item.type === WidgetType.Widget && !isList) {
widgets.push(item);
}
if (item.type === WidgetType.Tab) {
const index = isNaN(item.activedTabIndex) ? 0 : item.activedTabIndex;
widgets.push(
...item.children[index].children.filter(
(d: LayoutConfig) => d.type === WidgetType.Widget && !ListChartTypes.includes(d.graph?.type || ""),
),
);
widgets.push(...item.children[index].children);
}
}
const configList = widgets.map((d: LayoutConfig) => ({
@ -107,9 +103,7 @@ limitations under the License. -->
}
async function queryTabsMetrics() {
const configList = dashboardStore.currentTabItems
.filter(
(item: LayoutConfig) => item.type === WidgetType.Widget && !ListChartTypes.includes(item.graph?.type || ""),
)
.filter((item: LayoutConfig) => item.type === WidgetType.Widget && !ListChartTypes.includes(item.type || ""))
.map((d: LayoutConfig) => ({
metrics: d.expressions || [],
metricConfig: d.metricConfig || [],
@ -157,7 +151,7 @@ limitations under the License. -->
},
);
watch(
() => [appStore.durationTime, dashboardStore.layout],
() => appStore.durationTime,
() => {
if (dashboardStore.entity === EntityType[1].value) {
queryMetrics();

View File

@ -16,7 +16,7 @@ limitations under the License. -->
<div class="dashboard-tool flex-h">
<div :class="isRelation ? 'flex-v' : 'flex-h'" class="tool-selectors">
<div class="flex-h">
<div class="flex-h selectors-item" v-if="key !== 10">
<div class="selectors-item" v-if="key !== 10">
<span class="label">$Service</span>
<Selector
v-model="states.currentService"
@ -30,7 +30,7 @@ limitations under the License. -->
<Icon size="small" iconName="hierarchy_topology" />
</span>
</div>
<div class="flex-h selectors-item" v-if="key === 3 || key === 4 || key === 5 || key === 6">
<div class="selectors-item" v-if="key === 3 || key === 4 || key === 5 || key === 6">
<span class="label">
{{ ["EndpointRelation", "Endpoint"].includes(dashboardStore.entity) ? "$Endpoint" : "$ServiceInstance" }}
</span>
@ -52,7 +52,7 @@ limitations under the License. -->
<Icon size="small" iconName="hierarchy_topology" />
</span>
</div>
<div class="flex-h selectors-item" v-if="key === 5 || key === 6">
<div class="selectors-item" v-if="key === 5 || key === 6">
<span class="label"> $Process </span>
<Selector
v-model="states.currentProcess"
@ -65,7 +65,7 @@ limitations under the License. -->
</div>
</div>
<div class="flex-h" :class="isRelation ? 'relation' : ''">
<div class="flex-h selectors-item" v-if="key === 2 || key === 4 || key === 5">
<div class="selectors-item" v-if="key === 2 || key === 4 || key === 5">
<span class="label">$DestinationService</span>
<Selector
v-model="states.currentDestService"
@ -76,7 +76,7 @@ limitations under the License. -->
class="selectors"
/>
</div>
<div class="flex-h selectors-item" v-if="key === 4 || key === 5">
<div class="selectors-item" v-if="key === 4 || key === 5">
<span class="label">
{{ dashboardStore.entity === "EndpointRelation" ? "$DestinationEndpoint" : "$DestinationServiceInstance" }}
</span>
@ -91,7 +91,7 @@ limitations under the License. -->
:isRemote="dashboardStore.entity === 'EndpointRelation'"
/>
</div>
<div class="flex-h selectors-item" v-if="key === 5">
<div class="selectors-item" v-if="key === 5">
<span class="label"> $DestinationProcess </span>
<Selector
v-model="states.currentDestProcess"
@ -105,11 +105,11 @@ limitations under the License. -->
</div>
</div>
<div class="flex-h tools" v-loading="loading" v-if="!appStore.isMobile">
<div class="flex-h" v-if="dashboardStore.editMode">
<div class="tool-icons" v-if="dashboardStore.editMode">
<el-dropdown content="Controls" placement="bottom" :persistent="false">
<div class="icon-btn">
<Icon size="sm" iconName="control" />
</div>
<i>
<Icon class="icon-btn" size="sm" iconName="control" />
</i>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="clickIcons(t)" v-for="(t, index) in toolIcons" :key="index" :title="t.content">
@ -120,9 +120,9 @@ limitations under the License. -->
</template>
</el-dropdown>
<el-tooltip content="Apply" placement="bottom">
<div class="icon-btn" @click="applyDashboard">
<Icon size="sm" iconName="save" />
</div>
<i @click="applyDashboard">
<Icon class="icon-btn" size="sm" iconName="save" />
</i>
</el-tooltip>
</div>
<div class="ml-5">
@ -693,6 +693,10 @@ limitations under the License. -->
padding: 4px 2px;
}
.tool-icons {
margin-top: 2px;
}
.tools {
justify-content: space-between;
align-items: center;
@ -700,10 +704,9 @@ limitations under the License. -->
}
.icon-btn {
width: 22px;
height: 22px;
display: inline-block;
padding: 3px;
text-align: center;
line-height: 18px;
border: 1px solid var(--sw-icon-btn-border);
border-radius: 3px;
margin-left: 6px;
@ -731,7 +734,7 @@ limitations under the License. -->
.hierarchy-btn {
display: inline-block;
padding: 0 3px;
padding: 0 2px 2px;
border: 1px solid #666;
border-radius: 4px;
text-align: center;

View File

@ -1,69 +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="flex-h content">
<TaskList />
<div class="vis-graph ml-5">
<div class="mb-20">
<Filter />
</div>
<div class="stack" v-loading="asyncProfilingStore.loadingTree">
<EBPFStack :type="ComponentType" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
import { ElMessage } from "element-plus";
import { useAsyncProfilingStore } from "@/store/modules/async-profiling";
import { useSelectorStore } from "@/store/modules/selectors";
import TaskList from "./components/TaskList.vue";
import Filter from "./components/Filter.vue";
import EBPFStack from "@/views/dashboard/related/ebpf/components/EBPFStack.vue";
import { ComponentType } from "./components/data";
const asyncProfilingStore = useAsyncProfilingStore();
const selectorStore = useSelectorStore();
onMounted(async () => {
const resp = await asyncProfilingStore.getServiceInstances({ serviceId: selectorStore.currentService.id });
if (resp && resp.errors) {
ElMessage.error(resp.errors);
}
});
</script>
<style lang="scss" scoped>
.content {
height: calc(100% - 30px);
width: 100%;
}
.vis-graph {
height: 100%;
flex-grow: 2;
min-width: 700px;
overflow: hidden;
position: relative;
width: calc(100% - 330px);
}
.stack {
width: 100%;
overflow: auto;
height: calc(100% - 100px);
padding-bottom: 10px;
}
</style>

View File

@ -1,50 +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="flex-h header">
<div class="title">Async Profiling</div>
<el-button class="mr-20" size="small" type="primary" @click="() => (newTask = true)">
{{ t("newTask") }}
</el-button>
</div>
<el-dialog v-model="newTask" :destroy-on-close="true" fullscreen @closed="newTask = false">
<NewTask @close="newTask = false" />
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import NewTask from "./components/NewTask.vue";
const { t } = useI18n();
const newTask = ref<boolean>(false);
</script>
<style lang="scss" scoped>
.header {
padding: 10px;
font-size: $font-size-smaller;
border-bottom: 1px solid $border-color;
justify-content: space-between;
}
.name {
width: 270px;
}
.title {
font-weight: bold;
line-height: 24px;
}
</style>

View File

@ -1,115 +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="flex-h">
<Selector
class="filter-selector"
:multiple="true"
:value="serviceInstanceIds"
size="small"
:options="instances"
placeholder="Select instances"
@change="changeInstances"
/>
<Selector
class="filter-events"
:value="selectedEventType"
size="small"
:options="eventTypes"
placeholder="Select a event"
@change="changeEventType"
/>
<el-button type="primary" size="small" @click="analyzeProfiling">
{{ t("analyze") }}
</el-button>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { ElMessage } from "element-plus";
import { useAsyncProfilingStore } from "@/store/modules/async-profiling";
import type { Instance } from "@/types/selector";
import type { Option } from "@/types/app";
import { EventsMap, ProfilingEvents, JFREventType } from "./data";
const { t } = useI18n();
const asyncProfilingStore = useAsyncProfilingStore();
const serviceInstanceIds = ref<string[]>([]);
const selectedEventType = ref<string>("");
const eventTypes = computed(() =>
(asyncProfilingStore.selectedTask.events ?? [])
.map((d: string) => {
if (d === ProfilingEvents[1]) {
return [
{ label: JFREventType.OBJECT_ALLOCATION_IN_NEW_TLAB, value: JFREventType.OBJECT_ALLOCATION_IN_NEW_TLAB },
{ label: JFREventType.OBJECT_ALLOCATION_OUTSIDE_TLAB, value: JFREventType.OBJECT_ALLOCATION_OUTSIDE_TLAB },
];
}
return { label: d, value: d };
})
.flat(),
);
const instances = computed(() =>
asyncProfilingStore.instances.filter((d: Instance) =>
(asyncProfilingStore.selectedTask.successInstanceIds ?? []).includes(d.id),
),
);
function changeInstances(options: Option[]) {
serviceInstanceIds.value = options.map((d: Option) => d.value);
asyncProfilingStore.setAnalyzeTrees([]);
}
function changeEventType(options: Option[]) {
selectedEventType.value = options[0].value;
asyncProfilingStore.setAnalyzeTrees([]);
}
async function analyzeProfiling() {
const instanceIds = asyncProfilingStore.instances
.filter((d: Instance) => (serviceInstanceIds.value ?? []).includes(d.value))
.map((d: Instance) => d.id);
const res = await asyncProfilingStore.getAsyncProfilingAnalyze({
instanceIds,
taskId: asyncProfilingStore.selectedTask.id,
eventType: (EventsMap as any)[selectedEventType.value],
});
if (res.data && res.data.errors) {
ElMessage.error(res.data.errors);
return;
}
}
watch(
() => asyncProfilingStore.selectedTask.successInstanceIds,
() => {
serviceInstanceIds.value = [];
selectedEventType.value = "";
},
);
</script>
<style>
.filter-selector {
width: 400px;
margin-right: 10px;
}
.filter-events {
width: 300px;
margin-right: 10px;
}
</style>

View File

@ -1,187 +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="async-profile-task">
<div>
<div class="label">{{ t("instance") }}</div>
<Selector
class="profile-input"
:multiple="true"
:value="serviceInstanceIds"
size="small"
:options="asyncProfilingStore.instances"
placeholder="Select instances"
@change="changeInstances"
:filterable="false"
/>
</div>
<div>
<div class="label">{{ t("duration") }}</div>
<Radio class="mb-5" :value="duration" :options="DurationOptions" @change="changeDuration" />
</div>
<div>
<div class="label">{{ t("profilingEvents") }}</div>
<el-checkbox-group v-model="asyncEvents" class="profile-input mb-5">
<el-checkbox
v-for="event in ProfilingEvents"
:label="event"
:value="event"
:key="event"
:disabled="disableEvents(event)"
/>
</el-checkbox-group>
</div>
<div>
<div class="label">
<span class="mr-5 cp">{{ t("execArgs") }}</span>
<el-popover placement="right" :width="480" trigger="hover" title="Async profiler extension parameters">
<template #reference>
<span>
<Icon iconName="help" />
</span>
</template>
<div>
<p>
<span class="mr-10">live </span>
<span>- build allocation profile from live objects only</span>
</p>
<p>
<span class="mr-10">lock[=DURATION] </span>
<span>- profile contended locks overflowing the DURATION ns bucket (default: 10us)</span>
</p>
<p>
<span class="mr-10">alloc[=BYTES] </span>
<span>- profile allocations with BYTES interval</span>
</p>
<p>
<span class="mr-10">interval=N </span>
<span>- sampling interval in ns (default: 10'000'000, i.e. 10 ms)</span>
</p>
<p>
<span class="mr-10">jstackdepth=N </span>
<span>- maximum Java stack depth (default: 2048)</span>
</p>
<p>
<span class="mr-10">chunksize=N </span>
<span>- approximate size of JFR chunk in bytes (default: 100 MB)</span>
</p>
<p>
<span class="mr-10">chunktime=N </span>
<span>- duration of JFR chunk in seconds (default: 1 hour)</span>
</p>
</div>
</el-popover>
</div>
<el-input size="small" class="profile-input" v-model="execArgs" />
</div>
<div>
<el-button @click="createTask" type="primary" class="create-task-btn" :loading="loading">
{{ t("createTask") }}
</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { useAsyncProfilingStore } from "@/store/modules/async-profiling";
import { useSelectorStore } from "@/store/modules/selectors";
import { ElMessage } from "element-plus";
import { DurationOptions, ProfilingEvents } from "./data";
/* global defineEmits */
const emits = defineEmits(["close"]);
const asyncProfilingStore = useAsyncProfilingStore();
const selectorStore = useSelectorStore();
const { t } = useI18n();
const serviceInstanceIds = ref<string[]>([]);
const asyncEvents = ref<string[]>([]);
const duration = ref<string>(DurationOptions[0].value);
const execArgs = ref<string>("");
const loading = ref<boolean>(false);
const PartofEvents = [ProfilingEvents[3], ProfilingEvents[4], ProfilingEvents[5]];
function changeDuration(val: string) {
duration.value = val;
}
function changeInstances(options: { id: string }[]) {
serviceInstanceIds.value = options.map((d: { id: string }) => d.id);
}
function disableEvents(item: string) {
if (asyncEvents.value.includes(ProfilingEvents[0]) && PartofEvents.includes(item)) {
return true;
}
if (item === ProfilingEvents[0]) {
for (const event of PartofEvents) {
if (asyncEvents.value.includes(event)) {
return true;
}
}
}
return false;
}
async function createTask() {
const params = {
serviceId: selectorStore.currentService.id,
serviceInstanceIds: serviceInstanceIds.value,
duration: Number(duration.value) * 60,
events: asyncEvents.value,
execArgs: execArgs.value,
};
loading.value = true;
const res = await asyncProfilingStore.createTask(params);
loading.value = false;
if (res.errors) {
ElMessage.error(res.errors);
return;
}
const { errorReason } = res.data;
if (errorReason) {
ElMessage.error(errorReason);
return;
}
emits("close");
ElMessage.success("Task created successfully");
}
</script>
<style lang="scss" scoped>
.async-profile-task {
margin: 0 auto;
width: 600px;
}
.date {
font-size: $font-size-smaller;
}
.label {
margin-top: 10px;
font-size: $font-size-normal;
}
.profile-input {
width: 600px;
}
.create-task-btn {
width: 600px;
margin-top: 50px;
}
</style>

Some files were not shown because too many files have changed in this diff Show More