fix: trace

This commit is contained in:
Fine 2024-01-29 18:27:32 +08:00
parent 7d24e065e9
commit 1fde7188f2
6 changed files with 239 additions and 47 deletions

View File

@ -16,7 +16,7 @@
*/ */
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import type { LayoutConfig } from "@/types/dashboard"; import type { LayoutConfig, DashboardItem } from "@/types/dashboard";
import { ConfigFieldTypes } from "@/views/dashboard/data"; import { ConfigFieldTypes } from "@/views/dashboard/data";
export default function getDashboard(param?: { name?: string; layer: string; entity: string }, t?: string) { export default function getDashboard(param?: { name?: string; layer: string; entity: string }, t?: string) {
@ -24,7 +24,7 @@ export default function getDashboard(param?: { name?: string; layer: string; ent
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const opt = param || dashboardStore.currentDashboard; const opt = param || dashboardStore.currentDashboard;
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]"); const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
let dashboard; let dashboard: Nullable<DashboardItem>;
if (type === ConfigFieldTypes.NAME) { if (type === ConfigFieldTypes.NAME) {
dashboard = list.find( dashboard = list.find(
(d: { name: string; layer: string; entity: string }) => (d: { name: string; layer: string; entity: string }) =>
@ -62,6 +62,9 @@ export default function getDashboard(param?: { name?: string; layer: string; ent
filters, filters,
}; };
dashboardStore.setWidget(item); dashboardStore.setWidget(item);
if (widget.id === sourceId) {
return;
}
const targetTabIndex = (widget.id || "").split("-"); const targetTabIndex = (widget.id || "").split("-");
const sourceTabindex = (sourceId || "").split("-") || []; const sourceTabindex = (sourceId || "").split("-") || [];
let container: Nullable<Element>; let container: Nullable<Element>;

View File

@ -167,7 +167,144 @@ export const traceStore = defineStore({
if (res.data.errors) { if (res.data.errors) {
return res.data; return res.data;
} }
const data = res.data.data.trace.spans; const resp = {
data: {
trace: {
spans: [
{
traceId: "d013fdb11b56417a9997db9d56722ce2.57.17056716790540001",
segmentId: "d013fdb11b56417a9997db9d56722ce2.57.17056716790540000",
spanId: 0,
parentSpanId: -1,
refs: [],
serviceCode: "rmq5-grpc-A-scenario",
serviceInstanceName: "3d4a5d93c2af47838948691e07c502e6@198.18.0.1",
startTime: 1705671679055,
endTime: 1705671679135,
endpointName: "GET:/case/rocketmq-5-grpc-scenario",
type: "Entry",
peer: "",
component: "SpringMVC",
isError: false,
layer: "Http",
tags: [
{
key: "url",
value: "http://127.0.0.1:8080/rocketmq-5-grpc-scenario/case/rocketmq-5-grpc-scenario",
},
{
key: "http.method",
value: "GET",
},
{
key: "http.status_code",
value: "200",
},
],
logs: [],
attachedEvents: [],
},
{
traceId: "d013fdb11b56417a9997db9d56722ce2.57.17056716790540001",
segmentId: "d013fdb11b56417a9997db9d56722ce2.57.17056716790540000",
spanId: 1,
parentSpanId: 0,
refs: [],
serviceCode: "rmq5-grpc-A-scenario",
serviceInstanceName: "3d4a5d93c2af47838948691e07c502e6@198.18.0.1",
startTime: 1705671679057,
endTime: 1705671679133,
endpointName: "RocketMQ/ConsumerAsyncTopicTest/Producer",
type: "Exit",
peer: "123.56.194.74:8081",
component: "rocketMQ-producer",
isError: false,
layer: "MQ",
tags: [
{
key: "mq.broker",
value: "123.56.194.74:8081",
},
{
key: "mq.topic",
value: "ConsumerAsyncTopicTest",
},
{
key: "mq.message.keys",
value: "e8029111-fa26-4738-a59f-2d4dab0b2ffe",
},
{
key: "mq.message.tags",
value: "Tag:async:consumer",
},
{
key: "mq.message.id",
value: "01ACDE480011221E0005BC15FF00000003",
},
],
logs: [],
attachedEvents: [],
},
{
traceId: "d013fdb11b56417a9997db9d56722ce2.57.17056716790540001",
segmentId: "1c116740e8dd41f9af3577a03c969a4b.53.17056716950020000",
spanId: 0,
parentSpanId: -1,
refs: [
{
traceId: "d013fdb11b56417a9997db9d56722ce2.57.17056716790540001",
parentSegmentId: "d013fdb11b56417a9997db9d56722ce2.57.17056716790540000",
parentSpanId: 1,
type: "CROSS_PROCESS",
},
{
traceId: "d013fdb11b56417a9997db9d56722ce2.55.17056716721260001",
parentSegmentId: "d013fdb11b56417a9997db9d56722ce2.55.17056716721250000",
parentSpanId: 1,
type: "CROSS_PROCESS",
},
],
serviceCode: "rmq5-grpc-B-scenario",
serviceInstanceName: "88429d7fb3434b928dc39f41c0b7c4c6@198.18.0.1",
startTime: 1705671695004,
endTime: 1705671696483,
endpointName: "RocketMQ/ConsumerAsyncTopicTest/Consumer",
type: "Entry",
peer: "123.56.194.74:8081",
component: "rocketMQ-consumer",
isError: false,
layer: "MQ",
tags: [
{
key: "mq.topic",
value: "ConsumerAsyncTopicTest",
},
{
key: "mq.broker",
value: "123.56.194.74:8081",
},
{
key: "transmission.latency",
value: "17345",
},
{
key: "transmission.latency",
value: "24263",
},
{
key: "http.status_code",
value: "200",
},
],
logs: [],
attachedEvents: [],
},
],
},
},
};
// const data = res.data.data.trace.spans;
const data = resp.data.trace.spans;
this.setTraceSpans(data || []); this.setTraceSpans(data || []);
return res.data; return res.data;
}, },

View File

@ -71,6 +71,7 @@ limitations under the License. -->
emit("select", props.data); emit("select", props.data);
} }
function linkTrace(id: string) { function linkTrace(id: string) {
console.log(options);
const { associationWidget } = getDashboard(dashboardStore.currentDashboard); const { associationWidget } = getDashboard(dashboardStore.currentDashboard);
associationWidget( associationWidget(
(options.id as any) || "", (options.id as any) || "",

View File

@ -227,7 +227,8 @@ limitations under the License. -->
.no-data { .no-data {
padding-top: 50px; padding-top: 50px;
width: 100%; width: 280px;
text-align: center; text-align: center;
height: 100px;
} }
</style> </style>

View File

@ -139,11 +139,18 @@ limitations under the License. -->
} }
segmentHeaders.forEach((span: Span) => { segmentHeaders.forEach((span: Span) => {
if (span.refs.length) { if (span.refs.length) {
let exit = 0;
span.refs.forEach((ref) => { span.refs.forEach((ref) => {
const index = props.data.findIndex( const i = props.data.findIndex(
(i: Recordable) => ref.parentSegmentId === i.segmentId && ref.parentSpanId === i.spanId, (i: Recordable) => ref.parentSegmentId === i.segmentId && ref.parentSpanId === i.spanId,
); );
if (index === -1) { if (i > -1) {
exit = 1;
}
});
if (!exit) {
const ref = span.refs[0];
// create a known broken node. // create a known broken node.
const i = ref.parentSpanId; const i = ref.parentSpanId;
const fixSpanKeyContent = { const fixSpanKeyContent = {
@ -198,16 +205,25 @@ limitations under the License. -->
} }
} }
} }
});
} }
}); });
[...fixSpans, ...props.data].forEach((i) => { [...fixSpans, ...props.data].forEach((i) => {
if (i.refs.length) {
const item = i.refs.find((d) => d.traceId === i.traceId);
if (item) {
i = {
...i,
...item,
segmentId: item.parentSegmentId,
spanId: -2,
};
}
}
i.label = i.endpointName || "no operation name"; i.label = i.endpointName || "no operation name";
i.children = []; i.children = [];
if (segmentGroup[i.segmentId] === undefined) { if (!segmentGroup[i.segmentId]) {
segmentIdGroup.push(i.segmentId); segmentIdGroup.push(i.segmentId);
segmentGroup[i.segmentId] = []; segmentGroup[i.segmentId] = [i];
segmentGroup[i.segmentId].push(i);
} else { } else {
segmentGroup[i.segmentId].push(i); segmentGroup[i.segmentId].push(i);
} }

View File

@ -42,6 +42,13 @@ limitations under the License. -->
<span class="g-sm-4 grey">{{ t("isError") }}:</span> <span class="g-sm-4 grey">{{ t("isError") }}:</span>
<span class="g-sm-8 wba">{{ currentSpan.isError }}</span> <span class="g-sm-8 wba">{{ currentSpan.isError }}</span>
</div> </div>
<h5 class="mb-10" v-if="diffRefs.length"> {{ t("traceID") }}. </h5>
<div class="mb-10 clear item" v-for="(item, index) in diffRefs" :key="item.traceId">
<span class="g-sm-4 grey">No.{{ index + 1 }}</span>
<span class="g-sm-8 wba link cp" @click="viewRelateTrace(item)">
{{ item.traceId }}
</span>
</div>
<h5 class="mb-10" v-if="currentSpan.tags && currentSpan.tags.length"> {{ t("tags") }}. </h5> <h5 class="mb-10" v-if="currentSpan.tags && currentSpan.tags.length"> {{ t("tags") }}. </h5>
<div class="mb-10 clear item" v-for="i in currentSpan.tags" :key="i.key"> <div class="mb-10 clear item" v-for="i in currentSpan.tags" :key="i.key">
<span class="g-sm-4 grey">{{ i.key }}:</span> <span class="g-sm-4 grey">{{ i.key }}:</span>
@ -127,7 +134,7 @@ limitations under the License. -->
</el-dialog> </el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, onMounted } from "vue"; import { ref, computed, onMounted, inject } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import type { PropType } from "vue"; import type { PropType } from "vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -138,14 +145,20 @@ limitations under the License. -->
import { useTraceStore } from "@/store/modules/trace"; import { useTraceStore } from "@/store/modules/trace";
import LogTable from "@/views/dashboard/related/log/LogTable/Index.vue"; import LogTable from "@/views/dashboard/related/log/LogTable/Index.vue";
import type { SpanAttachedEvent } from "@/types/trace"; import type { SpanAttachedEvent } from "@/types/trace";
import getDashboard from "@/hooks/useDashboardsSession";
import { useDashboardStore } from "@/store/modules/dashboard";
import { WidgetType } from "@/views/dashboard/data";
import type { LayoutConfig } from "@/types/dashboard";
/*global defineProps, Nullable, Recordable */ /*global defineProps, Nullable, Recordable */
const props = defineProps({ const props = defineProps({
currentSpan: { type: Object as PropType<Recordable>, default: () => ({}) }, currentSpan: { type: Object as PropType<Recordable>, default: () => ({}) },
traceId: { type: String, default: "" }, traceId: { type: String, default: "" },
}); });
const options: Recordable<LayoutConfig> = inject("options") || {};
const { t } = useI18n(); const { t } = useI18n();
const traceStore = useTraceStore(); const traceStore = useTraceStore();
const dashboardStore = useDashboardStore();
const pageNum = ref<number>(1); const pageNum = ref<number>(1);
const showRelatedLogs = ref<boolean>(false); const showRelatedLogs = ref<boolean>(false);
const showEventDetail = ref<boolean>(false); const showEventDetail = ref<boolean>(false);
@ -154,6 +167,9 @@ limitations under the License. -->
const total = computed(() => const total = computed(() =>
traceStore.traceList.length === pageSize ? pageSize * pageNum.value + 1 : pageSize * pageNum.value, traceStore.traceList.length === pageSize ? pageSize * pageNum.value + 1 : pageSize * pageNum.value,
); );
const diffRefs = computed(() =>
props.currentSpan.refs.filter((d: Recordable) => d.traceId !== props.currentSpan.traceId),
);
const tree = ref<any>(null); const tree = ref<any>(null);
const eventGraph = ref<Nullable<HTMLDivElement>>(null); const eventGraph = ref<Nullable<HTMLDivElement>>(null);
const visDate = (date: number, pattern = "YYYY-MM-DD HH:mm:ss:SSS") => dayjs(date).format(pattern); const visDate = (date: number, pattern = "YYYY-MM-DD HH:mm:ss:SSS") => dayjs(date).format(pattern);
@ -217,6 +233,18 @@ limitations under the License. -->
tree.value.draw(); tree.value.draw();
} }
function viewRelateTrace(item: Recordable) {
const { associationWidget } = getDashboard(dashboardStore.currentDashboard);
associationWidget(
(options.id as any) || "",
{
sourceId: options.id || "",
traceId: item.traceId,
},
WidgetType.Trace,
);
}
function selectEvent(i: any) { function selectEvent(i: any) {
currentEvent.value = i.data; currentEvent.value = i.data;
showEventDetail.value = true; showEventDetail.value = true;
@ -259,7 +287,13 @@ limitations under the License. -->
cursor: pointer; cursor: pointer;
} }
.link,
.link-hover:hover { .link-hover:hover {
color: #448dfe; color: var(--el-color-primary);
}
.link {
color: var(--el-color-primary);
text-decoration: underline;
} }
</style> </style>