feat: create tasks

This commit is contained in:
Qiuxia Fan 2022-03-02 14:17:24 +08:00
parent eab42fa1c6
commit ff9da5a379
11 changed files with 358 additions and 66 deletions

50
src/components/Radio.vue Normal file
View File

@ -0,0 +1,50 @@
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<el-radio-group v-model="selected" @change="checked">
<el-radio v-for="item in options" :key="item.value" :label="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import type { PropType } from "vue";
interface Option {
label: string;
value: string;
}
/*global defineProps, defineEmits */
const emit = defineEmits(["change"]);
const props = defineProps({
options: {
type: Array as PropType<(Option & { disabled: boolean })[]>,
default: () => [],
},
value: {
type: String as PropType<string>,
default: "",
},
size: { type: null, default: "default" },
});
const selected = ref<string>(props.value);
function checked(opt: string) {
emit("change", opt);
}
</script>

View File

@ -76,42 +76,6 @@ watch(
); );
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.icon {
width: 16px;
height: 16px;
vertical-align: middle;
fill: currentColor;
&.sm {
width: 14px;
height: 14px;
}
&.middle {
width: 18px;
height: 18px;
}
&.lg {
width: 24px;
height: 24px;
}
&.loading {
animation: loading 1.5s linear infinite;
}
&.logo {
height: 30px;
width: 110px;
}
&.xl {
height: 30px;
width: 30px;
}
}
.el-input__inner { .el-input__inner {
border-radius: unset !important; border-radius: unset !important;
} }

View File

@ -18,6 +18,7 @@ import Icon from "./Icon.vue";
import TimePicker from "./TimePicker.vue"; import TimePicker from "./TimePicker.vue";
import Selector from "./Selector.vue"; import Selector from "./Selector.vue";
import Graph from "./Graph.vue"; import Graph from "./Graph.vue";
import Radio from "./Radio.vue";
import type { App } from "vue"; import type { App } from "vue";
import VueGridLayout from "vue-grid-layout"; import VueGridLayout from "vue-grid-layout";
@ -27,6 +28,7 @@ const components: { [key: string]: any } = {
VueGridLayout, VueGridLayout,
Selector, Selector,
Graph, Graph,
Radio,
}; };
const componentsName: string[] = Object.keys(components); const componentsName: string[] = Object.keys(components);

View File

