feat: support cold stage data for metrics, trace and log (#469)

This commit is contained in:
Fine0830 2025-05-21 09:19:06 +08:00 committed by GitHub
parent 0c2cfa5630
commit a28972bc5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 432 additions and 61 deletions

View File

@ -169,12 +169,13 @@ limitations under the License. -->
value: { type: Date },
left: { type: Boolean, default: false },
right: { type: Boolean, default: false },
dates: { type: Array as PropType<number[] | string[]>, default: () => [] },
dates: { type: Array as PropType<Date[]>, default: () => [] },
disabledDate: { type: Function, default: () => false },
format: {
type: String,
default: "YYYY-MM-DD",
},
maxRange: { type: Array as PropType<Date[]>, default: () => [] },
});
const state = reactive({
pre: "",
@ -241,6 +242,12 @@ limitations under the License. -->
const end = computed(() => {
return parse(Number(props.dates[1]));
});
const minStart = computed(() => {
return parse(Number(props.maxRange[0]));
});
const maxEnd = computed(() => {
return parse(Number(props.maxRange[1]));
});
const ys = computed(() => {
return Math.floor(state.year / 10) * 10;
});
@ -369,7 +376,10 @@ limitations under the License. -->
flag = tf(props.value, format) === tf(time, format);
}
classObj[`${state.pre}-date`] = true;
classObj[`${state.pre}-date-disabled`] = (props.right && t < start.value) || props.disabledDate(time, format);
const rightDisabled = props.right && (t < start.value || t > maxEnd.value || !props.maxRange?.length);
const leftDisabled =
props.left && (t < minStart.value || t > end.value || !props.maxRange?.length || t > maxEnd.value);
classObj[`${state.pre}-date-disabled`] = rightDisabled || leftDisabled || props.disabledDate(time, format);
classObj[`${state.pre}-date-on`] = (props.left && t > start.value) || (props.right && t < end.value);
classObj[`${state.pre}-date-selected`] = flag;
return classObj;

View File

@ -68,6 +68,7 @@ limitations under the License. -->
:left="true"
:disabledDate="disabledDate"
:format="format"
:maxRange="maxRange"
@ok="ok"
@setDates="setDates"
/>
@ -78,6 +79,7 @@ limitations under the License. -->
:right="true"
:disabledDate="disabledDate"
:format="format"
:maxRange="maxRange"
@ok="ok"
@setDates="setDates"
/>
@ -112,11 +114,11 @@ limitations under the License. -->
import { useI18n } from "vue-i18n";
import DateCalendar from "./DateCalendar.vue";
import { useTimeoutFn } from "@/hooks/useTimeout";
/*global defineProps, defineEmits*/
/*global PropType, defineProps, defineEmits*/
const datepicker = ref(null);
const { t } = useI18n();
const show = ref<boolean>(false);
const dates = ref<Date | string[] | any>([]);
const dates = ref<Date[]>([]);
const props = defineProps({
position: { type: String, default: "bottom" },
name: [String],
@ -149,7 +151,7 @@ limitations under the License. -->
type: Boolean,
default: false,
},
dateRangeSelect: [Function],
maxRange: { type: Array as PropType<Date[]>, default: () => [] },
});
const emit = defineEmits(["clear", "input", "confirm", "cancel"]);
const local = computed(() => {

View File

@ -49,3 +49,22 @@ export const MenuItems = {
}
`,
};
export const RecordsTTL = {
query: `getRecordsTTL {
value
superDataset
coldValue
coldSuperDataset
}`,
};
export const MetricsTTL = {
query: `getMetricsTTL {
minute
hour
day
coldMinute
coldHour
coldDay
}`,
};

View File

@ -103,3 +103,63 @@ export const TraceTagValues = {
query: `
tagValues: queryTraceTagAutocompleteValues(tagKey: $tagKey, duration: $duration)`,
};
export const TraceSpansFromColdStage = {
variable: "$traceId: ID!, $duration: Duration!, $debug: Boolean",
query: `
trace: queryTrace(traceId: $traceId, duration: $duration, debug: $debug) {
spans {
traceId
segmentId
spanId
parentSpanId
refs {
traceId
parentSegmentId
parentSpanId
type
}
serviceCode
serviceInstanceName
startTime
endTime
endpointName
type
peer
component
isError
layer
tags {
key
value
}
logs {
time
data {
key
value
}
}
attachedEvents {
startTime {
seconds
nanos
}
event
endTime {
seconds
nanos
}
tags {
key
value
}
summary {
key
value
}
}
}
}
`,
};

View File

@ -14,10 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { OAPTimeInfo, OAPVersion, MenuItems } from "../fragments/app";
import { OAPTimeInfo, OAPVersion, MenuItems, MetricsTTL, RecordsTTL } from "../fragments/app";
export const queryOAPTimeInfo = `query queryOAPTimeInfo {${OAPTimeInfo.query}}`;
export const queryOAPVersion = `query ${OAPVersion.query}`;
export const queryMenuItems = `query menuItems {${MenuItems.query}}`;
export const queryMetricsTTL = `query MetricsTTL {${MetricsTTL.query}}`;
export const queryRecordsTTL = `query RecordsTTL {${RecordsTTL.query}}`;

View File

@ -15,12 +15,14 @@
* limitations under the License.
*/
import { Traces, TraceSpans, TraceTagKeys, TraceTagValues } from "../fragments/trace";
import { Traces, TraceSpans, TraceTagKeys, TraceTagValues, TraceSpansFromColdStage } from "../fragments/trace";
export const queryTraces = `query queryTraces(${Traces.variable}) {${Traces.query}}`;
export const queryTrace = `query queryTrace(${TraceSpans.variable}) {${TraceSpans.query}}`;
export const querySpans = `query querySpans(${TraceSpans.variable}) {${TraceSpans.query}}`;
export const queryTraceTagKeys = `query queryTraceTagKeys(${TraceTagKeys.variable}) {${TraceTagKeys.query}}`;
export const queryTraceTagValues = `query queryTraceTagValues(${TraceTagValues.variable}) {${TraceTagValues.query}}`;
export const queryTraceSpansFromColdStage = `query queryTraceSpansFromColdStage(${TraceSpansFromColdStage.variable}) {${TraceSpansFromColdStage.query}}`;

55
src/hooks/useDuration.ts Normal file
View File

@ -0,0 +1,55 @@
/**
* 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, InitializationDurationRow } from "@/store/modules/app";
import type { Duration, DurationTime } from "@/types/app";
import getLocalTime from "@/utils/localtime";
import dateFormatStep from "@/utils/dateFormat";
export function useDuration() {
let durationRow: Duration = InitializationDurationRow;
function getDuration() {
const appStore = useAppStoreWithOut();
return {
start: getLocalTime(appStore.utc, durationRow.start),
end: getLocalTime(appStore.utc, durationRow.end),
step: durationRow.step,
};
}
function getDurationTime(): DurationTime {
const { start, step, end } = getDuration();
return {
start: dateFormatStep(start, step, true),
end: dateFormatStep(end, step, true),
step: step,
};
}
function setDurationRow(data: Duration) {
durationRow = data;
}
function getMaxRange(day: number) {
if (day === -1) {
return [];
}
const gap = (day + 1) * 24 * 60 * 60 * 1000;
const dates: Date[] = [new Date(new Date().getTime() - gap), new Date()];
return dates;
}
return { setDurationRow, getDurationTime, getMaxRange };
}

View File

@ -40,14 +40,25 @@ limitations under the License. -->
</el-breadcrumb>
<div class="title" v-else>{{ pageTitle }}</div>
<div class="app-config">
<span class="red" v-show="timeRange">{{ t("timeTips") }}</span>
<span class="red" v-show="showTimeRangeTips">{{ t("timeTips") }}</span>
<TimePicker
:value="[appStore.durationRow.start, appStore.durationRow.end]"
:maxRange="appStore.maxRange"
position="bottom"
format="YYYY-MM-DD HH:mm"
@input="changeTimeRange"
/>
<span> UTC{{ appStore.utcHour >= 0 ? "+" : "" }}{{ `${appStore.utcHour}:${appStore.utcMin}` }} </span>
<span class="ml-5">
<el-switch
v-model="coldStage"
inline-prompt
active-text="Active Data"
inactive-text="Cold Data"
@change="changeDataMode"
width="90px"
/>
</span>
<span class="ml-5" ref="themeSwitchRef">
<el-switch
v-model="theme"
@ -75,7 +86,7 @@ limitations under the License. -->
<script lang="ts" setup>
import { Themes } from "@/constants/data";
import router from "@/router";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useAppStoreWithOut, InitializationDurationRow } from "@/store/modules/app";
import { useDashboardStore } from "@/store/modules/dashboard";
import type { DashboardItem } from "@/types/dashboard";
import timeFormat from "@/utils/timeFormat";
@ -92,10 +103,11 @@ limitations under the License. -->
const dashboardStore = useDashboardStore();
const route = useRoute();
const pathNames = ref<{ path?: string; name: string; selected: boolean }[][]>([]);
const timeRange = ref<number>(0);
const showTimeRangeTips = ref<boolean>(false);
const pageTitle = ref<string>("");
const theme = ref<boolean>(true);
const themeSwitchRef = ref<HTMLElement>();
const coldStage = ref<boolean>(false);
const savedTheme = window.localStorage.getItem("theme-is-dark");
if (savedTheme === "false") {
@ -110,6 +122,7 @@ limitations under the License. -->
resetDuration();
getVersion();
getNavPaths();
setTTL();
function changeTheme() {
const root = document.documentElement;
@ -126,6 +139,24 @@ limitations under the License. -->
window.localStorage.setItem("theme-is-dark", String(theme.value));
}
function changeDataMode() {
appStore.setColdStageMode(coldStage.value);
if (coldStage.value) {
handleMetricsTTL({
minute: appStore.metricsTTL.coldMinute,
hour: appStore.metricsTTL.coldHour,
day: appStore.metricsTTL.coldDay,
});
} else {
handleMetricsTTL({
minute: appStore.metricsTTL.minute,
hour: appStore.metricsTTL.hour,
day: appStore.metricsTTL.day,
});
}
appStore.setDuration(InitializationDurationRow);
}
function handleChangeTheme() {
const x = themeSwitchRef.value?.offsetLeft ?? 0;
const y = themeSwitchRef.value?.offsetTop ?? 0;
@ -184,13 +215,48 @@ limitations under the License. -->
}
function changeTimeRange(val: Date[]) {
timeRange.value = val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000 ? 1 : 0;
if (timeRange.value) {
showTimeRangeTips.value = val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000;
if (showTimeRangeTips.value) {
return;
}
appStore.setDuration(timeFormat(val));
}
function setTTL() {
getMetricsTTL();
getRecordsTTL();
changeDataMode();
}
async function getRecordsTTL() {
const resp = await appStore.queryRecordsTTL();
if (resp.errors) {
ElMessage.error(resp.errors);
}
}
async function getMetricsTTL() {
const resp = await appStore.queryMetricsTTL();
if (resp.errors) {
ElMessage.error(resp.errors);
}
}
function handleMetricsTTL({ minute, hour, day }: { minute: number; hour: number; day: number }) {
if (minute === -1 || hour === -1 || day === -1) {
return appStore.setMaxRange([]);
}
if (!day) {
return appStore.setMaxRange([]);
}
const gap = Math.max(day, hour, minute);
const dates: Date[] = [new Date(new Date().getTime() - dayToMS(gap + 1)), new Date()];
appStore.setMaxRange(dates);
}
function dayToMS(day: number) {
return day * 24 * 60 * 60 * 1000;
}
function getNavPaths() {
pathNames.value = [];
pageTitle.value = "";

View File

@ -21,11 +21,11 @@ import type { Duration, DurationTime } from "@/types/app";
import getLocalTime from "@/utils/localtime";
import dateFormatStep, { dateFormatTime } from "@/utils/dateFormat";
import { TimeType } from "@/constants/data";
import type { MenuOptions, SubItem } from "@/types/app";
import type { MenuOptions, SubItem, MetricsTTL, RecordsTTL } from "@/types/app";
import { Themes } from "@/constants/data";
/*global Nullable*/
interface AppState {
durationRow: Recordable;
durationRow: Duration;
utc: string;
utcHour: number;
utcMin: number;
@ -37,16 +37,22 @@ interface AppState {
reloadTimer: Nullable<IntervalHandle>;
allMenus: MenuOptions[];
theme: string;
coldStageMode: boolean;
maxRange: Date[];
metricsTTL: Recordable<MetricsTTL>;
recordsTTL: Recordable<RecordsTTL>;
}
export const InitializationDurationRow = {
start: new Date(new Date().getTime() - 1800000),
end: new Date(),
step: TimeType.MINUTE_TIME,
};
export const appStore = defineStore({
id: "app",
state: (): AppState => ({
durationRow: {
start: new Date(new Date().getTime() - 1800000),
end: new Date(),
step: TimeType.MINUTE_TIME,
},
durationRow: InitializationDurationRow,
utc: "",
utcHour: 0,
utcMin: 0,
@ -58,6 +64,10 @@ export const appStore = defineStore({
reloadTimer: null,
allMenus: [],
theme: Themes.Dark,
coldStageMode: false,
maxRange: [],
metricsTTL: {},
recordsTTL: {},
}),
getters: {
duration(): Duration {
@ -122,6 +132,9 @@ export const appStore = defineStore({
updateDurationRow(data: Duration) {
this.durationRow = data;
},
setMaxRange(times: Date[]) {
this.maxRange = times;
},
setTheme(data: string) {
this.theme = data;
},
@ -143,6 +156,9 @@ export const appStore = defineStore({
setAutoRefresh(auto: boolean) {
this.autoRefresh = auto;
},
setColdStageMode(mode: boolean) {
this.coldStageMode = mode;
},
runEventStack() {
if (this.timer) {
clearTimeout(this.timer);
@ -206,6 +222,22 @@ export const appStore = defineStore({
return res.data;
},
async queryMetricsTTL() {
const res = await graphql.query("queryMetricsTTL").params({});
if (res.errors) {
return res;
}
this.metricsTTL = res.data.getMetricsTTL;
return res.data;
},
async queryRecordsTTL() {
const res = await graphql.query("queryRecordsTTL").params({});
if (res.errors) {
return res;
}
this.recordsTTL = res.data.getRecordsTTL;
return res.data;
},
setReloadTimer(timer: IntervalHandle) {
this.reloadTimer = timer;
},

View File

@ -21,6 +21,7 @@ import graphql from "@/graphql";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useSelectorStore } from "@/store/modules/selectors";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useDuration } from "@/hooks/useDuration";
import { EndpointsTopNDefault } from "../data";
interface LogState {
@ -33,6 +34,7 @@ interface LogState {
logs: Recordable[];
loadLogs: boolean;
}
const { getDurationTime } = useDuration();
export const logStore = defineStore({
id: "log",
@ -41,7 +43,7 @@ export const logStore = defineStore({
instances: [{ value: "0", label: "All" }],
endpoints: [{ value: "0", label: "All" }],
conditions: {
queryDuration: useAppStoreWithOut().durationTime,
queryDuration: getDurationTime(),
paging: { pageNum: 1, pageSize: 15 },
},
supportQueryLogsByKeywords: true,
@ -56,7 +58,7 @@ export const logStore = defineStore({
resetState() {
this.logs = [];
this.conditions = {
queryDuration: useAppStoreWithOut().durationTime,
queryDuration: getDurationTime(),
paging: { pageNum: 1, pageSize: 15 },
};
},

View File

@ -23,6 +23,7 @@ import { useAppStoreWithOut } from "@/store/modules/app";
import { useSelectorStore } from "@/store/modules/selectors";
import { QueryOrders } from "@/views/dashboard/data";
import { EndpointsTopNDefault } from "../data";
import { useDuration } from "@/hooks/useDuration";
interface TraceState {
services: Service[];
instances: Instance[];
@ -35,6 +36,7 @@ interface TraceState {
selectorStore: Recordable;
selectedSpan: Recordable<Span>;
}
const { getDurationTime } = useDuration();
export const traceStore = defineStore({
id: "trace",
@ -47,7 +49,7 @@ export const traceStore = defineStore({
currentTrace: {},
selectedSpan: {},
conditions: {
queryDuration: useAppStoreWithOut().durationTime,
queryDuration: getDurationTime(),
traceState: "ALL",
queryOrder: QueryOrders[0].value,
paging: { pageNum: 1, pageSize: 20 },
@ -73,7 +75,7 @@ export const traceStore = defineStore({
this.traceList = [];
this.currentTrace = {};
this.conditions = {
queryDuration: useAppStoreWithOut().durationTime,
queryDuration: getDurationTime(),
paging: { pageNum: 1, pageSize: 20 },
traceState: "ALL",
queryOrder: QueryOrders[0].value,
@ -166,7 +168,15 @@ export const traceStore = defineStore({
return response;
},
async getTraceSpans(params: { traceId: string }) {
const response = await graphql.query("queryTrace").params(params);
const appStore = useAppStoreWithOut();
let response;
if (appStore.coldStageMode) {
response = await graphql
.query("queryTraceSpansFromColdStage")
.params({ ...params, duration: this.conditions.queryDuration });
} else {
response = await graphql.query("querySpans").params(params);
}
if (response.errors) {
return response;
}

16
src/types/app.d.ts vendored
View File

@ -68,3 +68,19 @@ export interface SubItem {
descKey: string;
i18nKey: string;
}
export interface MetricsTTL {
minute: number;
hour: number;
day: number;
coldMinute: number;
coldHour: number;
coldDay: number;
}
export interface RecordsTTL {
value: number;
superDataset: number;
coldValue: number;
coldSuperDataset: number;
}

View File

@ -60,10 +60,7 @@ export type Filters = {
dataIndex: number;
sourceId: string;
isRange?: boolean;
duration?: {
startTime: string;
endTime: string;
};
duration?: DurationTime;
traceId?: string;
spanId?: string;
segmentId?: string;

View File

@ -30,6 +30,16 @@ limitations under the License. -->
<span class="grey">{{ t("searchKeyword") }}: </span>
<el-input size="small" v-model="keyword" class="alarm-tool-input" @change="refreshAlarms({ pageNum: 1 })" />
</div>
<div>
<span class="sm b grey mr-5">{{ t("timeRange") }}:</span>
<TimePicker
:value="[durationRow.start, durationRow.end]"
:maxRange="maxRange"
position="bottom"
format="YYYY-MM-DD HH:mm"
@input="changeDuration"
/>
</div>
<div class="pagination">
<el-pagination
v-model="pageNum"
@ -51,31 +61,40 @@ limitations under the License. -->
</nav>
</template>
<script lang="ts" setup>
import { ref, computed } from "vue";
import { ref, computed, watch } from "vue";
import { useI18n } from "vue-i18n";
import { ElMessage } from "element-plus";
import ConditionTags from "@/views/components/ConditionTags.vue";
import { AlarmOptions } from "./data";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useAppStoreWithOut, InitializationDurationRow } from "@/store/modules/app";
import { useAlarmStore } from "@/store/modules/alarm";
import { ElMessage } from "element-plus";
import { useDuration } from "@/hooks/useDuration";
import timeFormat from "@/utils/timeFormat";
import type { DurationTime, Duration } from "@/types/app";
import { Themes } from "@/constants/data";
/*global Recordable */
const appStore = useAppStoreWithOut();
const alarmStore = useAlarmStore();
const { t } = useI18n();
const { setDurationRow, getDurationTime, getMaxRange } = useDuration();
const pageSize = 20;
const entity = ref<string>("");
const keyword = ref<string>("");
const pageNum = ref<number>(1);
const duration = ref<DurationTime>(getDurationTime());
const durationRow = ref<Duration>(InitializationDurationRow);
const total = computed(() =>
alarmStore.alarms.length === pageSize ? pageSize * pageNum.value + 1 : pageSize * pageNum.value,
);
const maxRange = computed(() =>
getMaxRange(appStore.coldStageMode ? appStore.recordsTTL.coldValue : appStore.recordsTTL.value),
);
refreshAlarms({ pageNum: 1 });
async function refreshAlarms(param: { pageNum: number; tagsMap?: any }) {
const params: any = {
duration: appStore.durationTime,
async function refreshAlarms(param: { pageNum: number; tagsMap?: Recordable }) {
const params: Recordable = {
duration: duration.value,
paging: {
pageNum: param.pageNum,
pageSize,
@ -91,7 +110,14 @@ limitations under the License. -->
}
}
function changeEntity(param: any) {
function changeDuration(val: Date[]) {
durationRow.value = timeFormat(val);
setDurationRow(durationRow.value);
duration.value = getDurationTime();
refreshAlarms({ pageNum: 1 });
}
function changeEntity(param: { value: string }[]) {
entity.value = param[0].value;
refreshAlarms({ pageNum: 1 });
}
@ -100,6 +126,16 @@ limitations under the License. -->
pageNum.value = p;
refreshAlarms({ pageNum: p });
}
watch(
() => appStore.coldStageMode,
() => {
durationRow.value = InitializationDurationRow;
setDurationRow(durationRow.value);
duration.value = getDurationTime();
refreshAlarms({ pageNum: 1 });
},
);
</script>
<style lang="scss" scoped>
.alarm-tool {

View File

@ -56,6 +56,16 @@ limitations under the License. -->
@change="changeField('category', $event)"
/>
</div>
<div>
<span class="sm b grey mr-5">{{ t("timeRange") }}:</span>
<TimePicker
:value="[durationRow.start, durationRow.end]"
:maxRange="maxRange"
position="bottom"
format="YYYY-MM-DD HH:mm"
@input="changeDuration"
/>
</div>
<el-button class="search-btn" size="small" type="primary" @click="searchLogs">
{{ t("search") }}
</el-button>
@ -119,20 +129,21 @@ limitations under the License. -->
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, onUnmounted } from "vue";
import { ref, reactive, watch, onUnmounted, computed } from "vue";
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
import type { Option } from "@/types/app";
import { ElMessage } from "element-plus";
import { useLogStore } from "@/store/modules/log";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useAppStoreWithOut, InitializationDurationRow } from "@/store/modules/app";
import { useSelectorStore } from "@/store/modules/selectors";
import { useDuration } from "@/hooks/useDuration";
import ConditionTags from "@/views/components/ConditionTags.vue";
import { ElMessage } from "element-plus";
import timeFormat from "@/utils/timeFormat";
import { EntityType } from "../../data";
import { ErrorCategory } from "./data";
import type { LayoutConfig } from "@/types/dashboard";
import type { DurationTime } from "@/types/app";
import type { Option, DurationTime, Duration } from "@/types/app";
/*global defineProps, Recordable */
const props = defineProps({
@ -147,8 +158,9 @@ limitations under the License. -->
const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore();
const logStore = useLogStore();
const { setDurationRow, getDurationTime, getMaxRange } = useDuration();
const traceId = ref<string>((props.data.filters && props.data.filters.traceId) || "");
const duration = ref<DurationTime>((props.data.filters && props.data.filters.duration) || appStore.durationTime);
const duration = ref<DurationTime>((props.data.filters && props.data.filters.duration) || getDurationTime());
const keywordsOfContent = ref<string[]>([]);
const excludingKeywordsOfContent = ref<string[]>([]);
const tagsList = ref<string[]>([]);
@ -156,12 +168,16 @@ limitations under the License. -->
const contentStr = ref<string>("");
const excludingContentStr = ref<string>("");
const isBrowser = ref<boolean>(dashboardStore.layerId === "BROWSER");
const durationRow = ref<Duration>(InitializationDurationRow);
const state = reactive<Recordable>({
instance: { value: "0", label: "All" },
endpoint: { value: "0", label: "All" },
service: { value: "", label: "" },
category: { value: "ALL", label: "All" },
});
const maxRange = computed(() =>
getMaxRange(appStore.coldStageMode ? appStore.recordsTTL.coldSuperDataset : appStore.recordsTTL.superDataset),
);
if (props.needQuery) {
init();
}
@ -275,6 +291,11 @@ limitations under the License. -->
ElMessage.error(res.errors);
}
}
function changeDuration(val: Date[]) {
durationRow.value = timeFormat(val);
setDurationRow(durationRow.value);
duration.value = getDurationTime();
}
function changeField(type: string, opt: any) {
state[type] = opt[0];
if (type === "service") {
@ -352,12 +373,12 @@ limitations under the License. -->
},
);
watch(
() => appStore.durationTime,
() => appStore.coldStageMode,
() => {
duration.value = appStore.durationTime;
if (dashboardStore.entity === EntityType[1].value) {
durationRow.value = InitializationDurationRow;
setDurationRow(durationRow.value);
duration.value = getDurationTime();
init();
}
},
);
watch(
@ -368,7 +389,7 @@ limitations under the License. -->
return;
}
traceId.value = props.data.filters.traceId || "";
duration.value = props.data.filters.duration || appStore.durationTime;
duration.value = props.data.filters.duration || getDurationTime();
init();
}
},

View File

@ -71,24 +71,36 @@ limitations under the License. -->
<span class="grey mr-5">-</span>
<el-input size="small" class="inputs" v-model="maxTraceDuration" type="number" />
</div>
<div>
<span class="sm b grey mr-5">{{ t("timeRange") }}:</span>
<TimePicker
:value="[durationRow.start, durationRow.end]"
:maxRange="maxRange"
position="bottom"
format="YYYY-MM-DD HH:mm"
@input="changeDuration"
/>
</div>
</div>
<div class="flex-h">
<ConditionTags :type="'TRACE'" @update="updateTags" />
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, onUnmounted } from "vue";
import { ref, reactive, watch, onUnmounted, computed } from "vue";
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
import type { Option, DurationTime } from "@/types/app";
import type { Option, DurationTime, Duration } from "@/types/app";
import { useTraceStore } from "@/store/modules/trace";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useAppStoreWithOut, InitializationDurationRow } from "@/store/modules/app";
import { useSelectorStore } from "@/store/modules/selectors";
import timeFormat from "@/utils/timeFormat";
import ConditionTags from "@/views/components/ConditionTags.vue";
import { ElMessage } from "element-plus";
import { EntityType, QueryOrders, Status } from "../../data";
import type { LayoutConfig } from "@/types/dashboard";
import { useDuration } from "@/hooks/useDuration";
/*global defineProps, defineEmits, Recordable */
const emits = defineEmits(["get", "search"]);
@ -99,14 +111,15 @@ limitations under the License. -->
default: () => ({ graph: {} }),
},
});
const filters = reactive<Recordable>(props.data.filters || {});
const traceId = ref<string>(filters.traceId || "");
const { t } = useI18n();
const appStore = useAppStoreWithOut();
const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore();
const traceStore = useTraceStore();
const duration = ref<DurationTime>(filters.duration || appStore.durationTime);
const { setDurationRow, getDurationTime, getMaxRange } = useDuration();
const filters = reactive<Recordable>(props.data.filters || {});
const traceId = ref<string>(filters.traceId || "");
const duration = ref<DurationTime>(filters.duration || getDurationTime());
const minTraceDuration = ref<number>();
const maxTraceDuration = ref<number>();
const tagsList = ref<string[]>([]);
@ -117,6 +130,10 @@ limitations under the License. -->
endpoint: { value: "0", label: "All" },
service: { value: "", label: "" },
});
const durationRow = ref<Duration>(InitializationDurationRow);
const maxRange = computed(() =>
getMaxRange(appStore.coldStageMode ? appStore.recordsTTL.coldSuperDataset : appStore.recordsTTL.superDataset),
);
if (filters.queryOrder) {
traceStore.setTraceCondition({
queryOrder: filters.queryOrder,
@ -255,6 +272,11 @@ limitations under the License. -->
ElMessage.error(resp.errors);
}
}
function changeDuration(val: Date[]) {
durationRow.value = timeFormat(val);
setDurationRow(durationRow.value);
duration.value = getDurationTime();
}
onUnmounted(() => {
traceStore.resetState();
const config = props.data;
@ -280,12 +302,12 @@ limitations under the License. -->
},
);
watch(
() => appStore.durationTime,
() => appStore.coldStageMode,
() => {
duration.value = appStore.durationTime;
if (dashboardStore.entity === EntityType[1].value) {
durationRow.value = InitializationDurationRow;
setDurationRow(durationRow.value);
duration.value = getDurationTime();
init();
}
},
);
// Event widget associate with trace widget
@ -299,7 +321,7 @@ limitations under the License. -->
return;
}
traceId.value = props.data.filters.traceId || "";
duration.value = props.data.filters.duration || appStore.durationTime;
duration.value = props.data.filters.duration || getDurationTime();
init();
},
);

View File

@ -31,6 +31,12 @@ limitations under the License. -->
@change="changeLatency"
class="ml-10"
/>
<TimePicker
:value="[appStore.durationRow.start, appStore.durationRow.end]"
position="bottom"
format="YYYY-MM-DD HH:mm"
@input="changeTimeRange"
/>
<el-popover trigger="hover" width="250" placement="bottom">
<template #reference>
<div class="cp conditions-popup">
@ -82,6 +88,7 @@ limitations under the License. -->
import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useSelectorStore } from "@/store/modules/selectors";
import timeFormat from "@/utils/timeFormat";
import ConditionTags from "@/views/components/ConditionTags.vue";
import { ElMessage } from "element-plus";
import { EntityType, QueryOrders, Status } from "../../data";
@ -107,6 +114,7 @@ limitations under the License. -->
const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore();
const traceStore = useTraceStore();
const timeRange = ref<number>(NaN);
const tagsList = ref<string[]>([]);
const tagsMap = ref<Option[]>([]);
const traceId = ref<string>(filters.refId || "");
@ -169,6 +177,15 @@ limitations under the License. -->
}
await queryTraces();
}
function changeTimeRange(val: Date[]) {
timeRange.value = val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000 ? 1 : 0;
if (timeRange.value) {
return;
}
appStore.setDuration(timeFormat(val));
}
function changeCondition() {
if (conditions.value === "latency") {
currentLatency.value = filters.latency ? filters.latency[0].data : [];