feat: add trace detail

This commit is contained in:
Qiuxia Fan 2022-02-23 18:05:36 +08:00
parent d2cf41c7d1
commit 6e170e987a
13 changed files with 335 additions and 48 deletions

View 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/issue-child</title><path d="M11 8H5v1h.7A1.3 1.3 0 0 1 7 10.3v2.4A1.3 1.3 0 0 1 5.7 14H3.3A1.3 1.3 0 0 1 2 12.7v-2.4a1.3 1.3 0 0 1 1-1.265V7a1 1 0 0 1 1-1h3V5H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v1h3a1 1 0 0 1 1 1v2.035a1.3 1.3 0 0 1 1 1.265v2.4a1.3 1.3 0 0 1-1.3 1.3h-2.4A1.3 1.3 0 0 1 9 12.7v-2.4A1.3 1.3 0 0 1 10.3 9h.7V8zm0 3v1h1v-1h-1zm-7 0v1h1v-1H4z" id="a"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/list-bulleted</title><path d="M3 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5.818 2h7.364c.452 0 .818.448.818 1s-.366 1-.818 1H5.818C5.366 4 5 3.552 5 3s.366-1 .818-1zm0 5h7.364c.452 0 .818.448.818 1s-.366 1-.818 1H5.818C5.366 9 5 8.552 5 8s.366-1 .818-1zm0 5h7.364c.452 0 .818.448.818 1s-.366 1-.818 1H5.818C5.366 14 5 13.552 5 13s.366-1 .818-1z" id="a"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View 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="1619507658599" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2073" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M331.840623 793.484755 331.840623 387.450978c0-16.191531-12.457456-28.645338-27.399836-28.645338L222.235197 358.80564c-14.94238 0-27.399836 13.698094-27.399836 28.645338L194.835361 793.484755 331.840623 793.484755 331.840623 793.484755zM506.210956 793.484755 506.210956 213.081861c0-16.192747-12.453808-29.89449-27.401052-29.89449l-82.20559 0c-14.94238 0-27.399836 13.701743-27.399836 29.89449L369.204478 793.484755 506.210956 793.484755 506.210956 793.484755zM680.580073 793.484755 680.580073 536.910048c0-16.191531-12.452591-29.889625-27.399836-29.889625L570.979512 507.020423c-14.947245 0-27.405918 13.698094-27.405918 29.889625L543.573595 793.484755 680.580073 793.484755 680.580073 793.484755zM854.94919 793.484755 854.94919 387.450978c0-16.191531-12.452591-28.645338-27.399836-28.645338l-82.200725 0c-14.947245 0-27.399836 13.698094-27.399836 28.645338L717.948794 793.484755 854.94919 793.484755 854.94919 793.484755zM879.860454 830.84861" p-id="2074" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

23
src/assets/icons/table.svg Executable file
View File