@ -22,6 +22,7 @@ import {
SegmentSpan, SegmentSpan,
ProfileAnalyzationTrees, ProfileAnalyzationTrees,
TaskLog, TaskLog,
ProfileTaskCreationRequest,
} from "@/types/profile"; } from "@/types/profile";
import { Trace, Span } from "@/types/trace"; import { Trace, Span } from "@/types/trace";
import { store } from "@/store"; import { store } from "@/store";
@ -98,6 +99,10 @@ export const traceStore = defineStore({
const list = res.data.data.taskList; const list = res.data.data.taskList;
this.taskList = list; this.taskList = list;
if (!list.length) { if (!list.length) {
this.segmentList = [];
this.segmentSpans = [];
this.analyzeTrees = [];
return res.data; return res.data;
} }
this.getSegmentList({ taskID: list[0].id }); this.getSegmentList({ taskID: list[0].id });
@ -115,7 +120,12 @@ export const traceStore = defineStore({
const { segmentList } = res.data.data; const { segmentList } = res.data.data;
this.segmentList = segmentList; this.segmentList = segmentList;
if (!segmentList.length) {
this.segmentSpans = [];
this.analyzeTrees = [];
return res.data;
}
if (segmentList[0]) { if (segmentList[0]) {
this.currentSegment = segmentList[0]; this.currentSegment = segmentList[0];
this.getSegmentSpans({ segmentId: segmentList[0].segmentId }); this.getSegmentSpans({ segmentId: segmentList[0].segmentId });
@ -135,10 +145,12 @@ export const traceStore = defineStore({
const { segment } = res.data.data; const { segment } = res.data.data;
if (!segment) { if (!segment) {
this.segmentSpans = []; this.segmentSpans = [];
this.analyzeTrees = [];
return res.data; return res.data;
} }
this.segmentSpans = segment.spans; this.segmentSpans = segment.spans;
if (!(segment.spans && segment.spans.length)) { if (!(segment.spans && segment.spans.length)) {
this.analyzeTrees = [];
return res.data; return res.data;
} }
const index = segment.spans.length - 1 || 0; const index = segment.spans.length - 1 || 0;
@ -169,10 +181,10 @@ export const traceStore = defineStore({
this.analyzeTrees = analyze.trees; this.analyzeTrees = analyze.trees;
return res.data; return res.data;
}, },
async createTask(param: any) { async createTask(param: ProfileTaskCreationRequest) {
const res: AxiosResponse = await graphql const res: AxiosResponse = await graphql
.query("saveProfileTask") .query("saveProfileTask")
.params({ param }); .params({ creationRequest: param });
if (res.data.errors) { if (res.data.errors) {
return res.data; return res.data;

View File

@ -58,3 +58,13 @@ export interface SegmentSpan {
tags: any[]; tags: any[];
logs: any[]; logs: any[];
} }
export interface ProfileTaskCreationRequest {
serviceId: string;
endpointName: string;
startTime: number;
duration: number;
minDurationThreshold: number;
dumpPeriod: number;
maxSamplingCount: number;
}

View File

@ -214,7 +214,3 @@ export const QueryOrders = [
{ label: "Duration", value: "BY_DURATION" }, { label: "Duration", value: "BY_DURATION" },
]; ];
export const TraceEntitys = ["All", "Service", "ServiceInstance", "Endpoint"]; export const TraceEntitys = ["All", "Service", "ServiceInstance", "Endpoint"];
export const ProfileMode: any[] = [
{ label: "Include Children", value: "include" },
{ label: "Exclude Children", value: "exclude" },
];

View File

@ -84,10 +84,13 @@ limitations under the License. -->
size="sm" size="sm"
:iconName="t.name" :iconName="t.name"
v-if=" v-if="
!['topology', 'trace'].includes(t.id) || !['topology', 'trace', 'profile'].includes(t.id) ||
(t.id === 'topology' && (t.id === 'topology' &&
hasTopology.includes(dashboardStore.entity)) || hasTopology.includes(dashboardStore.entity)) ||
(t.id === 'trace' && TraceEntitys.includes(dashboardStore.entity)) (t.id === 'trace' &&
TraceEntitys.includes(dashboardStore.entity)) ||
(t.id === 'profile' &&
dashboardStore.entity === EntityType[0].value)
" "
/> />
</span> </span>

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div class="flex-h header"> <div class="flex-h header">
<div class="mr-10"> <!-- <div class="mr-10" v-if="dashboardStore.entity==='All'">
<span class="grey mr-5">{{ t("service") }}:</span> <span class="grey mr-5">{{ t("service") }}:</span>
<Selector <Selector
size="small" size="small"
@ -23,7 +23,7 @@ limitations under the License. -->
placeholder="Select a service" placeholder="Select a service"
@change="changeService" @change="changeService"
/> />
</div> </div> -->
<div class="mr-10"> <div class="mr-10">
<span class="grey mr-5">{{ t("endpointName") }}:</span> <span class="grey mr-5">{{ t("endpointName") }}:</span>
<el-input v-model="endpointName" class="name" /> <el-input v-model="endpointName" class="name" />
@ -36,43 +36,54 @@ limitations under the License. -->
> >
{{ t("search") }} {{ t("search") }}
</el-button> </el-button>
<el-button class="search-btn" size="small"> <el-button class="search-btn" size="small" @click="createTask">
{{ t("newTask") }} {{ t("newTask") }}
</el-button> </el-button>
</div> </div>
<el-dialog
v-model="newTask"
:destroy-on-close="true"
fullscreen
@closed="newTask = false"
>
<NewTask @close="newTask = false" />
</el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref, watch } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useProfileStore } from "@/store/modules/profile"; import { useProfileStore } from "@/store/modules/profile";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useSelectorStore } from "@/store/modules/selectors";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import NewTask from "./components/NewTask.vue";
const profileStore = useProfileStore(); const profileStore = useProfileStore();
const dashboardStore = useDashboardStore(); const selectorStore = useSelectorStore();
const { t } = useI18n(); const { t } = useI18n();
const service = ref<any>({}); // const service = ref<any>({});
const endpointName = ref<string>(""); const endpointName = ref<string>("");
const newTask = ref<boolean>(false);
getServices(); searchTasks();
// getServices();
async function getServices() { // async function getServices() {
const res = await profileStore.getServices(dashboardStore.layerId); // const res = await profileStore.getServices(dashboardStore.layerId);
if (res.errors) { // if (res.errors) {
ElMessage.error(res.errors); // ElMessage.error(res.errors);
return; // return;
} // }
service.value = profileStore.services[0]; // service.value = profileStore.services[0];
searchTasks(); // searchTasks();
} // }
function changeService(opt: any[]) { // function changeService(opt: any[]) {
service.value = opt[0]; // service.value = opt[0];
} // }
async function searchTasks() { async function searchTasks() {
profileStore.setConditions({ profileStore.setConditions({
serviceId: service.value.id, serviceId: selectorStore.currentService.id,
endpointName: endpointName.value, endpointName: endpointName.value,
}); });
const res = await profileStore.getTaskList(); const res = await profileStore.getTaskList();
@ -81,6 +92,17 @@ async function searchTasks() {
ElMessage.error(res.errors); ElMessage.error(res.errors);
} }
} }
function createTask() {
newTask.value = true;
}
watch(
() => selectorStore.currentService,
() => {
searchTasks();
}
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.header { .header {

View File

@ -0,0 +1,170 @@
<!-- 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="rk-profile-task">
<div>
<div class="label">{{ t("endpointName") }}</div>
<el-input v-model="endpointName" class="profile-input" />
</div>
<div>
<div class="label">{{ t("monitorTime") }}</div>
<div>
<Radio
class="mb-5"
:value="monitorTime"
:options="InitTaskField.monitorTimeEn"
@change="changeMonitorTime"
/>
<span>
<TimePicker
:value="time"
position="bottom"
format="YYYY-MM-DD HH:mm:ss"
@input="changeTimeRange"
/>
</span>
</div>
</div>
<div>
<div class="label">{{ t("monitorDuration") }}</div>
<Radio
class="mb-5"
:value="monitorDuration"
:options="InitTaskField.monitorDuration"
@change="changeMonitorDuration"
/>
</div>
<div>
<div class="label">{{ t("minThreshold") }} (ms)</div>
<el-input-number class="profile-input" :min="0" v-model="minThreshold" />
</div>
<div>
<div class="label">{{ t("dumpPeriod") }}</div>
<Radio
class="mb-5"
:value="dumpPeriod"
:options="InitTaskField.dumpPeriod"
@change="changeDumpPeriod"
/>
</div>
<div>
<div class="label">{{ t("maxSamplingCount") }}</div>
<Selector
size="small"
:value="maxSamplingCount"
:options="InitTaskField.maxSamplingCount"
placeholder="Select a data"
@change="changeMaxSamplingCount"
class="profile-input"
/>
</div>
<div>
<el-button @click="createTask" type="primary" class="create-task-btn">
{{ t("createTask") }}
</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { useProfileStore } from "@/store/modules/profile";
import { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app";
import { ElMessage } from "element-plus";
import { InitTaskField } from "./data";
/* global defineEmits */
const emits = defineEmits(["close"]);
const profileStore = useProfileStore();
const selectorStore = useSelectorStore();
const appStore = useAppStoreWithOut();
const { t } = useI18n();
const endpointName = ref<string>("");
const monitorTime = ref<string>(InitTaskField.monitorTimeEn[0].value);
const monitorDuration = ref<string>(InitTaskField.monitorDuration[0].value);
const time = ref<Date>(appStore.durationRow.start);
const minThreshold = ref<number>(0);
const dumpPeriod = ref<string>(InitTaskField.dumpPeriod[0].value);
const maxSamplingCount = ref<string>(InitTaskField.maxSamplingCount[0].value);
function changeMonitorTime(opt: string) {
monitorTime.value = opt;
}
function changeMonitorDuration(val: string) {
monitorDuration.value = val;
}
function changeDumpPeriod(val: string) {
dumpPeriod.value = val;
}
function changeMaxSamplingCount(opt: any[]) {
maxSamplingCount.value = opt[0].value;
}
async function createTask() {
emits("close");
const params = {
serviceId: selectorStore.currentService.id,
endpointName: endpointName.value,
startTime: time.value.getTime(),
duration: Number(monitorDuration.value),
minDurationThreshold: Number(minThreshold.value),
dumpPeriod: Number(dumpPeriod.value),
maxSamplingCount: Number(maxSamplingCount.value),
};
const res = await profileStore.createTask(params);
if (res.errors) {
ElMessage.error(res.errors);
return;
}
const { tip } = res.data;
if (tip) {
ElMessage.error(tip);
return;
}
ElMessage.success("Task created successfully");
}
function changeTimeRange(val: Date) {
time.value = val;
}
</script>
<style lang="scss" scoped>
.label {
margin-top: 10px;
font-size: 14px;
}
.profile-input {
width: 300px;
}
.create-task-btn {
width: 300px;
margin-top: 50px;
}
.message-tip {
font-size: 14px;
color: red;
margin-top: 10px;
}
.monitor-time-radio {
display: inline;
}
</style>

View File

@ -56,7 +56,7 @@ import Selector from "@/components/Selector.vue";
import { Span } from "@/types/trace"; import { Span } from "@/types/trace";
import { Option } from "@/types/app"; import { Option } from "@/types/app";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { ProfileMode } from "../../../data"; import { ProfileMode } from "./data";
const { t } = useI18n(); const { t } = useI18n();
const profileStore = useProfileStore(); const profileStore = useProfileStore();

View 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.
*/
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" },
],
};