mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-01 21:34:27 +00:00
feat: associate metrics with trace widget on dashboards (#174)
This commit is contained in:
parent
78f0096c00
commit
eda44db0cd
16
src/assets/icons/conditions.svg
Normal file
16
src/assets/icons/conditions.svg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!-- 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 t="1666624449554" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2649" width="48" height="48"><path d="M381.482667 673.877333a90.389333 90.389333 0 0 1 85.226666 60.245334H853.333333v64H465.28a90.389333 90.389333 0 0 1-167.573333 0H170.666667v-64h125.610666a90.389333 90.389333 0 0 1 85.205334-60.245334z m0 64a26.346667 26.346667 0 1 0 0 52.693334 26.346667 26.346667 0 0 0 0-52.693334z m261.034666-304.938666a90.389333 90.389333 0 0 1 85.205334 60.245333H853.333333v64h-127.04a90.389333 90.389333 0 0 1-167.573333 0H170.666667v-64h386.624a90.389333 90.389333 0 0 1 85.226666-60.245333z m0 64a26.346667 26.346667 0 1 0 0 52.693333 26.346667 26.346667 0 0 0 0-52.693333zM381.482667 192a90.389333 90.389333 0 0 1 85.226666 60.224H853.333333v64H465.28a90.389333 90.389333 0 0 1-167.573333 0H170.666667v-64h125.610666A90.389333 90.389333 0 0 1 381.482667 192z m0 64a26.346667 26.346667 0 1 0 0 52.693333 26.346667 26.346667 0 0 0 0-52.693333z" p-id="2650"></path></svg>
|
After Width: | Height: | Size: 1.7 KiB |
15
src/assets/icons/copy.svg
Normal file
15
src/assets/icons/copy.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!-- 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 t="1664265269855" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4109" width="48" height="48"><path d="M866.461538 39.384615H354.461538c-43.323077 0-78.769231 35.446154-78.76923 78.769231v39.384616h472.615384c43.323077 0 78.769231 35.446154 78.769231 78.76923v551.384616h39.384615c43.323077 0 78.769231-35.446154 78.769231-78.769231V118.153846c0-43.323077-35.446154-78.769231-78.769231-78.769231z m-118.153846 275.692308c0-43.323077-35.446154-78.769231-78.76923-78.769231H157.538462c-43.323077 0-78.769231 35.446154-78.769231 78.769231v590.769231c0 43.323077 35.446154 78.769231 78.769231 78.769231h512c43.323077 0 78.769231-35.446154 78.76923-78.769231V315.076923z m-354.461538 137.846154c0 11.815385-7.876923 19.692308-19.692308 19.692308h-157.538461c-11.815385 0-19.692308-7.876923-19.692308-19.692308v-39.384615c0-11.815385 7.876923-19.692308 19.692308-19.692308h157.538461c11.815385 0 19.692308 7.876923 19.692308 19.692308v39.384615z m157.538461 315.076923c0 11.815385-7.876923 19.692308-19.692307 19.692308H216.615385c-11.815385 0-19.692308-7.876923-19.692308-19.692308v-39.384615c0-11.815385 7.876923-19.692308 19.692308-19.692308h315.076923c11.815385 0 19.692308 7.876923 19.692307 19.692308v39.384615z m78.769231-157.538462c0 11.815385-7.876923 19.692308-19.692308 19.692308H216.615385c-11.815385 0-19.692308-7.876923-19.692308-19.692308v-39.384615c0-11.815385 7.876923-19.692308 19.692308-19.692308h393.846153c11.815385 0 19.692308 7.876923 19.692308 19.692308v39.384615z" p-id="4110"></path></svg>
|
After Width: | Height: | Size: 2.3 KiB |
@ -13,6 +13,5 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
<title>info_outline</title>
|
|
||||||
<path d="M11.016 9v-2.016h1.969v2.016h-1.969zM12 20.016q3.281 0 5.648-2.367t2.367-5.648-2.367-5.648-5.648-2.367-5.648 2.367-2.367 5.648 2.367 5.648 5.648 2.367zM12 2.016q4.125 0 7.055 2.93t2.93 7.055-2.93 7.055-7.055 2.93-7.055-2.93-2.93-7.055 2.93-7.055 7.055-2.93zM11.016 17.016v-6h1.969v6h-1.969z"></path>
|
<path d="M11.016 9v-2.016h1.969v2.016h-1.969zM12 20.016q3.281 0 5.648-2.367t2.367-5.648-2.367-5.648-5.648-2.367-5.648 2.367-2.367 5.648 2.367 5.648 5.648 2.367zM12 2.016q4.125 0 7.055 2.93t2.93 7.055-2.93 7.055-7.055 2.93-7.055-2.93-2.93-7.055 2.93-7.055 7.055-2.93zM11.016 17.016v-6h1.969v6h-1.969z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
15
src/assets/icons/operation.svg
Normal file
15
src/assets/icons/operation.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!-- 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 t="1664266918236" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5378" width="48" height="48"><path d="M571.178667 643.328a144 144 0 0 1-189.098667-193.450667l77.781333 77.866667a48 48 0 1 0 67.882667-67.84l-77.824-77.909333a144 144 0 0 1 193.450667 189.141333l226.517333 207.061333a64.896 64.896 0 1 1-91.690667 91.690667l-207.018666-226.56z m51.498666 134.656a288.298667 288.298667 0 0 1-38.656 12.928v95.488c0 5.290667-4.309333 9.6-9.642666 9.6h-124.757334a9.6 9.6 0 0 1-9.6-9.6v-95.488a286.293333 286.293333 0 0 1-74.325333-30.805333l-67.541333 67.541333a9.6 9.6 0 0 1-13.568 0L196.352 739.413333a9.6 9.6 0 0 1 0-13.568l67.541333-67.541333a286.293333 286.293333 0 0 1-30.805333-74.325333H137.6A9.6 9.6 0 0 1 128 574.378667v-124.757334c0-5.290667 4.309333-9.6 9.6-9.6h95.488c6.826667-26.453333 17.28-51.370667 30.805333-74.325333L196.352 298.154667a9.6 9.6 0 0 1 0-13.568L284.586667 196.352a9.6 9.6 0 0 1 13.568 0l67.541333 67.498667a287.146667 287.146667 0 0 1 74.325333-30.848V137.6c0-5.290667 4.266667-9.6 9.6-9.6h124.8c5.248 0 9.6 4.309333 9.6 9.6v95.488c26.368 6.826667 51.328 17.28 74.282667 30.805333l67.541333-67.541333a9.6 9.6 0 0 1 13.568 0l88.234667 88.234667a9.6 9.6 0 0 1 0 13.568l-67.498667 67.541333a287.146667 287.146667 0 0 1 30.848 74.282667h95.402667c5.290667 0 9.6 4.352 9.6 9.642666v124.757334c0 5.333333-4.266667 9.6-9.6 9.6h-95.488c-4.693333 18.133333-11.178667 35.754667-19.328 52.650666a9.6 9.6 0 0 1-15.018667 2.986667l-10.112-9.173333-38.314666-34.261334-12.16-10.88a9.6 9.6 0 0 1-2.688-10.24A192.298667 192.298667 0 0 0 512 320a192 192 0 1 0 63.018667 373.333333 9.6 9.6 0 0 1 10.24 2.645334l10.837333 12.074666 35.285333 39.338667 8.149334 9.130667a9.6 9.6 0 0 1-2.901334 15.061333 283.306667 283.306667 0 0 1-13.952 6.4z" p-id="5379"></path></svg>
|
After Width: | Height: | Size: 2.5 KiB |
@ -15,6 +15,27 @@ limitations under the License. -->
|
|||||||
<template>
|
<template>
|
||||||
<div class="chart" ref="chartRef" :style="`height:${height};width:${width};`">
|
<div class="chart" ref="chartRef" :style="`height:${height};width:${width};`">
|
||||||
<div v-if="!available" class="no-data">No Data</div>
|
<div v-if="!available" class="no-data">No Data</div>
|
||||||
|
<div class="menus" v-show="visMenus" ref="menus">
|
||||||
|
<div class="tools" @click="associateMetrics">
|
||||||
|
{{ t("associateMetrics") }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="tools"
|
||||||
|
@click="viewTrace"
|
||||||
|
v-if="props.relatedTrace && props.relatedTrace.enableRelate"
|
||||||
|
>
|
||||||
|
{{ t("viewTrace") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-drawer
|
||||||
|
v-model="showTrace"
|
||||||
|
size="100%"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:before-close="() => (showTrace = false)"
|
||||||
|
:append-to-body="true"
|
||||||
|
>
|
||||||
|
<Trace :data="traceOptions" />
|
||||||
|
</el-drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@ -28,15 +49,28 @@ import {
|
|||||||
computed,
|
computed,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { EventParams } from "@/types/app";
|
||||||
|
import { Filters, RelatedTrace } from "@/types/dashboard";
|
||||||
import { useECharts } from "@/hooks/useEcharts";
|
import { useECharts } from "@/hooks/useEcharts";
|
||||||
import { addResizeListener, removeResizeListener } from "@/utils/event";
|
import { addResizeListener, removeResizeListener } from "@/utils/event";
|
||||||
|
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||||
|
import associateProcessor from "@/hooks/useAssociateProcessor";
|
||||||
|
|
||||||
/*global Nullable, defineProps, defineEmits*/
|
/*global Nullable, defineProps, defineEmits*/
|
||||||
const emits = defineEmits(["select"]);
|
const emits = defineEmits(["select"]);
|
||||||
|
const { t } = useI18n();
|
||||||
const chartRef = ref<Nullable<HTMLDivElement>>(null);
|
const chartRef = ref<Nullable<HTMLDivElement>>(null);
|
||||||
|
const menus = ref<Nullable<HTMLDivElement>>(null);
|
||||||
|
const visMenus = ref<boolean>(false);
|
||||||
const { setOptions, resize, getInstance } = useECharts(
|
const { setOptions, resize, getInstance } = useECharts(
|
||||||
chartRef as Ref<HTMLDivElement>
|
chartRef as Ref<HTMLDivElement>
|
||||||
);
|
);
|
||||||
|
const currentParams = ref<Nullable<EventParams>>(null);
|
||||||
|
const showTrace = ref<boolean>(false);
|
||||||
|
const traceOptions = ref<{ type: string; filters?: unknown }>({
|
||||||
|
type: "Trace",
|
||||||
|
});
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
height: { type: String, default: "100%" },
|
height: { type: String, default: "100%" },
|
||||||
width: { type: String, default: "100%" },
|
width: { type: String, default: "100%" },
|
||||||
@ -45,15 +79,10 @@ const props = defineProps({
|
|||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
type: Object as PropType<{
|
type: Object as PropType<Filters>,
|
||||||
duration: {
|
},
|
||||||
startTime: string;
|
relatedTrace: {
|
||||||
endTime: string;
|
type: Object as PropType<RelatedTrace>,
|
||||||
};
|
|
||||||
isRange: boolean;
|
|
||||||
dataIndex?: number;
|
|
||||||
sourceId: string;
|
|
||||||
}>,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const available = computed(
|
const available = computed(
|
||||||
@ -66,14 +95,29 @@ const available = computed(
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await setOptions(props.option);
|
await setOptions(props.option);
|
||||||
chartRef.value && addResizeListener(unref(chartRef), resize);
|
chartRef.value && addResizeListener(unref(chartRef), resize);
|
||||||
|
instanceEvent();
|
||||||
|
});
|
||||||
|
|
||||||
|
function instanceEvent() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const instance = getInstance();
|
const instance = getInstance();
|
||||||
|
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instance.on("click", (params: unknown) => {
|
instance.on("click", (params: EventParams) => {
|
||||||
emits("select", params);
|
currentParams.value = params;
|
||||||
|
if (!menus.value || !chartRef.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visMenus.value = true;
|
||||||
|
const w = chartRef.value.getBoundingClientRect().width || 0;
|
||||||
|
if (w - params.event.offsetX > 125) {
|
||||||
|
menus.value.style.left = params.event.offsetX + "px";
|
||||||
|
} else {
|
||||||
|
menus.value.style.left = params.event.offsetX - 125 + "px";
|
||||||
|
}
|
||||||
|
menus.value.style.top = params.event.offsetY + 5 + "px";
|
||||||
});
|
});
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
@ -81,9 +125,7 @@ onMounted(async () => {
|
|||||||
if (instance.isDisposed()) {
|
if (instance.isDisposed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instance.dispatchAction({
|
visMenus.value = false;
|
||||||
type: "hideTip",
|
|
||||||
});
|
|
||||||
instance.dispatchAction({
|
instance.dispatchAction({
|
||||||
type: "updateAxisPointer",
|
type: "updateAxisPointer",
|
||||||
currTrigger: "leave",
|
currTrigger: "leave",
|
||||||
@ -92,7 +134,13 @@ onMounted(async () => {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
function associateMetrics() {
|
||||||
|
emits("select", currentParams.value);
|
||||||
|
visMenus.value = true;
|
||||||
|
updateOptions();
|
||||||
|
}
|
||||||
|
|
||||||
function updateOptions() {
|
function updateOptions() {
|
||||||
const instance = getInstance();
|
const instance = getInstance();
|
||||||
@ -103,60 +151,26 @@ function updateOptions() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (props.filters.isRange) {
|
if (props.filters.isRange) {
|
||||||
|
const { eventAssociate } = associateProcessor(props);
|
||||||
const options = eventAssociate();
|
const options = eventAssociate();
|
||||||
setOptions(options || props.option);
|
setOptions(options || props.option);
|
||||||
} else {
|
} else {
|
||||||
instance.dispatchAction({
|
instance.dispatchAction({
|
||||||
type: "showTip",
|
type: "updateAxisPointer",
|
||||||
dataIndex: props.filters.dataIndex,
|
dataIndex: props.filters.dataIndex,
|
||||||
seriesIndex: 0,
|
seriesIndex: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventAssociate() {
|
function viewTrace() {
|
||||||
if (!props.filters) {
|
const item = associateProcessor(props).traceFilters(currentParams.value);
|
||||||
return;
|
traceOptions.value = {
|
||||||
}
|
...traceOptions.value,
|
||||||
if (!props.filters.duration) {
|
filters: item,
|
||||||
return props.option;
|
|
||||||
}
|
|
||||||
if (!props.option.series[0]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const list = props.option.series[0].data.map(
|
|
||||||
(d: (number | string)[]) => d[0]
|
|
||||||
);
|
|
||||||
if (!list.includes(props.filters.duration.endTime)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const markArea = {
|
|
||||||
silent: true,
|
|
||||||
itemStyle: {
|
|
||||||
opacity: 0.3,
|
|
||||||
},
|
|
||||||
data: [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
xAxis: props.filters.duration.startTime,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
xAxis: props.filters.duration.endTime,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
const series = (window as any).structuredClone(props.option.series);
|
showTrace.value = true;
|
||||||
for (const [key, temp] of series.entries()) {
|
visMenus.value = true;
|
||||||
if (key === 0) {
|
|
||||||
temp.markArea = markArea;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const options = {
|
|
||||||
...props.option,
|
|
||||||
series,
|
|
||||||
};
|
|
||||||
return options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@ -170,6 +184,7 @@ watch(
|
|||||||
}
|
}
|
||||||
let options;
|
let options;
|
||||||
if (props.filters && props.filters.isRange) {
|
if (props.filters && props.filters.isRange) {
|
||||||
|
const { eventAssociate } = associateProcessor(props);
|
||||||
options = eventAssociate();
|
options = eventAssociate();
|
||||||
}
|
}
|
||||||
setOptions(options || props.option);
|
setOptions(options || props.option);
|
||||||
@ -201,4 +216,28 @@ onBeforeUnmount(() => {
|
|||||||
.chart {
|
.chart {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menus {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 9999999;
|
||||||
|
box-shadow: #ddd 1px 2px 10px;
|
||||||
|
transition: all cubic-bezier(0.075, 0.82, 0.165, 1) linear;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: rgb(51, 51, 51);
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
padding: 5px;
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409eff;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -32,6 +32,6 @@ export const queryInstances = `query queryInstances(${Instances.variable}) {${In
|
|||||||
export const queryLayers = `query listLayer {${Layers.query}}`;
|
export const queryLayers = `query listLayer {${Layers.query}}`;
|
||||||
export const queryService = `query queryService(${getService.variable}) {${getService.query}}`;
|
export const queryService = `query queryService(${getService.variable}) {${getService.query}}`;
|
||||||
export const queryInstance = `query queryInstance(${getInstance.variable}) {${getInstance.query}}`;
|
export const queryInstance = `query queryInstance(${getInstance.variable}) {${getInstance.query}}`;
|
||||||
export const queryEndpoint = `query queryInstance(${getEndpoint.variable}) {${getEndpoint.query}}`;
|
export const queryEndpoint = `query queryEndpoint(${getEndpoint.variable}) {${getEndpoint.query}}`;
|
||||||
export const queryProcesses = `query queryProcesses(${Processes.variable}) {${Processes.query}}`;
|
export const queryProcesses = `query queryProcesses(${Processes.variable}) {${Processes.query}}`;
|
||||||
export const queryProcess = `query queryProcess(${getProcess.variable}) {${getProcess.query}}`;
|
export const queryProcess = `query queryProcess(${getProcess.variable}) {${getProcess.query}}`;
|
||||||
|
138
src/hooks/useAssociateProcessor.ts
Normal file
138
src/hooks/useAssociateProcessor.ts
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
|
import dateFormatStep from "@/utils/dateFormat";
|
||||||
|
import getLocalTime from "@/utils/localtime";
|
||||||
|
import { EventParams } from "@/types/app";
|
||||||
|
|
||||||
|
export default function associateProcessor(props: any) {
|
||||||
|
function eventAssociate() {
|
||||||
|
if (!props.filters) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!props.filters.duration) {
|
||||||
|
return props.option;
|
||||||
|
}
|
||||||
|
if (!props.option.series[0]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const list = props.option.series[0].data.map(
|
||||||
|
(d: (number | string)[]) => d[0]
|
||||||
|
);
|
||||||
|
if (!list.includes(props.filters.duration.endTime)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const markArea = {
|
||||||
|
silent: true,
|
||||||
|
itemStyle: {
|
||||||
|
opacity: 0.3,
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
xAxis: props.filters.duration.startTime,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xAxis: props.filters.duration.endTime,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const series = (window as any).structuredClone(props.option.series);
|
||||||
|
for (const [key, temp] of series.entries()) {
|
||||||
|
if (key === 0) {
|
||||||
|
temp.markArea = markArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const options = {
|
||||||
|
...props.option,
|
||||||
|
series,
|
||||||
|
};
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
function traceFilters(currentParams: Nullable<EventParams>) {
|
||||||
|
const appStore = useAppStoreWithOut();
|
||||||
|
|
||||||
|
if (!currentParams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const start = appStore.intervalUnix[currentParams.dataIndex];
|
||||||
|
const { step } = appStore.durationRow;
|
||||||
|
let duration = undefined;
|
||||||
|
|
||||||
|
if (start) {
|
||||||
|
const end = start;
|
||||||
|
duration = {
|
||||||
|
start: dateFormatStep(
|
||||||
|
getLocalTime(appStore.utc, new Date(start)),
|
||||||
|
step,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
end: dateFormatStep(
|
||||||
|
getLocalTime(appStore.utc, new Date(end)),
|
||||||
|
step,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
step,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const relatedTrace = props.relatedTrace || {};
|
||||||
|
const status = relatedTrace.status;
|
||||||
|
const queryOrder = relatedTrace.queryOrder;
|
||||||
|
const latency = relatedTrace.latency;
|
||||||
|
const series = props.option.series || [];
|
||||||
|
const item: any = {
|
||||||
|
duration,
|
||||||
|
queryOrder,
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
if (latency) {
|
||||||
|
const latencyList = series.map(
|
||||||
|
(d: { name: string; data: number[][] }, index: number) => {
|
||||||
|
const data = [
|
||||||
|
d.data[currentParams.dataIndex][1],
|
||||||
|
series[index + 1]
|
||||||
|
? series[index + 1].data[currentParams.dataIndex][1]
|
||||||
|
: Infinity,
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
label:
|
||||||
|
d.name +
|
||||||
|
"--" +
|
||||||
|
(series[index + 1] ? series[index + 1].name : "Infinity"),
|
||||||
|
value: String(index),
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
item.latency = latencyList;
|
||||||
|
}
|
||||||
|
const value = series.map(
|
||||||
|
(d: { name: string; data: number[][] }, index: number) => {
|
||||||
|
return {
|
||||||
|
label: d.name,
|
||||||
|
value: String(index),
|
||||||
|
data: d.data[currentParams.dataIndex][1],
|
||||||
|
date: d.data[currentParams.dataIndex][0],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
item.metricValue = value;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return { eventAssociate, traceFilters };
|
||||||
|
}
|
@ -146,6 +146,7 @@ const msg = {
|
|||||||
pause: "Pause",
|
pause: "Pause",
|
||||||
begin: "Start",
|
begin: "Start",
|
||||||
associateOptions: "Association Options",
|
associateOptions: "Association Options",
|
||||||
|
associateMetrics: "Association Metrics",
|
||||||
widget: "Widget",
|
widget: "Widget",
|
||||||
nameTip:
|
nameTip:
|
||||||
"The name only supports Chinese and English, horizontal lines and underscores. The length of the name is limited to 300 characters",
|
"The name only supports Chinese and English, horizontal lines and underscores. The length of the name is limited to 300 characters",
|
||||||
@ -156,6 +157,16 @@ const msg = {
|
|||||||
postgreSQL: "PostgreSQL",
|
postgreSQL: "PostgreSQL",
|
||||||
endpointTips: "The table shows up to 20 pieces of endpoints.",
|
endpointTips: "The table shows up to 20 pieces of endpoints.",
|
||||||
apisix: "APISIX",
|
apisix: "APISIX",
|
||||||
|
viewTrace: "View Related Traces",
|
||||||
|
relatedTraceOptions: "Related Trace Options",
|
||||||
|
setLatencyDuration: "Set Latency Range",
|
||||||
|
queryOrder: "Query Order",
|
||||||
|
latency: "Latency",
|
||||||
|
metricValues: "Metric Values",
|
||||||
|
queryConditions: "Query Conditions",
|
||||||
|
enableRelatedTrace: "Enable Related Trace",
|
||||||
|
maxTraceDuration: "Maximum Duration",
|
||||||
|
minTraceDuration: "Minimum Duration",
|
||||||
seconds: "Seconds",
|
seconds: "Seconds",
|
||||||
hourTip: "Select Hour",
|
hourTip: "Select Hour",
|
||||||
minuteTip: "Select Minute",
|
minuteTip: "Select Minute",
|
||||||
@ -348,6 +359,6 @@ const msg = {
|
|||||||
"Notice: Please press Enter after inputting a key of content, exclude key of content(key=value).",
|
"Notice: Please press Enter after inputting a key of content, exclude key of content(key=value).",
|
||||||
language: "Language",
|
language: "Language",
|
||||||
gateway: "Gateway",
|
gateway: "Gateway",
|
||||||
virtualMQ: "Virtual MQ"
|
virtualMQ: "Virtual MQ",
|
||||||
};
|
};
|
||||||
export default msg;
|
export default msg;
|
||||||
|
@ -146,6 +146,7 @@ const msg = {
|
|||||||
pause: "Pausa",
|
pause: "Pausa",
|
||||||
begin: "Inicio",
|
begin: "Inicio",
|
||||||
associateOptions: "Opciones de asociación",
|
associateOptions: "Opciones de asociación",
|
||||||
|
associateMetrics: "Índice de correlación",
|
||||||
widget: "Dispositivo pequeño",
|
widget: "Dispositivo pequeño",
|
||||||
text: "Texto",
|
text: "Texto",
|
||||||
duplicateName: "Nombre duplicado",
|
duplicateName: "Nombre duplicado",
|
||||||
@ -156,10 +157,20 @@ const msg = {
|
|||||||
postgreSQL: "PostgreSQL",
|
postgreSQL: "PostgreSQL",
|
||||||
endpointTips: "Aquí, la tabla muestra hasta 20 punto final.",
|
endpointTips: "Aquí, la tabla muestra hasta 20 punto final.",
|
||||||
apisix: "APISIX",
|
apisix: "APISIX",
|
||||||
|
queryOrder: "Orden de consulta",
|
||||||
|
latency: "Retraso",
|
||||||
|
metricValues: "Valor métrico",
|
||||||
seconds: "Segundos",
|
seconds: "Segundos",
|
||||||
hourTip: "Seleccione Hora",
|
hourTip: "Seleccione Hora",
|
||||||
minuteTip: "Seleccione Minuto",
|
minuteTip: "Seleccione Minuto",
|
||||||
secondTip: "Seleccione Segundo",
|
secondTip: "Seleccione Segundo",
|
||||||
|
viewTrace: "Ver trazas relacionadas",
|
||||||
|
relatedTraceOptions: "Opciones de seguimiento relacionadas",
|
||||||
|
setLatencyDuration: "Establecer el rango de retardo",
|
||||||
|
enableRelatedTrace: "Activar trazas relacionadas",
|
||||||
|
queryConditions: "Condiciones de consulta",
|
||||||
|
maxTraceDuration: "Duración máxima",
|
||||||
|
minTraceDuration: "Duración mínima",
|
||||||
second: "s",
|
second: "s",
|
||||||
yearSuffix: "Año",
|
yearSuffix: "Año",
|
||||||
monthsHead: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
|
monthsHead: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
|
||||||
|
@ -144,6 +144,7 @@ const msg = {
|
|||||||
pause: "暂停",
|
pause: "暂停",
|
||||||
begin: "开始",
|
begin: "开始",
|
||||||
associateOptions: "关联选项",
|
associateOptions: "关联选项",
|
||||||
|
associateMetrics: "关联指标",
|
||||||
widget: "部件",
|
widget: "部件",
|
||||||
enableAssociate: "启用关联",
|
enableAssociate: "启用关联",
|
||||||
nameTip: "该名称仅支持中文和英文、横线和下划线, 并且限制长度为300个字符",
|
nameTip: "该名称仅支持中文和英文、横线和下划线, 并且限制长度为300个字符",
|
||||||
@ -153,6 +154,16 @@ const msg = {
|
|||||||
postgreSQL: "PostgreSQL",
|
postgreSQL: "PostgreSQL",
|
||||||
endpointTips: "这里最多展示20条endpoints。",
|
endpointTips: "这里最多展示20条endpoints。",
|
||||||
apisix: "APISIX",
|
apisix: "APISIX",
|
||||||
|
viewTrace: "查看相关Trace",
|
||||||
|
relatedTraceOptions: "相关的Trace选项",
|
||||||
|
setLatencyDuration: "设置延时范围",
|
||||||
|
queryOrder: "查询顺序",
|
||||||
|
latency: "延迟",
|
||||||
|
metricValues: "指标值",
|
||||||
|
enableRelatedTrace: "启用相关Trace",
|
||||||
|
queryConditions: "查询条件",
|
||||||
|
maxTraceDuration: "最大持续时间",
|
||||||
|
minTraceDuration: "最小持续时间",
|
||||||
seconds: "秒",
|
seconds: "秒",
|
||||||
hourTip: "选择小时",
|
hourTip: "选择小时",
|
||||||
minuteTip: "选择分钟",
|
minuteTip: "选择分钟",
|
||||||
|
@ -71,7 +71,7 @@ export const appStore = defineStore({
|
|||||||
step: this.duration.step,
|
step: this.duration.step,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
intervalTime(): string[] {
|
intervalUnix(): number[] {
|
||||||
let interval = 946080000000;
|
let interval = 946080000000;
|
||||||
switch (this.duration.step) {
|
switch (this.duration.step) {
|
||||||
case "MINUTE":
|
case "MINUTE":
|
||||||
@ -97,12 +97,17 @@ export const appStore = defineStore({
|
|||||||
this.utcMin * 60000;
|
this.utcMin * 60000;
|
||||||
const startUnix: number = this.duration.start.getTime();
|
const startUnix: number = this.duration.start.getTime();
|
||||||
const endUnix: number = this.duration.end.getTime();
|
const endUnix: number = this.duration.end.getTime();
|
||||||
const timeIntervals: string[] = [];
|
const timeIntervals: number[] = [];
|
||||||
for (let i = 0; i <= endUnix - startUnix; i += interval) {
|
for (let i = 0; i <= endUnix - startUnix; i += interval) {
|
||||||
const temp: string = dateFormatTime(
|
timeIntervals.push(startUnix + i - utcSpace);
|
||||||
new Date(startUnix + i - utcSpace),
|
}
|
||||||
this.duration.step
|
return timeIntervals;
|
||||||
);
|
},
|
||||||
|
intervalTime(): string[] {
|
||||||
|
const arr = this.intervalUnix;
|
||||||
|
const timeIntervals: string[] = [];
|
||||||
|
for (const item of arr) {
|
||||||
|
const temp: string = dateFormatTime(new Date(item), this.duration.step);
|
||||||
timeIntervals.push(temp);
|
timeIntervals.push(temp);
|
||||||
}
|
}
|
||||||
return timeIntervals;
|
return timeIntervals;
|
||||||
|
@ -22,6 +22,7 @@ import graphql from "@/graphql";
|
|||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { useSelectorStore } from "@/store/modules/selectors";
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
|
import { QueryOrders } from "@/views/dashboard/data";
|
||||||
|
|
||||||
interface TraceState {
|
interface TraceState {
|
||||||
services: Service[];
|
services: Service[];
|
||||||
@ -47,7 +48,7 @@ export const traceStore = defineStore({
|
|||||||
conditions: {
|
conditions: {
|
||||||
queryDuration: useAppStoreWithOut().durationTime,
|
queryDuration: useAppStoreWithOut().durationTime,
|
||||||
traceState: "ALL",
|
traceState: "ALL",
|
||||||
queryOrder: "BY_START_TIME",
|
queryOrder: QueryOrders[0].value,
|
||||||
paging: { pageNum: 1, pageSize: 20 },
|
paging: { pageNum: 1, pageSize: 20 },
|
||||||
},
|
},
|
||||||
traceSpanLogs: [],
|
traceSpanLogs: [],
|
||||||
@ -71,7 +72,7 @@ export const traceStore = defineStore({
|
|||||||
queryDuration: useAppStoreWithOut().durationTime,
|
queryDuration: useAppStoreWithOut().durationTime,
|
||||||
paging: { pageNum: 1, pageSize: 20 },
|
paging: { pageNum: 1, pageSize: 20 },
|
||||||
traceState: "ALL",
|
traceState: "ALL",
|
||||||
queryOrder: "BY_START_TIME",
|
queryOrder: QueryOrders[0].value,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async getServices(layer: string) {
|
async getServices(layer: string) {
|
||||||
@ -84,6 +85,36 @@ export const traceStore = defineStore({
|
|||||||
this.services = res.data.data.services;
|
this.services = res.data.data.services;
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
|
async getService(serviceId: string) {
|
||||||
|
if (!serviceId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res: AxiosResponse = await graphql.query("queryService").params({
|
||||||
|
serviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
async getInstance(instanceId: string) {
|
||||||
|
if (!instanceId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res: AxiosResponse = await graphql.query("queryInstance").params({
|
||||||
|
instanceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
async getEndpoint(endpointId: string) {
|
||||||
|
if (!endpointId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res: AxiosResponse = await graphql.query("queryEndpoint").params({
|
||||||
|
endpointId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
async getInstances(id: string) {
|
async getInstances(id: string) {
|
||||||
const serviceId = this.selectorStore.currentService
|
const serviceId = this.selectorStore.currentService
|
||||||
? this.selectorStore.currentService.id
|
? this.selectorStore.currentService.id
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import "element-plus/es/components/message/style/css";
|
import "element-plus/es/components/message/style/css";
|
||||||
import "element-plus/es/components/message-box/style/css";
|
import "element-plus/es/components/message-box/style/css";
|
||||||
import "element-plus/es/components/notification/style/css";
|
import "element-plus/es/components/notification/style/css";
|
||||||
|
import "element-plus/es/components/drawer/style/css";
|
||||||
import "./grid.scss";
|
import "./grid.scss";
|
||||||
import "./lib.scss";
|
import "./lib.scss";
|
||||||
import "./reset.scss";
|
import "./reset.scss";
|
||||||
|
@ -153,6 +153,14 @@ pre {
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-drawer__header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer__body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.switch {
|
.switch {
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
|
15
src/types/app.d.ts
vendored
15
src/types/app.d.ts
vendored
@ -32,3 +32,18 @@ export type Paging = {
|
|||||||
pageNum: number;
|
pageNum: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EventParams = {
|
||||||
|
componentType: string;
|
||||||
|
seriesType: string;
|
||||||
|
seriesIndex: number;
|
||||||
|
seriesName: string;
|
||||||
|
name: string;
|
||||||
|
dataIndex: number;
|
||||||
|
data: unknown;
|
||||||
|
dataType: string;
|
||||||
|
value: number | Array;
|
||||||
|
color: string;
|
||||||
|
event: any;
|
||||||
|
dataIndex: number;
|
||||||
|
};
|
||||||
|
1
src/types/components.d.ts
vendored
1
src/types/components.d.ts
vendored
@ -25,6 +25,7 @@ declare module '@vue/runtime-core' {
|
|||||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||||
|
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||||
ElSlider: typeof import('element-plus/es')['ElSlider']
|
ElSlider: typeof import('element-plus/es')['ElSlider']
|
||||||
|
20
src/types/dashboard.d.ts
vendored
20
src/types/dashboard.d.ts
vendored
@ -1,3 +1,4 @@
|
|||||||
|
import { DurationTime } from "@/types/app";
|
||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -39,7 +40,18 @@ export interface LayoutConfig {
|
|||||||
id?: string;
|
id?: string;
|
||||||
associate?: { widgetId: string }[];
|
associate?: { widgetId: string }[];
|
||||||
eventAssociate?: boolean;
|
eventAssociate?: boolean;
|
||||||
filters?: {
|
filters?: Filters;
|
||||||
|
relatedTrace?: RelatedTrace;
|
||||||
|
}
|
||||||
|
export type RelatedTrace = {
|
||||||
|
duration: DurationTime;
|
||||||
|
status: string;
|
||||||
|
queryOrder: string;
|
||||||
|
latency: boolean;
|
||||||
|
enableRelate: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Filters = {
|
||||||
dataIndex: number;
|
dataIndex: number;
|
||||||
sourceId: string;
|
sourceId: string;
|
||||||
isRange?: boolean;
|
isRange?: boolean;
|
||||||
@ -50,8 +62,10 @@ export interface LayoutConfig {
|
|||||||
traceId?: string;
|
traceId?: string;
|
||||||
spanId?: string;
|
spanId?: string;
|
||||||
segmentId?: string;
|
segmentId?: string;
|
||||||
};
|
id?: string;
|
||||||
}
|
queryOrder?: string;
|
||||||
|
status?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type MetricConfigOpt = {
|
export type MetricConfigOpt = {
|
||||||
unit?: string;
|
unit?: string;
|
||||||
|
@ -36,6 +36,7 @@ limitations under the License. -->
|
|||||||
metrics: dashboardStore.selectedGrid.metrics,
|
metrics: dashboardStore.selectedGrid.metrics,
|
||||||
metricTypes: dashboardStore.selectedGrid.metricTypes,
|
metricTypes: dashboardStore.selectedGrid.metricTypes,
|
||||||
metricConfig: dashboardStore.selectedGrid.metricConfig,
|
metricConfig: dashboardStore.selectedGrid.metricConfig,
|
||||||
|
relatedTrace: dashboardStore.selectedGrid.relatedTrace,
|
||||||
}"
|
}"
|
||||||
:needQuery="true"
|
:needQuery="true"
|
||||||
/>
|
/>
|
||||||
@ -65,6 +66,13 @@ limitations under the License. -->
|
|||||||
>
|
>
|
||||||
<AssociateOptions />
|
<AssociateOptions />
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
|
<el-collapse-item
|
||||||
|
:title="t('relatedTraceOptions')"
|
||||||
|
name="5"
|
||||||
|
v-if="hasAssociate"
|
||||||
|
>
|
||||||
|
<RelatedTraceOptions />
|
||||||
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
<!-- 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="item">
|
||||||
|
<span class="label">{{ t("enableRelatedTrace") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="enableRelate"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateConfig({ enableRelate })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-show="enableRelate">
|
||||||
|
<div class="item">
|
||||||
|
<span class="label">{{ t("status") }}</span>
|
||||||
|
<Selector
|
||||||
|
size="small"
|
||||||
|
:value="status"
|
||||||
|
:options="Status"
|
||||||
|
placeholder="Select a status"
|
||||||
|
@change="updateConfig({ status: $event[0].value })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="label">{{ t("queryOrder") }}</span>
|
||||||
|
<Selector
|
||||||
|
size="small"
|
||||||
|
:value="queryOrder"
|
||||||
|
:options="QueryOrders"
|
||||||
|
placeholder="Select a option"
|
||||||
|
@change="updateConfig({ queryOrder: $event[0].value })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="label">{{ t("setLatencyDuration") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="latency"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateConfig({ latency })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
|
import { Status, QueryOrders } from "../../data";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const dashboardStore = useDashboardStore();
|
||||||
|
const traceOpt = dashboardStore.selectedGrid.relatedTrace || {};
|
||||||
|
const status = ref<string>(traceOpt.status || Status[0].value);
|
||||||
|
const queryOrder = ref<string>(traceOpt.queryOrder || QueryOrders[0].value);
|
||||||
|
const latency = ref<boolean>(traceOpt.setLatencyDuration || false);
|
||||||
|
const enableRelate = ref<boolean>(traceOpt.enableRelate || false);
|
||||||
|
|
||||||
|
function updateConfig(param: { [key: string]: unknown }) {
|
||||||
|
const relatedTrace = {
|
||||||
|
...dashboardStore.selectedGrid.relatedTrace,
|
||||||
|
...param,
|
||||||
|
};
|
||||||
|
dashboardStore.selectWidget({ ...dashboardStore.selectedGrid, relatedTrace });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.label {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -19,10 +19,12 @@ import StyleOptions from "./graph-styles";
|
|||||||
import WidgetOptions from "./WidgetOptions.vue";
|
import WidgetOptions from "./WidgetOptions.vue";
|
||||||
import MetricOptions from "./metric/Index.vue";
|
import MetricOptions from "./metric/Index.vue";
|
||||||
import AssociateOptions from "./AssociateOptions.vue";
|
import AssociateOptions from "./AssociateOptions.vue";
|
||||||
|
import RelatedTraceOptions from "./RelatedTraceOptions.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
...StyleOptions,
|
...StyleOptions,
|
||||||
WidgetOptions,
|
WidgetOptions,
|
||||||
MetricOptions,
|
MetricOptions,
|
||||||
AssociateOptions,
|
AssociateOptions,
|
||||||
|
RelatedTraceOptions,
|
||||||
};
|
};
|
||||||
|
@ -40,7 +40,7 @@ limitations under the License. -->
|
|||||||
<span class="tab-icons">
|
<span class="tab-icons">
|
||||||
<el-tooltip content="Copy Link" placement="bottom">
|
<el-tooltip content="Copy Link" placement="bottom">
|
||||||
<i @click="copyLink">
|
<i @click="copyLink">
|
||||||
<Icon size="middle" iconName="review-list" class="tab-icon" />
|
<Icon size="middle" iconName="copy" class="tab-icon" />
|
||||||
</i>
|
</i>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</span>
|
</span>
|
||||||
|
@ -64,6 +64,7 @@ limitations under the License. -->
|
|||||||
id: data.id,
|
id: data.id,
|
||||||
metricConfig: data.metricConfig,
|
metricConfig: data.metricConfig,
|
||||||
filters: data.filters || {},
|
filters: data.filters || {},
|
||||||
|
relatedTrace: data.relatedTrace || {},
|
||||||
}"
|
}"
|
||||||
:needQuery="needQuery"
|
:needQuery="needQuery"
|
||||||
@click="clickHandle"
|
@click="clickHandle"
|
||||||
|
@ -174,7 +174,7 @@ export const SortOrder = [
|
|||||||
];
|
];
|
||||||
export const AllTools = [
|
export const AllTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||||
@ -182,7 +182,7 @@ export const AllTools = [
|
|||||||
];
|
];
|
||||||
export const ServiceTools = [
|
export const ServiceTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||||
@ -194,7 +194,7 @@ export const ServiceTools = [
|
|||||||
];
|
];
|
||||||
export const InstanceTools = [
|
export const InstanceTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||||
{ name: "assignment", content: "Add Log", id: "addLog" },
|
{ name: "assignment", content: "Add Log", id: "addLog" },
|
||||||
@ -208,7 +208,7 @@ export const InstanceTools = [
|
|||||||
];
|
];
|
||||||
export const EndpointTools = [
|
export const EndpointTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||||
@ -217,25 +217,25 @@ export const EndpointTools = [
|
|||||||
];
|
];
|
||||||
export const ProcessTools = [
|
export const ProcessTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "time_range", content: "Add Time Range Text", id: "addTimeRange" },
|
{ name: "time_range", content: "Add Time Range Text", id: "addTimeRange" },
|
||||||
];
|
];
|
||||||
export const ServiceRelationTools = [
|
export const ServiceRelationTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const EndpointRelationTools = [
|
export const EndpointRelationTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
];
|
];
|
||||||
export const InstanceRelationTools = [
|
export const InstanceRelationTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
];
|
];
|
||||||
|
@ -24,7 +24,12 @@ limitations under the License. -->
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import Line from "./Line.vue";
|
import Line from "./Line.vue";
|
||||||
import { AreaConfig, EventParams } from "@/types/dashboard";
|
import {
|
||||||
|
AreaConfig,
|
||||||
|
EventParams,
|
||||||
|
RelatedTrace,
|
||||||
|
Filters,
|
||||||
|
} from "@/types/dashboard";
|
||||||
|
|
||||||
/*global defineProps, defineEmits */
|
/*global defineProps, defineEmits */
|
||||||
const emits = defineEmits(["click"]);
|
const emits = defineEmits(["click"]);
|
||||||
@ -37,15 +42,8 @@ defineProps({
|
|||||||
config: {
|
config: {
|
||||||
type: Object as PropType<
|
type: Object as PropType<
|
||||||
AreaConfig & {
|
AreaConfig & {
|
||||||
filters: {
|
filters: Filters;
|
||||||
sourceId: string;
|
relatedTrace: RelatedTrace;
|
||||||
duration: {
|
|
||||||
startTime: string;
|
|
||||||
endTime: string;
|
|
||||||
};
|
|
||||||
isRange: boolean;
|
|
||||||
dataIndex?: number;
|
|
||||||
};
|
|
||||||
} & { id: string }
|
} & { id: string }
|
||||||
>,
|
>,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
|
@ -18,7 +18,12 @@ limitations under the License. -->
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { BarConfig, EventParams } from "@/types/dashboard";
|
import {
|
||||||
|
BarConfig,
|
||||||
|
EventParams,
|
||||||
|
RelatedTrace,
|
||||||
|
Filters,
|
||||||
|
} from "@/types/dashboard";
|
||||||
|
|
||||||
/*global defineProps, defineEmits */
|
/*global defineProps, defineEmits */
|
||||||
const emits = defineEmits(["click"]);
|
const emits = defineEmits(["click"]);
|
||||||
@ -32,15 +37,8 @@ const props = defineProps({
|
|||||||
config: {
|
config: {
|
||||||
type: Object as PropType<
|
type: Object as PropType<
|
||||||
BarConfig & {
|
BarConfig & {
|
||||||
filters: {
|
filters: Filters;
|
||||||
sourceId: string;
|
relatedTrace: RelatedTrace;
|
||||||
duration: {
|
|
||||||
startTime: string;
|
|
||||||
endTime: string;
|
|
||||||
};
|
|
||||||
isRange: boolean;
|
|
||||||
dataIndex?: number;
|
|
||||||
};
|
|
||||||
} & { id: string }
|
} & { id: string }
|
||||||
>,
|
>,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
@ -107,16 +105,12 @@ function getOption() {
|
|||||||
return {
|
return {
|
||||||
color,
|
color,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: "none",
|
||||||
zlevel: 1000,
|
axisPointer: {
|
||||||
z: 60,
|
type: "cross",
|
||||||
confine: true,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: 13,
|
|
||||||
color: "#333",
|
color: "#333",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||||
},
|
},
|
||||||
enterable: true,
|
|
||||||
extraCssText: "max-height: 300px; overflow: auto; border: none",
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
type: "scroll",
|
type: "scroll",
|
||||||
@ -136,6 +130,12 @@ function getOption() {
|
|||||||
bottom: 5,
|
bottom: 5,
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
},
|
||||||
|
axisPointer: {
|
||||||
|
label: {
|
||||||
|
color: "#333",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||||
|
},
|
||||||
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: "category",
|
type: "category",
|
||||||
axisTick: {
|
axisTick: {
|
||||||
|
@ -13,12 +13,22 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<template>
|
<template>
|
||||||
<Graph :option="option" @select="clickEvent" :filters="config.filters" />
|
<Graph
|
||||||
|
:option="option"
|
||||||
|
@select="clickEvent"
|
||||||
|
:filters="config.filters"
|
||||||
|
:relatedTrace="config.relatedTrace"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { LineConfig, EventParams } from "@/types/dashboard";
|
import {
|
||||||
|
LineConfig,
|
||||||
|
EventParams,
|
||||||
|
RelatedTrace,
|
||||||
|
Filters,
|
||||||
|
} from "@/types/dashboard";
|
||||||
|
|
||||||
/*global defineProps, defineEmits */
|
/*global defineProps, defineEmits */
|
||||||
const emits = defineEmits(["click"]);
|
const emits = defineEmits(["click"]);
|
||||||
@ -32,15 +42,8 @@ const props = defineProps({
|
|||||||
config: {
|
config: {
|
||||||
type: Object as PropType<
|
type: Object as PropType<
|
||||||
LineConfig & {
|
LineConfig & {
|
||||||
filters: {
|
filters: Filters;
|
||||||
sourceId: string;
|
relatedTrace: RelatedTrace;
|
||||||
duration: {
|
|
||||||
startTime: string;
|
|
||||||
endTime: string;
|
|
||||||
};
|
|
||||||
isRange: boolean;
|
|
||||||
dataIndex?: number;
|
|
||||||
};
|
|
||||||
} & { id: string }
|
} & { id: string }
|
||||||
>,
|
>,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
@ -115,13 +118,19 @@ function getOption() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const tooltip = {
|
const tooltip = {
|
||||||
trigger: "axis",
|
trigger: "none",
|
||||||
textStyle: {
|
axisPointer: {
|
||||||
fontSize: 12,
|
type: "cross",
|
||||||
color: "#333",
|
color: "#333",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||||
},
|
},
|
||||||
enterable: true,
|
// trigger: "axis",
|
||||||
confine: true,
|
// textStyle: {
|
||||||
|
// fontSize: 12,
|
||||||
|
// color: "#333",
|
||||||
|
// },
|
||||||
|
// enterable: true,
|
||||||
|
// confine: true,
|
||||||
extraCssText: "max-height: 300px; overflow: auto; border: none;",
|
extraCssText: "max-height: 300px; overflow: auto; border: none;",
|
||||||
};
|
};
|
||||||
const tips = {
|
const tips = {
|
||||||
@ -151,6 +160,12 @@ function getOption() {
|
|||||||
color: props.theme === "dark" ? "#fff" : "#333",
|
color: props.theme === "dark" ? "#fff" : "#333",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
axisPointer: {
|
||||||
|
label: {
|
||||||
|
color: "#333",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||||
|
},
|
||||||
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: keys.length === 1 ? 15 : 55,
|
top: keys.length === 1 ? 15 : 55,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
@ -23,14 +23,19 @@ limitations under the License. -->
|
|||||||
{{ i.name }}
|
{{ i.name }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="copy">
|
<el-popover placement="bottom" trigger="click">
|
||||||
<Icon
|
<template #reference>
|
||||||
iconName="review-list"
|
<div class="operation-icon cp ml-10">
|
||||||
size="middle"
|
<Icon iconName="ellipsis_v" size="middle" />
|
||||||
class="cp"
|
|
||||||
@click="handleClick(i.name)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="operation" @click="handleClick(i.name)">
|
||||||
|
<span>{{ t("copy") }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="operation" @click="viewTrace(i)">
|
||||||
|
<span>{{ t("viewTrace") }}</span>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
</div>
|
</div>
|
||||||
<el-progress
|
<el-progress
|
||||||
:stroke-width="6"
|
:stroke-width="6"
|
||||||
@ -39,28 +44,45 @@ limitations under the License. -->
|
|||||||
:show-text="false"
|
:show-text="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<el-drawer
|
||||||
|
v-model="showTrace"
|
||||||
|
size="100%"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:before-close="() => (showTrace = false)"
|
||||||
|
:append-to-body="true"
|
||||||
|
>
|
||||||
|
<Trace :data="traceOptions" />
|
||||||
|
</el-drawer>
|
||||||
</div>
|
</div>
|
||||||
<div class="center no-data" v-else>No Data</div>
|
<div class="center no-data" v-else>No Data</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { computed } from "vue";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { computed, ref } from "vue";
|
||||||
import copy from "@/utils/copy";
|
import copy from "@/utils/copy";
|
||||||
import { TextColors } from "@/views/dashboard/data";
|
import { TextColors } from "@/views/dashboard/data";
|
||||||
|
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||||
|
import { QueryOrders, Status } from "../data";
|
||||||
/*global defineProps */
|
/*global defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object as PropType<{
|
type: Object as PropType<{
|
||||||
[key: string]: { name: string; value: number; traceIds: string[] }[];
|
[key: string]: { name: string; value: number; id: string }[];
|
||||||
}>,
|
}>,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
type: Object as PropType<{ color: string }>,
|
type: Object as PropType<{ color: string; metrics: string[] }>,
|
||||||
default: () => ({ color: "purple" }),
|
default: () => ({ color: "purple" }),
|
||||||
},
|
},
|
||||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||||
});
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const showTrace = ref<boolean>(false);
|
||||||
|
const traceOptions = ref<{ type: string; filters?: unknown }>({
|
||||||
|
type: "Trace",
|
||||||
|
});
|
||||||
const key = computed(() => Object.keys(props.data)[0] || "");
|
const key = computed(() => Object.keys(props.data)[0] || "");
|
||||||
const available = computed(
|
const available = computed(
|
||||||
() =>
|
() =>
|
||||||
@ -78,6 +100,21 @@ const maxValue = computed(() => {
|
|||||||
function handleClick(i: string) {
|
function handleClick(i: string) {
|
||||||
copy(i);
|
copy(i);
|
||||||
}
|
}
|
||||||
|
function viewTrace(item: { name: string; id: string; value: unknown }) {
|
||||||
|
const filters = {
|
||||||
|
...item,
|
||||||
|
queryOrder: QueryOrders[1].value,
|
||||||
|
status: Status[2].value,
|
||||||
|
metricValue: [
|
||||||
|
{ label: props.config.metrics[0], data: item.value, value: item.name },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
traceOptions.value = {
|
||||||
|
...traceOptions.value,
|
||||||
|
filters,
|
||||||
|
};
|
||||||
|
showTrace.value = true;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.top-list {
|
.top-list {
|
||||||
@ -109,10 +146,6 @@ function handleClick(i: string) {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy {
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calls {
|
.calls {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
@ -141,4 +174,22 @@ function handleClick(i: string) {
|
|||||||
-webkit-box-pack: center;
|
-webkit-box-pack: center;
|
||||||
-webkit-box-align: center;
|
-webkit-box-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.operation-icon {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation {
|
||||||
|
padding: 5px 0;
|
||||||
|
color: #333;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409eff;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -34,7 +34,7 @@ limitations under the License. -->
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</h5>
|
</h5>
|
||||||
<div class="mb-5 blue sm">
|
<div class="mb-5 blue">
|
||||||
<Selector
|
<Selector
|
||||||
size="small"
|
size="small"
|
||||||
:value="
|
:value="
|
||||||
@ -46,12 +46,7 @@ limitations under the License. -->
|
|||||||
@change="changeTraceId"
|
@change="changeTraceId"
|
||||||
class="trace-detail-ids"
|
class="trace-detail-ids"
|
||||||
/>
|
/>
|
||||||
<Icon
|
<Icon class="cp ml-5" iconName="copy" @click="handleClick" />
|
||||||
size="sm"
|
|
||||||
class="icon grey link-hover cp ml-5"
|
|
||||||
iconName="review-list"
|
|
||||||
@click="handleClick"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-h item">
|
<div class="flex-h item">
|
||||||
<div>
|
<div>
|
||||||
|
@ -95,17 +95,15 @@ limitations under the License. -->
|
|||||||
import { ref, reactive, watch, onUnmounted } from "vue";
|
import { ref, reactive, watch, onUnmounted } from "vue";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { Option } from "@/types/app";
|
import { Option, DurationTime } from "@/types/app";
|
||||||
import { Status } from "../../data";
|
|
||||||
import { useTraceStore } from "@/store/modules/trace";
|
import { useTraceStore } from "@/store/modules/trace";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { useSelectorStore } from "@/store/modules/selectors";
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
import ConditionTags from "@/views/components/ConditionTags.vue";
|
import ConditionTags from "@/views/components/ConditionTags.vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { EntityType } from "../../data";
|
import { EntityType, QueryOrders, Status } from "../../data";
|
||||||
import { LayoutConfig } from "@/types/dashboard";
|
import { LayoutConfig } from "@/types/dashboard";
|
||||||
import { DurationTime } from "@/types/app";
|
|
||||||
|
|
||||||
/*global defineProps, Recordable */
|
/*global defineProps, Recordable */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -115,28 +113,29 @@ const props = defineProps({
|
|||||||
default: () => ({ graph: {} }),
|
default: () => ({ graph: {} }),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const traceId = ref<string>(
|
const filters = reactive<Recordable>(props.data.filters || {});
|
||||||
(props.data.filters && props.data.filters.traceId) || ""
|
const traceId = ref<string>(filters.traceId || "");
|
||||||
);
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const appStore = useAppStoreWithOut();
|
const appStore = useAppStoreWithOut();
|
||||||
const selectorStore = useSelectorStore();
|
const selectorStore = useSelectorStore();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
const traceStore = useTraceStore();
|
const traceStore = useTraceStore();
|
||||||
const duration = ref<DurationTime>(
|
const duration = ref<DurationTime>(filters.duration || appStore.durationTime);
|
||||||
(props.data.filters && props.data.filters.duration) || appStore.durationTime
|
|
||||||
);
|
|
||||||
const minTraceDuration = ref<number>();
|
const minTraceDuration = ref<number>();
|
||||||
const maxTraceDuration = ref<number>();
|
const maxTraceDuration = ref<number>();
|
||||||
const tagsList = ref<string[]>([]);
|
const tagsList = ref<string[]>([]);
|
||||||
const tagsMap = ref<Option[]>([]);
|
const tagsMap = ref<Option[]>([]);
|
||||||
const state = reactive<Recordable>({
|
const state = reactive<Recordable>({
|
||||||
status: { label: "All", value: "ALL" },
|
status: filters.status === "ERROR" ? Status[2] : Status[0],
|
||||||
instance: { value: "0", label: "All" },
|
instance: { value: "0", label: "All" },
|
||||||
endpoint: { value: "0", label: "All" },
|
endpoint: { value: "0", label: "All" },
|
||||||
service: { value: "", label: "" },
|
service: { value: "", label: "" },
|
||||||
});
|
});
|
||||||
|
if (filters.queryOrder) {
|
||||||
|
traceStore.setTraceCondition({
|
||||||
|
queryOrder: filters.queryOrder,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (props.needQuery) {
|
if (props.needQuery) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@ -164,7 +163,7 @@ async function getServices() {
|
|||||||
ElMessage.error(resp.errors);
|
ElMessage.error(resp.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.service = traceStore.services[0];
|
state.service = getCurrentNode(traceStore.services) || traceStore.services[0];
|
||||||
getEndpoints(state.service.id);
|
getEndpoints(state.service.id);
|
||||||
getInstances(state.service.id);
|
getInstances(state.service.id);
|
||||||
}
|
}
|
||||||
@ -175,7 +174,8 @@ async function getEndpoints(id?: string, keyword?: string) {
|
|||||||
ElMessage.error(resp.errors);
|
ElMessage.error(resp.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.endpoint = traceStore.endpoints[0];
|
state.endpoint =
|
||||||
|
getCurrentNode(traceStore.endpoints) || traceStore.endpoints[0];
|
||||||
}
|
}
|
||||||
async function getInstances(id?: string) {
|
async function getInstances(id?: string) {
|
||||||
const resp = await traceStore.getInstances(id);
|
const resp = await traceStore.getInstances(id);
|
||||||
@ -183,9 +183,39 @@ async function getInstances(id?: string) {
|
|||||||
ElMessage.error(resp.errors);
|
ElMessage.error(resp.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.instance = traceStore.instances[0];
|
state.instance =
|
||||||
|
getCurrentNode(traceStore.instances) || traceStore.instances[0];
|
||||||
}
|
}
|
||||||
function searchTraces() {
|
function getCurrentNode(arr: { id: string }[]) {
|
||||||
|
let item;
|
||||||
|
if (!props.data.filters) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
if (props.data.filters.id) {
|
||||||
|
item = arr.find((d: { id: string }) => d.id === props.data.filters?.id);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
function setCondition() {
|
||||||
|
let param: any = {
|
||||||
|
traceState: state.status.value || "ALL",
|
||||||
|
tags: tagsMap.value.length ? tagsMap.value : undefined,
|
||||||
|
queryOrder: traceStore.conditions.queryOrder || QueryOrders[1].value,
|
||||||
|
queryDuration: duration.value,
|
||||||
|
minTraceDuration: Number(minTraceDuration.value),
|
||||||
|
maxTraceDuration: Number(maxTraceDuration.value),
|
||||||
|
traceId: traceId.value || undefined,
|
||||||
|
paging: { pageNum: 1, pageSize: 20 },
|
||||||
|
};
|
||||||
|
if (props.data.filters && props.data.filters.id) {
|
||||||
|
param = {
|
||||||
|
...param,
|
||||||
|
serviceId: selectorStore.currentService.id,
|
||||||
|
endpointId: state.endpoint.id || undefined,
|
||||||
|
serviceInstanceId: state.instance.id || undefined,
|
||||||
|
};
|
||||||
|
return param;
|
||||||
|
}
|
||||||
let endpoint = "",
|
let endpoint = "",
|
||||||
instance = "";
|
instance = "";
|
||||||
if (dashboardStore.entity === EntityType[2].value) {
|
if (dashboardStore.entity === EntityType[2].value) {
|
||||||
@ -194,21 +224,18 @@ function searchTraces() {
|
|||||||
if (dashboardStore.entity === EntityType[3].value) {
|
if (dashboardStore.entity === EntityType[3].value) {
|
||||||
instance = selectorStore.currentPod.id;
|
instance = selectorStore.currentPod.id;
|
||||||
}
|
}
|
||||||
traceStore.setTraceCondition({
|
param = {
|
||||||
|
...param,
|
||||||
serviceId: selectorStore.currentService
|
serviceId: selectorStore.currentService
|
||||||
? selectorStore.currentService.id
|
? selectorStore.currentService.id
|
||||||
: state.service.id,
|
: state.service.id,
|
||||||
traceId: traceId.value || undefined,
|
|
||||||
endpointId: endpoint || state.endpoint.id || undefined,
|
endpointId: endpoint || state.endpoint.id || undefined,
|
||||||
serviceInstanceId: instance || state.instance.id || undefined,
|
serviceInstanceId: instance || state.instance.id || undefined,
|
||||||
traceState: state.status.value || "ALL",
|
};
|
||||||
queryDuration: duration.value,
|
return param;
|
||||||
minTraceDuration: Number(minTraceDuration.value),
|
}
|
||||||
maxTraceDuration: Number(maxTraceDuration.value),
|
function searchTraces() {
|
||||||
queryOrder: traceStore.conditions.queryOrder || "BY_DURATION",
|
traceStore.setTraceCondition(setCondition());
|
||||||
tags: tagsMap.value.length ? tagsMap.value : undefined,
|
|
||||||
paging: { pageNum: 1, pageSize: 20 },
|
|
||||||
});
|
|
||||||
queryTraces();
|
queryTraces();
|
||||||
}
|
}
|
||||||
async function queryTraces() {
|
async function queryTraces() {
|
||||||
@ -263,10 +290,13 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// Event widget associate with trace widget
|
||||||
watch(
|
watch(
|
||||||
() => props.data.filters,
|
() => props.data.filters,
|
||||||
(newJson, oldJson) => {
|
(newJson, oldJson) => {
|
||||||
if (props.data.filters) {
|
if (!props.data.filters) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (JSON.stringify(newJson) === JSON.stringify(oldJson)) {
|
if (JSON.stringify(newJson) === JSON.stringify(oldJson)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -274,7 +304,6 @@ watch(
|
|||||||
duration.value = props.data.filters.duration || appStore.durationTime;
|
duration.value = props.data.filters.duration || appStore.durationTime;
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
267
src/views/dashboard/related/trace/Header.vue
Normal file
267
src/views/dashboard/related/trace/Header.vue
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
<!-- 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="conditions flex-h">
|
||||||
|
<el-radio-group v-model="conditions" @change="changeCondition">
|
||||||
|
<el-radio-button
|
||||||
|
v-for="(item, index) in items"
|
||||||
|
:label="item.label"
|
||||||
|
:key="item.label + index"
|
||||||
|
border
|
||||||
|
>
|
||||||
|
{{ t(item.label) }}
|
||||||
|
</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
<Selector
|
||||||
|
v-if="conditions === 'latency' && filters.latency.length > 1"
|
||||||
|
:value="filters.latency[0].value"
|
||||||
|
:options="filters.latency"
|
||||||
|
placeholder="Select a option"
|
||||||
|
@change="changeLatency"
|
||||||
|
class="ml-10"
|
||||||
|
/>
|
||||||
|
<el-popover trigger="hover" width="250" placement="bottom" effect="light">
|
||||||
|
<template #reference>
|
||||||
|
<div class="cp conditions-popup">
|
||||||
|
<Icon iconName="conditions" size="middle" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<div class="title">{{ t("queryConditions") }}</div>
|
||||||
|
<div
|
||||||
|
v-for="key in Object.keys(FiltersKeys)"
|
||||||
|
:key="key"
|
||||||
|
v-show="traceStore.conditions[FiltersKeys[key]]"
|
||||||
|
>
|
||||||
|
<span v-if="key !== 'duration'">
|
||||||
|
{{ t(key) }}: {{ traceStore.conditions[FiltersKeys[key]] }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
<el-popover trigger="hover" width="250" placement="bottom" effect="light">
|
||||||
|
<template #reference>
|
||||||
|
<div class="cp metric-value">
|
||||||
|
<Icon iconName="info_outline" size="middle" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<div class="title">{{ t("metricValues") }}</div>
|
||||||
|
<div v-for="metric in filters.metricValue" :key="metric.value">
|
||||||
|
{{ metric.label }}: {{ metric.data }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
<div class="flex-h">
|
||||||
|
<ConditionTags :type="'TRACE'" @update="updateTags" />
|
||||||
|
<div class="search-btn">
|
||||||
|
<el-button size="small" type="primary" @click="queryTraces">
|
||||||
|
{{ t("search") }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, onUnmounted } from "vue";
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { Option, DurationTime } from "@/types/app";
|
||||||
|
import { useTraceStore } from "@/store/modules/trace";
|
||||||
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
|
import ConditionTags from "@/views/components/ConditionTags.vue";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { EntityType, QueryOrders, Status } from "../../data";
|
||||||
|
import { LayoutConfig } from "@/types/dashboard";
|
||||||
|
|
||||||
|
const FiltersKeys: { [key: string]: string } = {
|
||||||
|
status: "traceState",
|
||||||
|
queryOrder: "queryOrder",
|
||||||
|
duration: "queryDuration",
|
||||||
|
minTraceDuration: "minTraceDuration",
|
||||||
|
maxTraceDuration: "maxTraceDuration",
|
||||||
|
};
|
||||||
|
/*global defineProps, Recordable */
|
||||||
|
const props = defineProps({
|
||||||
|
needQuery: { type: Boolean, default: true },
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<LayoutConfig>,
|
||||||
|
default: () => ({ graph: {} }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const filters = reactive<Recordable>(props.data.filters || {});
|
||||||
|
const appStore = useAppStoreWithOut();
|
||||||
|
const selectorStore = useSelectorStore();
|
||||||
|
const dashboardStore = useDashboardStore();
|
||||||
|
const traceStore = useTraceStore();
|
||||||
|
const tagsList = ref<string[]>([]);
|
||||||
|
const tagsMap = ref<Option[]>([]);
|
||||||
|
const duration = ref<DurationTime>(filters.duration || appStore.durationTime);
|
||||||
|
const state = reactive<Recordable>({
|
||||||
|
instance: "",
|
||||||
|
endpoint: "",
|
||||||
|
service: "",
|
||||||
|
});
|
||||||
|
const conditions = ref<string>("");
|
||||||
|
const items = ref<{ label: string; value: string }[]>([]);
|
||||||
|
const currentLatency = ref<number[]>(
|
||||||
|
filters.latency ? filters.latency[0].data : []
|
||||||
|
);
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
for (const d of Object.keys(filters)) {
|
||||||
|
if (filters[d] && ["status", "queryOrder", "latency"].includes(d)) {
|
||||||
|
items.value.push({ label: d, value: FiltersKeys[d] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conditions.value = (items.value[0] && items.value[0].label) || "";
|
||||||
|
if (!filters.id) {
|
||||||
|
state.service = selectorStore.currentService.id;
|
||||||
|
if (dashboardStore.entity === EntityType[2].value) {
|
||||||
|
state.instance = selectorStore.currentPod.id;
|
||||||
|
}
|
||||||
|
if (dashboardStore.entity === EntityType[3].value) {
|
||||||
|
state.endpoint = selectorStore.currentPod.id;
|
||||||
|
}
|
||||||
|
await queryTraces();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dashboardStore.entity === EntityType[1].value) {
|
||||||
|
await getService();
|
||||||
|
}
|
||||||
|
if (dashboardStore.entity === EntityType[0].value) {
|
||||||
|
state.service = selectorStore.currentService.id;
|
||||||
|
await getInstance();
|
||||||
|
if (!state.instance) {
|
||||||
|
await getEndpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await queryTraces();
|
||||||
|
}
|
||||||
|
function changeCondition() {
|
||||||
|
if (conditions.value === "latency") {
|
||||||
|
currentLatency.value = filters.latency ? filters.latency[0].data : [];
|
||||||
|
}
|
||||||
|
queryTraces();
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeLatency(options: any[]) {
|
||||||
|
currentLatency.value = options[0].data;
|
||||||
|
queryTraces();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getService() {
|
||||||
|
const resp = await traceStore.getService(filters.id);
|
||||||
|
if (resp.errors) {
|
||||||
|
ElMessage.error(resp.errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.service = (resp.data.service && resp.data.service) || "";
|
||||||
|
}
|
||||||
|
async function getEndpoint() {
|
||||||
|
const resp = await traceStore.getEndpoint(filters.id);
|
||||||
|
if (resp.errors) {
|
||||||
|
ElMessage.error(resp.errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.endpoint = (resp.data.endpoint && resp.data.endpoint.id) || "";
|
||||||
|
}
|
||||||
|
async function getInstance() {
|
||||||
|
const resp = await traceStore.getInstance(filters.id);
|
||||||
|
if (resp.errors) {
|
||||||
|
ElMessage.error(resp.errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.instance = (resp.data.instance && resp.data.instance.id) || "";
|
||||||
|
}
|
||||||
|
function setCondition() {
|
||||||
|
let params: any = {
|
||||||
|
traceState: Status[0].value,
|
||||||
|
queryOrder: QueryOrders[0].value,
|
||||||
|
queryDuration: duration.value,
|
||||||
|
minTraceDuration: isNaN(currentLatency.value[0])
|
||||||
|
? undefined
|
||||||
|
: currentLatency.value[0] === currentLatency.value[1]
|
||||||
|
? currentLatency.value[0] - 10
|
||||||
|
: currentLatency.value[0],
|
||||||
|
maxTraceDuration:
|
||||||
|
isNaN(currentLatency.value[1]) || currentLatency.value[1] === Infinity
|
||||||
|
? undefined
|
||||||
|
: currentLatency.value[1],
|
||||||
|
tags: tagsMap.value.length ? tagsMap.value : undefined,
|
||||||
|
paging: { pageNum: 1, pageSize: 20 },
|
||||||
|
serviceId: state.service || undefined,
|
||||||
|
endpointId: state.endpoint || undefined,
|
||||||
|
serviceInstanceId: state.instance || undefined,
|
||||||
|
};
|
||||||
|
for (const k of items.value) {
|
||||||
|
if (k.label === conditions.value && FiltersKeys[k.label]) {
|
||||||
|
params[k.value] = filters[k.label];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isNaN(params.minTraceDuration)) {
|
||||||
|
params.queryOrder = QueryOrders[1].value;
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
async function queryTraces() {
|
||||||
|
traceStore.setTraceCondition(setCondition());
|
||||||
|
const res = await traceStore.getTraces();
|
||||||
|
if (res && res.errors) {
|
||||||
|
ElMessage.error(res.errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateTags(data: { tagsMap: Array<Option>; tagsList: string[] }) {
|
||||||
|
tagsList.value = data.tagsList;
|
||||||
|
tagsMap.value = data.tagsMap;
|
||||||
|
}
|
||||||
|
onUnmounted(() => {
|
||||||
|
traceStore.resetState();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.row {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-value {
|
||||||
|
padding: 0 5px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions-popup {
|
||||||
|
padding-left: 10px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
63
src/views/dashboard/related/trace/Index.vue
Normal file
63
src/views/dashboard/related/trace/Index.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License. -->
|
||||||
|
<template>
|
||||||
|
<div class="trace-wrapper flex-v">
|
||||||
|
<div class="header">
|
||||||
|
<Header :data="data" />
|
||||||
|
</div>
|
||||||
|
<div class="trace flex-h">
|
||||||
|
<TraceList />
|
||||||
|
<TraceDetail />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { provide } from "vue";
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
import Header from "./Header.vue";
|
||||||
|
import TraceList from "./TraceList.vue";
|
||||||
|
import TraceDetail from "./Detail.vue";
|
||||||
|
|
||||||
|
/*global defineProps */
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<any>,
|
||||||
|
default: () => ({ graph: {} }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
provide("options", props.data);
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.trace-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-bottom: 1px solid #dcdfe6;
|
||||||
|
min-width: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace {
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
min-width: 1200px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -58,8 +58,8 @@ limitations under the License. -->
|
|||||||
<span class="b">{{ i.endpointNames[0] }}</span>
|
<span class="b">{{ i.endpointNames[0] }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grey ell sm">
|
<div class="grey ell sm">
|
||||||
<span class="tag mr-10 sm">{{ i.duration }} ms</span
|
<span class="tag mr-10 sm"> {{ i.duration }} ms </span>
|
||||||
>{{ dateFormat(parseInt(i.start, 10)) }}
|
{{ dateFormat(parseInt(i.start, 10)) }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
Loading…
Reference in New Issue
Block a user