From 493ec4bec2df69dcb17efb644094ac10f403f035 Mon Sep 17 00:00:00 2001 From: Qiuxia Fan Date: Mon, 18 Apr 2022 17:22:43 +0800 Subject: [PATCH] add ebpf --- src/components/Radio.vue | 4 +- src/store/modules/ebpf.ts | 227 ++++++++++++++++++ src/store/modules/profile.ts | 14 ++ src/views/dashboard/controls/Ebpf.vue | 4 +- src/views/dashboard/controls/Tab.vue | 3 +- src/views/dashboard/controls/index.ts | 3 +- src/views/dashboard/related/ebpf/Content.vue | 17 ++ src/views/dashboard/related/ebpf/Header.vue | 133 ++++++++++ .../related/ebpf/components/NewTask.vue | 203 ++++++++++++++++ .../dashboard/related/ebpf/components/data.ts | 63 +++++ .../related/profile/components/NewTask.vue | 31 ++- 11 files changed, 694 insertions(+), 8 deletions(-) create mode 100644 src/views/dashboard/related/ebpf/Content.vue create mode 100644 src/views/dashboard/related/ebpf/Header.vue create mode 100644 src/views/dashboard/related/ebpf/components/NewTask.vue create mode 100644 src/views/dashboard/related/ebpf/components/data.ts diff --git a/src/components/Radio.vue b/src/components/Radio.vue index f5aee030..7eafaa4e 100644 --- a/src/components/Radio.vue +++ b/src/components/Radio.vue @@ -32,7 +32,7 @@ interface Option { const emit = defineEmits(["change"]); const props = defineProps({ options: { - type: Array as PropType<(Option & { disabled: boolean })[]>, + type: Array as PropType, default: () => [], }, value: { @@ -44,7 +44,7 @@ const props = defineProps({ const selected = ref(props.value); -function checked(opt: string) { +function checked(opt: unknown) { emit("change", opt); } diff --git a/src/store/modules/ebpf.ts b/src/store/modules/ebpf.ts index e69de29b..75cbe19e 100644 --- a/src/store/modules/ebpf.ts +++ b/src/store/modules/ebpf.ts @@ -0,0 +1,227 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { defineStore } from "pinia"; +import { Duration, Option } from "@/types/app"; +import { Endpoint } from "@/types/selector"; +import { + TaskListItem, + SegmentSpan, + ProfileAnalyzationTrees, + TaskLog, + ProfileTaskCreationRequest, +} from "@/types/profile"; +import { Trace, Span } from "@/types/trace"; +import { store } from "@/store"; +import graphql from "@/graphql"; +import { AxiosResponse } from "axios"; +import { useAppStoreWithOut } from "@/store/modules/app"; + +interface EbpfStore { + endpoints: Endpoint[]; + taskEndpoints: Endpoint[]; + durationTime: Duration; + condition: { serviceId: string; endpointName: string }; + taskList: TaskListItem[]; + segmentList: Trace[]; + currentSegment: Trace | Record; + segmentSpans: SegmentSpan[]; + currentSpan: SegmentSpan | Record; + analyzeTrees: ProfileAnalyzationTrees; + taskLogs: TaskLog[]; + highlightTop: boolean; + labels: Option[]; +} + +export const ebpfStore = defineStore({ + id: "profile", + state: (): EbpfStore => ({ + endpoints: [{ value: "", label: "All" }], + taskEndpoints: [{ value: "", label: "All" }], + durationTime: useAppStoreWithOut().durationTime, + condition: { serviceId: "", endpointName: "" }, + taskList: [], + segmentList: [], + currentSegment: {}, + segmentSpans: [], + currentSpan: {}, + analyzeTrees: [], + taskLogs: [], + highlightTop: true, + labels: [{ value: "", label: "" }], + }), + actions: { + setConditions(data: { serviceId?: string; endpointName?: string }) { + this.condition = { + ...this.condition, + ...data, + }; + }, + setCurrentSpan(span: Span) { + this.currentSpan = span; + }, + setCurrentSegment(s: Trace) { + this.currentSegment = s; + }, + setHighlightTop() { + this.highlightTop = !this.highlightTop; + }, + async getEndpoints(serviceId: string, keyword?: string) { + const res: AxiosResponse = await graphql.query("queryEndpoints").params({ + serviceId, + duration: this.durationTime, + keyword: keyword || "", + }); + if (res.data.errors) { + return res.data; + } + this.endpoints = [{ value: "", label: "All" }, ...res.data.data.pods]; + return res.data; + }, + async getTaskEndpoints(serviceId: string, keyword?: string) { + const res: AxiosResponse = await graphql.query("queryEndpoints").params({ + serviceId, + duration: this.durationTime, + keyword: keyword || "", + }); + if (res.data.errors) { + return res.data; + } + this.taskEndpoints = [{ value: "", label: "All" }, ...res.data.data.pods]; + return res.data; + }, + async getTaskList() { + const res: AxiosResponse = await graphql + .query("getProfileTaskList") + .params(this.condition); + + if (res.data.errors) { + return res.data; + } + const list = res.data.data.taskList; + this.taskList = list; + if (!list.length) { + this.segmentList = []; + this.segmentSpans = []; + this.analyzeTrees = []; + + return res.data; + } + this.getSegmentList({ taskID: list[0].id }); + return res.data; + }, + async getSegmentList(params: { taskID: string }) { + const res: AxiosResponse = await graphql + .query("getProfileTaskSegmentList") + .params(params); + + if (res.data.errors) { + this.segmentList = []; + return res.data; + } + const { segmentList } = res.data.data; + + this.segmentList = segmentList; + if (!segmentList.length) { + this.segmentSpans = []; + this.analyzeTrees = []; + + return res.data; + } + if (segmentList[0]) { + this.currentSegment = segmentList[0]; + this.getSegmentSpans({ segmentId: segmentList[0].segmentId }); + } else { + this.currentSegment = null; + } + return res.data; + }, + async getSegmentSpans(params: { segmentId: string }) { + const res: AxiosResponse = await graphql + .query("queryProfileSegment") + .params(params); + if (res.data.errors) { + this.segmentSpans = []; + return res.data; + } + const { segment } = res.data.data; + if (!segment) { + this.segmentSpans = []; + this.analyzeTrees = []; + return res.data; + } + this.segmentSpans = segment.spans; + if (!(segment.spans && segment.spans.length)) { + this.analyzeTrees = []; + return res.data; + } + const index = segment.spans.length - 1 || 0; + this.currentSpan = segment.spans[index]; + return res.data; + }, + async getProfileAnalyze(params: { + segmentId: string; + timeRanges: Array<{ start: number; end: number }>; + }) { + const res: AxiosResponse = await graphql + .query("getProfileAnalyze") + .params(params); + + if (res.data.errors) { + this.analyzeTrees = []; + return res.data; + } + const { analyze, tip } = res.data.data; + if (tip) { + this.analyzeTrees = []; + return res.data; + } + + if (!analyze) { + this.analyzeTrees = []; + return res.data; + } + this.analyzeTrees = analyze.trees; + return res.data; + }, + async createTask(param: ProfileTaskCreationRequest) { + const res: AxiosResponse = await graphql + .query("saveProfileTask") + .params({ creationRequest: param }); + + if (res.data.errors) { + return res.data; + } + this.getTaskList(); + return res.data; + }, + async getTaskLogs(param: { taskID: string }) { + const res: AxiosResponse = await graphql + .query("getProfileTaskLogs") + .params(param); + + if (res.data.errors) { + return res.data; + } + this.taskLogs = res.data.data.taskLogs; + return res.data; + }, + }, +}); + +export function useEbpfStore(): any { + return ebpfStore(store); +} diff --git a/src/store/modules/profile.ts b/src/store/modules/profile.ts index 7020adc0..9209e395 100644 --- a/src/store/modules/profile.ts +++ b/src/store/modules/profile.ts @@ -32,6 +32,7 @@ import { useAppStoreWithOut } from "@/store/modules/app"; interface ProfileState { endpoints: Endpoint[]; + taskEndpoints: Endpoint[]; durationTime: Duration; condition: { serviceId: string; endpointName: string }; taskList: TaskListItem[]; @@ -48,6 +49,7 @@ export const profileStore = defineStore({ id: "profile", state: (): ProfileState => ({ endpoints: [{ value: "", label: "All" }], + taskEndpoints: [{ value: "", label: "All" }], durationTime: useAppStoreWithOut().durationTime, condition: { serviceId: "", endpointName: "" }, taskList: [], @@ -87,6 +89,18 @@ export const profileStore = defineStore({ this.endpoints = [{ value: "", label: "All" }, ...res.data.data.pods]; return res.data; }, + async getTaskEndpoints(serviceId: string, keyword?: string) { + const res: AxiosResponse = await graphql.query("queryEndpoints").params({ + serviceId, + duration: this.durationTime, + keyword: keyword || "", + }); + if (res.data.errors) { + return res.data; + } + this.taskEndpoints = [{ value: "", label: "All" }, ...res.data.data.pods]; + return res.data; + }, async getTaskList() { const res: AxiosResponse = await graphql .query("getProfileTaskList") diff --git a/src/views/dashboard/controls/Ebpf.vue b/src/views/dashboard/controls/Ebpf.vue index 1faca451..78f67ef7 100644 --- a/src/views/dashboard/controls/Ebpf.vue +++ b/src/views/dashboard/controls/Ebpf.vue @@ -37,8 +37,8 @@ limitations under the License. --> import type { PropType } from "vue"; import { useI18n } from "vue-i18n"; import { useDashboardStore } from "@/store/modules/dashboard"; -import Header from "../related/profile/Header.vue"; -import Content from "../related/profile/Content.vue"; +import Header from "../related/ebpf/Header.vue"; +import Content from "../related/ebpf/Content.vue"; /*global defineProps */ const props = defineProps({ diff --git a/src/views/dashboard/controls/Tab.vue b/src/views/dashboard/controls/Tab.vue index 7d153960..5e033c88 100644 --- a/src/views/dashboard/controls/Tab.vue +++ b/src/views/dashboard/controls/Tab.vue @@ -122,6 +122,7 @@ import Trace from "./Trace.vue"; import Profile from "./Profile.vue"; import Log from "./Log.vue"; import Text from "./Text.vue"; +import Ebpf from "./Ebpf.vue"; const props = { data: { @@ -132,7 +133,7 @@ const props = { }; export default defineComponent({ name: "Tab", - components: { Topology, Widget, Trace, Profile, Log, Text }, + components: { Topology, Widget, Trace, Profile, Log, Text, Ebpf }, props, setup(props) { const { t } = useI18n(); diff --git a/src/views/dashboard/controls/index.ts b/src/views/dashboard/controls/index.ts index 9829a0d3..01fc5dc5 100644 --- a/src/views/dashboard/controls/index.ts +++ b/src/views/dashboard/controls/index.ts @@ -21,5 +21,6 @@ import Trace from "./Trace.vue"; import Profile from "./Profile.vue"; import Log from "./Log.vue"; import Text from "./Text.vue"; +import Ebpf from "./Ebpf.vue"; -export default { Tab, Widget, Trace, Topology, Profile, Log, Text }; +export default { Tab, Widget, Trace, Topology, Profile, Log, Text, Ebpf }; diff --git a/src/views/dashboard/related/ebpf/Content.vue b/src/views/dashboard/related/ebpf/Content.vue new file mode 100644 index 00000000..f692e0de --- /dev/null +++ b/src/views/dashboard/related/ebpf/Content.vue @@ -0,0 +1,17 @@ + + diff --git a/src/views/dashboard/related/ebpf/Header.vue b/src/views/dashboard/related/ebpf/Header.vue new file mode 100644 index 00000000..a8af8c04 --- /dev/null +++ b/src/views/dashboard/related/ebpf/Header.vue @@ -0,0 +1,133 @@ + + + + diff --git a/src/views/dashboard/related/ebpf/components/NewTask.vue b/src/views/dashboard/related/ebpf/components/NewTask.vue new file mode 100644 index 00000000..310a4780 --- /dev/null +++ b/src/views/dashboard/related/ebpf/components/NewTask.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/src/views/dashboard/related/ebpf/components/data.ts b/src/views/dashboard/related/ebpf/components/data.ts new file mode 100644 index 00000000..a382e5a9 --- /dev/null +++ b/src/views/dashboard/related/ebpf/components/data.ts @@ -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. + */ +export const ProfileMode: any[] = [ + { label: "Include Children", value: "include" }, + { label: "Exclude Children", value: "exclude" }, +]; +export const NewTaskField = { + service: { key: "", label: "None" }, + monitorTime: { key: "0", label: "monitor now" }, + monitorDuration: { key: 5, label: "5 min" }, + minThreshold: 0, + dumpPeriod: { key: 10, label: "10ms" }, + endpointName: "", + maxSamplingCount: { key: 5, label: "5" }, +}; + +export const InitTaskField = { + serviceSource: [{ key: "", label: "None" }], + monitorTimeEn: [ + { value: "0", label: "monitor now" }, + { value: "1", label: "set start time" }, + ], + monitorTimeCn: [ + { value: "0", label: "此刻" }, + { value: "1", label: "设置时间" }, + ], + monitorDuration: [ + { value: "5", label: "5 min" }, + { value: "10", label: "10 min" }, + { value: "15", label: "15 min" }, + ], + dumpPeriod: [ + { value: "10", label: "10 ms" }, + { value: "20", label: "20 ms" }, + { value: "50", label: "50 ms" }, + { value: "100", label: "100 ms" }, + ], + maxSamplingCount: [ + { value: "1", label: "1" }, + { value: "2", label: "2" }, + { value: "3", label: "3" }, + { value: "4", label: "4" }, + { value: "5", label: "5" }, + { value: "6", label: "6" }, + { value: "7", label: "7" }, + { value: "8", label: "8" }, + { value: "9", label: "9" }, + ], +}; diff --git a/src/views/dashboard/related/profile/components/NewTask.vue b/src/views/dashboard/related/profile/components/NewTask.vue index f5c44010..c907bdbc 100644 --- a/src/views/dashboard/related/profile/components/NewTask.vue +++ b/src/views/dashboard/related/profile/components/NewTask.vue @@ -17,7 +17,16 @@ limitations under the License. -->
{{ t("endpointName") }}
- +
{{ t("monitorTime") }}
@@ -105,6 +114,20 @@ const minThreshold = ref(0); const dumpPeriod = ref(InitTaskField.dumpPeriod[0].value); const maxSamplingCount = ref(InitTaskField.maxSamplingCount[0].value); +async function searchEndpoints(keyword: string) { + if (!selectorStore.currentService) { + return; + } + const service = selectorStore.currentService.value; + const res = await profileStore.getEndpoints(service, keyword); + + if (res.errors) { + ElMessage.error(res.errors); + return; + } + endpointName.value = profileStore.taskEndpoints[0].value; +} + function changeMonitorTime(opt: string) { monitorTime.value = opt; } @@ -121,6 +144,10 @@ function changeMaxSamplingCount(opt: any[]) { maxSamplingCount.value = opt[0].value; } +function changeEndpoint(opt: any[]) { + endpointName.value = opt[0].value; +} + async function createTask() { emits("close"); const date = @@ -153,7 +180,7 @@ function changeTimeRange(val: Date) {