@ -0,0 +1,23 @@
<!-- 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="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="table" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="list-bulleted" fill="#FFFFFF" fill-rule="nonzero">
<path d="M1,2 C0.44771525,2 0,1.55228475 0,1 C0,0.44771525 0.44771525,0 1,0 C1.55228475,0 2,0.44771525 2,1 C2,1.55228475 1.55228475,2 1,2 Z M11,2 C10.4477153,2 10,1.55228475 10,1 C10,0.44771525 10.4477153,0 11,0 C11.5522847,0 12,0.44771525 12,1 C12,1.55228475 11.5522847,2 11,2 Z M8,2 C7.44771525,2 7,1.55228475 7,1 C7,0.44771525 7.44771525,0 8,0 C8.55228475,0 9,0.44771525 9,1 C9,1.55228475 8.55228475,2 8,2 Z M4,2 C3.44771525,2 3,1.55228475 3,1 C3,0.44771525 3.44771525,0 4,0 C4.55228475,0 5,0.44771525 5,1 C5,1.55228475 4.55228475,2 4,2 Z M1.09066667,5 L10.9093333,5 C11.512,5 12,5.448 12,6 C12,6.552 11.512,7 10.9093333,7 L1.09066667,7 C0.488,7 -1.77635684e-15,6.552 -1.77635684e-15,6 C-1.77635684e-15,5.448 0.488,5 1.09066667,5 Z M1.09066667,10 L10.9093333,10 C11.512,10 12,10.448 12,11 C12,11.552 11.512,12 10.9093333,12 L1.09066667,12 C0.488,12 -1.77635684e-15,11.552 -1.77635684e-15,11 C-1.77635684e-15,10.448 0.488,10 1.09066667,10 Z" id="a"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -30,7 +30,7 @@ interface TraceState {
traceList: Trace[]; traceList: Trace[];
traceTotal: number; traceTotal: number;
traceSpans: Span[]; traceSpans: Span[];
currentTrace: Nullable<Trace>; currentTrace: Trace | any;
conditions: any; conditions: any;
// traceSpanLogs: any[]; // traceSpanLogs: any[];
// traceSpanLogsTotal: number; // traceSpanLogsTotal: number;
@ -49,7 +49,7 @@ export const traceStore = defineStore({
traceList: [], traceList: [],
traceSpans: [], traceSpans: [],
traceTotal: 0, traceTotal: 0,
currentTrace: null, currentTrace: {},
conditions: { conditions: {
queryDuration: useAppStoreWithOut().durationTime, queryDuration: useAppStoreWithOut().durationTime,
traceState: "ALL", traceState: "ALL",
@ -103,10 +103,16 @@ export const traceStore = defineStore({
if (res.data.errors) { if (res.data.errors) {
return res.data; return res.data;
} }
this.traceList = res.data.data.data.traces; 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.traceTotal = res.data.data.data.total; this.traceTotal = res.data.data.data.total;
this.setCurrentTrace(res.data.data.data.traces[0] || {});
}, },
async getTraceSpans(params: any) { async getTraceSpans(params: { traceId: string }) {
const res: AxiosResponse = await graphql const res: AxiosResponse = await graphql
.query("queryTrace") .query("queryTrace")
.params(params); .params(params);

View File

@ -95,6 +95,10 @@
background-color: #a7aebb; background-color: #a7aebb;
} }
.mt-0 {
margin-top: 0;
}
.mb-5 { .mb-5 {
margin-bottom: 5px; margin-bottom: 5px;
} }

View File

@ -21,7 +21,7 @@ export interface Trace {
key: string; key: string;
operationNames: string[]; operationNames: string[];
start: string; start: string;
traceIds: string[]; traceIds: Array<string | any>;
} }
export interface Span { export interface Span {

View File

@ -13,12 +13,13 @@ 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>
<div class="trace-list"> <div class="trace-wrapper flex-v">
<div class="header"> <div class="header">
<Filter /> <Filter />
</div> </div>
<div class="content flex-h"> <div class="trace flex-h">
<TraceList /> <TraceList />
<Content />
</div> </div>
</div> </div>
</template> </template>
@ -26,9 +27,10 @@ limitations under the License. -->
import type { PropType } from "vue"; import type { PropType } from "vue";
import Filter from "../related/trace/Filter.vue"; import Filter from "../related/trace/Filter.vue";
import TraceList from "../related/trace/TraceList.vue"; import TraceList from "../related/trace/TraceList.vue";
import Content from "../related/trace/Content.vue";
/*global defineProps */ /*global defineProps */
const props = defineProps({ defineProps({
data: { data: {
type: Object as PropType<any>, type: Object as PropType<any>,
default: () => ({ graph: {} }), default: () => ({ graph: {} }),
@ -37,9 +39,10 @@ const props = defineProps({
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.trace-list { .trace-wrapper {
width: 100%;
height: 100%; height: 100%;
overflow: auto; font-size: 12px;
} }
.header { .header {
@ -48,7 +51,8 @@ const props = defineProps({
border-bottom: 1px solid #dcdfe6; border-bottom: 1px solid #dcdfe6;
} }
.content { .trace {
padding: 10px; width: 100%;
overflow: auto;
} }
</style> </style>

View File

@ -0,0 +1,18 @@
<!-- 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>
<Detail />
</template>
<script lang="ts" setup>
import Detail from "./components/Detail.vue";
</script>

View File

@ -48,12 +48,6 @@ limitations under the License. -->
<span class="grey mr-5">{{ t("traceID") }}:</span> <span class="grey mr-5">{{ t("traceID") }}:</span>
<el-input v-model="traceId" class="traceId" /> <el-input v-model="traceId" class="traceId" />
</div> </div>
<div class="mr-5">
<span class="sm b grey mr-5">{{ t("duration") }}:</span>
<el-input class="inputs mr-5" v-model="minTraceDuration" />
<span class="grey mr-5">-</span>
<el-input class="inputs" v-model="maxTraceDuration" />
</div>
</div> </div>
<div class="flex-h"> <div class="flex-h">
<!-- <div class="mr-5"> <!-- <div class="mr-5">
@ -65,6 +59,12 @@ limitations under the License. -->
@input="changeTimeRange" @input="changeTimeRange"
/> />
</div> --> </div> -->
<div class="mr-5">
<span class="sm b grey mr-5">{{ t("duration") }}:</span>
<el-input class="inputs mr-5" v-model="minTraceDuration" />
<span class="grey mr-5">-</span>
<el-input class="inputs" v-model="maxTraceDuration" />
</div>
<ConditionTags :type="'TRACE'" @update="updateTags" /> <ConditionTags :type="'TRACE'" @update="updateTags" />
<el-button <el-button
class="search-btn" class="search-btn"
@ -125,7 +125,7 @@ function searchTraces() {
} }
async function queryTraces() { async function queryTraces() {
const res = await traceStore.getTraces(); const res = await traceStore.getTraces();
if (res.errors) { if (res && res.errors) {
ElMessage.error(res.errors); ElMessage.error(res.errors);
} }
} }

View File

@ -19,8 +19,10 @@ limitations under the License. -->
:small="true" :small="true"
layout="prev, pager, next, jumper" layout="prev, pager, next, jumper"
:total="traceStore.traceTotal" :total="traceStore.traceTotal"
v-model:pager-count="pageCount"
@current-change="updatePage" @current-change="updatePage"
/> />
<div class="selectors">
<Selector <Selector
size="small" size="small"
v-model="traceStore.conditions.queryOrder" v-model="traceStore.conditions.queryOrder"
@ -29,11 +31,12 @@ limitations under the License. -->
@change="changeSort" @change="changeSort"
/> />
</div> </div>
</div>
<div class="trace-t-loading" v-loading="loading"> <div class="trace-t-loading" v-loading="loading">
<Icon iconName="spinner" size="sm" /> <Icon iconName="spinner" size="sm" />
</div> </div>
<div class="trace-t-wrapper scroll_hide"> <div class="trace-t-wrapper">
<table class="trace-t"> <table class="list">
<tr <tr
class="trace-tr cp" class="trace-tr cp"
v-for="(i, index) in traceStore.traceList" v-for="(i, index) in traceStore.traceList"
@ -59,7 +62,7 @@ limitations under the License. -->
</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
>{{ parseInt(i.start, 10) }} >{{ dateFormat(parseInt(i.start, 10)) }}
</div> </div>
</td> </td>
</tr> </tr>
@ -69,6 +72,7 @@ limitations under the License. -->
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import dayjs from "dayjs";
import { ref } from "vue"; import { ref } from "vue";
import { useTraceStore } from "@/store/modules/trace"; import { useTraceStore } from "@/store/modules/trace";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
@ -80,6 +84,9 @@ const traceStore = useTraceStore();
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
const selectedKey = ref<string>(""); const selectedKey = ref<string>("");
const pageSize = ref<number>(15); const pageSize = ref<number>(15);
const pageCount = ref<number>(5);
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
dayjs(date).format(pattern);
function searchTrace() { function searchTrace() {
loading.value = true; loading.value = true;
@ -89,8 +96,7 @@ function searchTrace() {
function updatePage(p: number) { function updatePage(p: number) {
traceStore.setCondition({ traceStore.setCondition({
type: "paging", paging: { pageNum: p, pageSize: 15, needTotal: true },
data: { pageNum: p, pageSize: 15, needTotal: true },
}); });
searchTrace(); searchTrace();
} }
@ -120,33 +126,26 @@ async function queryTraces() {
</script> </script>
<style lang="scss"> <style lang="scss">
.trace-t-tool { .trace-t-tool {
flex-shrink: 0;
background-color: rgba(196, 200, 225, 0.2); background-color: rgba(196, 200, 225, 0.2);
justify-content: space-between; justify-content: space-between;
padding-right: 5px;
padding-top: 1px;
border-bottom: 1px solid #c1c5ca41; border-bottom: 1px solid #c1c5ca41;
border-right: 1px solid #c1c5ca41; border-right: 1px solid #c1c5ca41;
height: 35px; height: 35px;
}
select { .selectors {
background-color: rgba(0, 0, 0, 0); margin: 2px 2px 0 0;
outline: 0;
border-style: unset;
margin: 0 10px;
}
} }
.trace-t-wrapper { .trace-t-wrapper {
overflow: auto; overflow: auto;
flex-grow: 1;
border-right: 1px solid rgba(0, 0, 0, 0.1); border-right: 1px solid rgba(0, 0, 0, 0.1);
} }
.trace-t-loading { .trace-t-loading {
text-align: center; text-align: center;
position: absolute; position: absolute;
width: 100%; width: 420px;
height: 70px; height: 70px;
margin-top: 40px; margin-top: 40px;
line-height: 88px; line-height: 88px;
@ -159,11 +158,11 @@ async function queryTraces() {
} }
.trace-t { .trace-t {
width: 100%; width: 420px;
border-spacing: 0; }
table-layout: fixed;
flex-grow: 1; .list {
position: relative; width: 400px;
} }
.trace-tr { .trace-tr {
@ -173,7 +172,7 @@ async function queryTraces() {
} }
.trace-td { .trace-td {
padding: 8px 10px; padding: 5px;
border-bottom: 1px solid rgba(0, 0, 0, 0.07); border-bottom: 1px solid rgba(0, 0, 0, 0.07);
&.selected { &.selected {

View File

@ -0,0 +1,186 @@
<!-- 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-detail-wrapper clear"
v-if="traceStore.currentTrace.endpointNames"
>
<h5 class="mb-5 mt-0">
<Icon
icon="clear"
v-if="traceStore.currentTrace.isError"
class="red mr-5 sm"
/>
<span class="vm">{{ traceStore.currentTrace.endpointNames[0] }}</span>
<!-- <div class="trace-log-btn bg-blue r mr-10" @click="searchTraceLogs">
{{ t("viewLogs") }}
</div> -->
</h5>
<div class="mb-5 blue sm">
<Selector
size="small"
v-model="traceId"
:options="traceStore.currentTrace.traceIds"
@change="changeTraceId"
class="trace-detail-ids"
/>
<Icon
size="sm"
class="icon grey link-hover cp ml-5"
iconName="review-list"
@click="handleClick"
/>
</div>
<div class="flex-h item">
<div>
<div class="tag mr-5">{{ t("start") }}</div>
<span class="mr-15 sm">{{
dateFormat(parseInt(traceStore.currentTrace.start))
}}</span>
<div class="tag mr-5">{{ t("duration") }}</div>
<span class="mr-15 sm">{{ traceStore.currentTrace.duration }} ms</span>
<div class="tag mr-5">{{ t("spans") }}</div>
<span class="sm">{{ traceStore.traceSpans.length }}</span>
</div>
<div>
<el-button
class="grey"
:class="{ ghost: displayMode !== 'list' }"
@click="displayMode = 'list'"
>
<Icon class="mr-5" size="sm" iconName="list-bulleted" />
{{ t("list") }}
</el-button>
<el-button
class="grey"
:class="{ ghost: displayMode !== 'tree' }"
@click="displayMode = 'tree'"
>
<Icon class="mr-5" size="sm" iconName="issue-child" />
{{ t("tree") }}
</el-button>
<el-button
class="grey"
:class="{ ghost: displayMode !== 'table' }"
@click="displayMode = 'table'"
>
<Icon class="mr-5" size="sm" iconName="table" />
{{ t("table") }}
</el-button>
<el-button
class="grey"
:class="{ ghost: displayMode !== 'statistics' }"
@click="displayMode = 'statistics'"
>
<Icon class="mr-5" size="sm" iconName="statistics-bulleted" />
{{ t("statistics") }}
</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import dayjs from "dayjs";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { useTraceStore } from "@/store/modules/trace";
import copy from "@/utils/copy";
const { t } = useI18n();
const traceStore = useTraceStore();
const traceId = ref<string>("");
const displayMode = ref<string>("list");
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
dayjs(date).format(pattern);
function handleClick(ids: string[]) {
let copyValue = null;
if (ids.length === 1) {
copyValue = ids[0];
} else {
copyValue = ids.join(",");
}
copy(copyValue);
}
function changeTraceId(i: string) {
traceId.value = i;
traceStore.getTraceSpans({ traceId: i });
}
// function searchTraceLogs() {
// this.showTraceLogs = true;
// this.GET_TRACE_SPAN_LOGS({
// condition: {
// relatedTrace: {
// traceId: this.traceId || traceStore.current.traceIds[0],
// },
// paging: { pageNum: this.pageNum, pageSize: this.pageSize, needTotal: true },
// },
// });
// }
</script>
<style lang="scss" scoped>
.trace-detail-wrapper {
font-size: 12px;
padding: 5px 10px;
border-bottom: 1px solid #eee;
width: 100%;
height: 95px;
.grey {
color: #fff;
background-color: #448dfe;
}
.ghost {
cursor: pointer;
background: rgba(0, 0, 0, 0.3);
}
}
.item {
justify-content: space-between;
}
.trace-detail-ids {
background-color: rgba(0, 0, 0, 0);
outline: 0;
border-style: unset;
color: inherit;
border: 1px solid;
border-radius: 4px;
margin: 0 10px;
}
.trace-log-btn {
padding: 3px 9px;
background-color: #484b55;
border-radius: 4px;
color: #eee;
font-weight: normal;
cursor: pointer;
&.bg-blue {
background-color: #448dfe;
}
}
.tag {
display: inline-block;
border-radius: 4px;
padding: 0px 7px;
background-color: #40454e;
color: #eee;
}
</style>