fix: UI Improvements (#1148)

1. Refactored date time component and updated same in logs, traces and
metrics
2. Added composable for notification
This commit is contained in:
Omkar Kesarkhane
2023-07-11 16:50:55 +05:30
committed by GitHub
parent 2af71450ba
commit 101cedd7d5
18 changed files with 761 additions and 807 deletions
+317 -154
View File
@@ -14,7 +14,7 @@
-->
<template>
<div icon="info" class="justify-between">
<div icon="info" class="justify-between date-time-container">
<q-btn
data-test="date-time-btn"
id="date-time-button"
@@ -26,34 +26,40 @@
icon="schedule"
icon-right="arrow_drop_down"
class="date-time-button"
:class="selectedType + 'type'"
>
<q-menu
id="date-time-menu"
class="date-time-dialog"
anchor="bottom left"
self="top left"
@before-show="onBeforeShow"
>
<div class="flex justify-evenly q-py-sm">
<q-btn
size="md"
class="tab-button no-border"
color="primary"
:flat="selectedDate.tab !== 'relative'"
@click="selectedDate.tab = 'relative'"
no-caps
:flat="selectedType !== 'relative'"
@click="selectedType = 'relative'"
>
RELATIVE
Relative
</q-btn>
<q-separator vertical inset />
<q-btn
size="md"
class="tab-button no-border"
color="primary"
:flat="selectedDate.tab !== 'absolute'"
@click="selectedDate.tab = 'absolute'"
no-caps
:flat="selectedType !== 'absolute'"
@click="selectedType = 'absolute'"
>
ABSOLUTE
Absolute
</q-btn>
</div>
<q-separator />
<q-tab-panels v-model="selectedDate.tab" animated>
<q-tab-panels v-model="selectedType" animated>
<q-tab-panel name="relative" class="q-pa-none">
<div class="date-time-table relative column">
<div
@@ -62,7 +68,7 @@
:key="'date_' + index"
>
<div class="relative-period-name">
{{ period.value }}
{{ period.label }}
</div>
<div
v-for="(item, item_index) in relativeDates[period.value]"
@@ -71,17 +77,17 @@
<q-btn
:data-test="`date-time-relative-${item}-${period.value}-btn`"
:class="
selectedDate.tab == 'relative' &&
selectedDate.relative.period.value == period.value &&
selectedDate.relative.value == item
selectedType == 'relative' &&
relativePeriod == period.value &&
relativeValue == item
? 'rp-selector-selected'
: `rp-selector ${selectedDate.relative.period.value}`
: `rp-selector ${relativePeriod}`
"
:label="item"
outline
dense
flat
@click="setRelativeDate(period, item)"
@click="setRelativeDate(period.value, item)"
:key="'period_' + item_index"
/>
</div>
@@ -93,22 +99,27 @@
<div class="row q-gutter-sm">
<div class="col">
<q-input
v-model="selectedDate.relative.value"
v-model="relativeValue"
type="number"
dense
filled
min="1"
@change="calculateMaxValue"
></q-input>
@update:model-value="onCustomPeriodSelect"
/>
</div>
<div class="col">
<q-select
v-model="selectedDate.relative.period"
v-model="relativePeriod"
:options="relativePeriods"
dense
filled
emit-value
@update:modelValue="onCustomPeriodSelect"
></q-select>
>
<template v-slot:selected-item>
<div>{{ getPeriodLabel }}</div>
</template>
</q-select>
</div>
</div>
</div>
@@ -118,7 +129,8 @@
<div class="date-time-table">
<div class="flex justify-center q-pa-none">
<q-date
v-model="selectedDate.absolute.date"
size="sm"
v-model="selectedDate"
class="absolute-calendar"
range
:locale="{
@@ -137,7 +149,7 @@
<tr>
<td>
<q-input
v-model="selectedDate.absolute.startTime"
v-model="selectedTime.startTime"
dense
filled
mask="time"
@@ -149,7 +161,7 @@
transition-show="scale"
transition-hide="scale"
>
<q-time v-model="selectedDate.absolute.startTime">
<q-time v-model="selectedTime.startTime">
<div class="row items-center justify-end">
<q-btn
v-close-popup
@@ -166,7 +178,7 @@
</td>
<td>
<q-input
v-model="selectedDate.absolute.endTime"
v-model="selectedTime.endTime"
dense
filled
mask="time"
@@ -178,7 +190,7 @@
transition-show="scale"
transition-hide="scale"
>
<q-time v-model="selectedDate.absolute.endTime">
<q-time v-model="selectedTime.endTime">
<div class="row items-center justify-end">
<q-btn
v-close-popup
@@ -198,6 +210,19 @@
</div>
</q-tab-panel>
</q-tab-panels>
<div v-if="!autoApply" class="flex justify-end q-py-sm q-px-md">
<q-separator class="q-my-sm" />
<q-btn
class="no-border q-py-xs"
color="secondary"
no-caps
size="md"
@click="saveDate(null)"
v-close-popup
>
Apply
</q-btn>
</div>
</q-menu>
</q-btn>
</div>
@@ -205,153 +230,295 @@
<script lang="ts">
// @ts-nocheck
import { ref, defineComponent } from "vue";
import {
ref,
defineComponent,
computed,
onMounted,
onBeforeMount,
watch,
} from "vue";
import { getImageURL } from "../utils/zincutils";
import { cloneDeep } from "lodash-es";
import { date } from "quasar";
export default defineComponent({
props: {
defaultDate: {
defaultType: {
type: String,
default: "relative",
},
defaultAbsoluteTime: {
type: Object,
default: null,
},
},
data() {
return {
relativePeriods: [
{ label: "Minutes", value: "Minutes" },
{ label: "Hours", value: "Hours" },
{ label: "Days", value: "Days" },
{ label: "Weeks", value: "Weeks" },
{ label: "Months", value: "Months" },
],
relativeDates: {
Minutes: [1, 5, 10, 15, 30, 45],
Hours: [1, 2, 3, 6, 8, 12],
Days: [1, 2, 3, 4, 5, 6],
Weeks: [1, 2, 3, 4, 5, 6],
Months: [1, 2, 3, 4, 5, 6],
},
selectedDate: {
tab: "relative",
relative: {
period: { label: "Minutes", value: "Minutes" },
value: 15,
},
absolute: {
date: {
from: new Date().toLocaleDateString("en-ZA"),
to: new Date().toLocaleDateString("en-ZA"),
},
startTime: "00:00",
endTime: "23:59",
},
userChangedValue: false,
},
};
},
methods: {
setRelativeDate(period, value) {
this.selectedDate.tab = "relative";
this.selectedDate.relative.period = period;
this.selectedDate.relative.value = value;
this.$refs.datetimeBtn.$el.click();
defaultRelativeTime: {
type: String,
default: "15m",
},
onCustomPeriodSelect() {
this.selectedDate.relative.value = 1;
autoApply: {
type: Boolean,
default: false,
},
},
emits: ["date-change"],
setup() {
const getDate = () => {
return this.selectedDate;
emits: ["on:date-change"],
setup(props, { emit }) {
const selectedType = ref("relative");
const selectedTime = ref({
startTime: "00:00",
endTime: "23:59",
});
const selectedDate = ref({
from: "",
to: "",
});
const relativePeriod = ref("m");
const relativeValue = ref(15);
const relativePeriods = [
{ label: "Minutes", value: "m" },
{ label: "Hours", value: "h" },
{ label: "Days", value: "d" },
{ label: "Weeks", value: "w" },
{ label: "Months", value: "M" },
];
const relativeDates = {
m: [1, 5, 10, 15, 30, 45],
h: [1, 2, 3, 6, 8, 12],
d: [1, 2, 3, 4, 5, 6],
w: [1, 2, 3, 4, 5, 6],
M: [1, 2, 3, 4, 5, 6],
};
const setDate = (date: Object) => {
this.selectedDate = date;
const datetimeBtn = ref(null);
const displayValue = ref("");
onMounted(() => {
// updateDisplayValue();
try {
selectedType.value = props.defaultType;
setAbsoluteTime(
props.defaultAbsoluteTime?.startTime,
props.defaultAbsoluteTime?.endTime
);
setRelativeTime(props.defaultRelativeTime);
displayValue.value = getDisplayValue();
if (props.autoApply) saveDate(props.defaultType);
} catch (e) {
console.log(e);
}
});
watch(
() => selectedType.value,
() => {
displayValue.value = getDisplayValue();
}
);
watch(
() => {
selectedTime.value.startTime;
selectedTime.value.endTime;
selectedDate.value.from;
selectedDate.value.to;
},
() => {
if (props.autoApply) saveDate("absolute");
},
{ deep: true }
);
const setRelativeDate = (period, value) => {
selectedType.value = "relative";
relativePeriod.value = period;
relativeValue.value = value;
if (props.autoApply) saveDate("relative");
};
const datetimeBtn = ref();
const onCustomPeriodSelect = () => {
if (props.autoApply) saveDate("relative-custom");
};
return { getDate, setDate, datetimeBtn, getImageURL };
},
beforeMount() {
if (this.defaultDate) this.selectedDate = cloneDeep(this.defaultDate);
},
computed: {
displayValue() {
if (this.selectedDate.tab === "relative") {
this.$emit("date-change", this.selectedDate);
return `${this.selectedDate.relative.value} ${this.selectedDate.relative.period.label}`;
} else {
if (this.selectedDate.absolute.date != null) {
if (typeof this.selectedDate.absolute.date != "object") {
if (this.selectedDate.absolute.date != "") {
this.selectedDate.absolute.date = {
from: this.selectedDate.absolute.date,
to: this.selectedDate.absolute.date,
};
} else {
const todayDate = new Date().toLocaleDateString("en-ZA");
this.selectedDate.absolute.date = {
from: todayDate,
to: todayDate,
};
}
}
const setRelativeTime = (period) => {
const periodString = period?.match(/(\d+)([mhdwM])/);
this.$emit("date-change", this.selectedDate);
return `${this.selectedDate.absolute.date.from} ${this.selectedDate.absolute.startTime} - ${this.selectedDate.absolute.date.to} ${this.selectedDate.absolute.endTime}`;
} else {
const todayDate = new Date().toLocaleDateString("en-ZA");
this.selectedDate.absolute.date = {
from: todayDate,
to: todayDate,
};
if (periodString) {
const periodValue = periodString[1];
const periodUnit = periodString[2];
this.$emit("date-change", this.selectedDate);
return `${todayDate} ${this.selectedDate.absolute.startTime} - ${todayDate} ${this.selectedDate.absolute.endTime}`;
const periodUnits = ["m", "h", "d", "w", "M"];
if (periodUnits.includes(periodUnit)) {
relativePeriod.value = periodUnit;
}
if (periodValue) {
relativeValue.value = parseInt(periodValue);
}
}
},
calculateMaxValue() {
if (
this.selectedDate.relative.period.value == "Minutes" &&
this.selectedDate.relative.value > 60
) {
this.selectedDate.relative.value = 60;
return false;
} else if (
this.selectedDate.relative.period.value == "Hours" &&
this.selectedDate.relative.value > 24
) {
this.selectedDate.relative.value = 24;
return false;
} else if (
this.selectedDate.relative.period.value == "Days" &&
this.selectedDate.relative.value > 31
) {
this.selectedDate.relative.value = 31;
return false;
} else if (
this.selectedDate.relative.period.value == "Weeks" &&
this.selectedDate.relative.value > 6
) {
this.selectedDate.relative.value = 6;
return false;
} else if (
this.selectedDate.relative.period.value == "Months" &&
this.selectedDate.relative.value > 6
) {
this.selectedDate.relative.value = 6;
return false;
};
const setAbsoluteTime = (startTime, endTime) => {
if (!startTime || !endTime) {
var dateString = new Date().toLocaleDateString("en-ZA");
selectedDate.value.from = dateString;
selectedDate.value.to = dateString;
selectedTime.value.startTime = "00:00";
selectedTime.value.endTime = "23:59";
return;
}
return false;
},
const startDateTime = convertUnixTime(startTime);
const endDateTime = convertUnixTime(endTime);
selectedDate.value.from = startDateTime.date;
selectedDate.value.to = endDateTime.date;
selectedTime.value.startTime = startDateTime.time;
selectedTime.value.endTime = endDateTime.time;
};
function convertUnixTime(unixTimeMicros) {
// Convert microseconds to milliseconds and create a new Date object
var date = new Date(unixTimeMicros / 1000);
// Extract hour, minute, day, month, and year
const hours = ("0" + date.getHours()).slice(-2); // pad with leading zero if needed
const minutes = ("0" + date.getMinutes()).slice(-2); // pad with leading zero if needed
// Build formatted strings
var dateString = new Date(date).toLocaleDateString("en-ZA");
var timeString = hours + ":" + minutes;
// Return both strings
return { date: dateString, time: timeString };
}
const refresh = () => {
saveDate();
};
const saveDate = (dateType) => {
displayValue.value = getDisplayValue();
const date = getConsumableDateTime();
date["valueType"] = dateType || selectedType.value;
emit("on:date-change", date);
};
const onBeforeShow = () => {
// if (props.modelValue) selectedDate.value = cloneDeep(props.modelValue);
};
const getPeriodLabel = computed(() => {
const periodMapping = {
m: "Minutes",
h: "Hours",
d: "Days",
w: "Weeks",
M: "Months",
};
return periodMapping[relativePeriod.value];
});
const getConsumableDateTime = () => {
if (selectedType.value == "relative") {
let period = getPeriodLabel.value.toLowerCase();
let periodValue = relativeValue.value;
// quasar does not support arithmetic on weeks. convert to days.
if (relativePeriod.value == "w") {
period = "days";
periodValue = periodValue * 7;
}
const subtractObject = '{"' + period + '":' + periodValue + "}";
const endTimeStamp = new Date();
const startTimeStamp = date.subtractFromDate(
endTimeStamp,
JSON.parse(subtractObject)
);
return {
startTime: new Date(startTimeStamp).getTime() * 1000,
endTime: new Date(endTimeStamp).getTime() * 1000,
relativeTimePeriod: relativeValue.value + relativePeriod.value,
};
} else {
let start, end;
if (!selectedDate.value.from && !selectedTime.value.startTime) {
start = new Date();
} else {
start = new Date(
selectedDate.value.from + " " + selectedTime.value.startTime
);
}
if (selectedDate.value.to == "" && selectedTime.value.endTime == "") {
end = new Date();
} else {
end = new Date(
selectedDate.value.to + " " + selectedTime.value.endTime
);
}
const rVal = {
startTime: new Date(start).getTime() * 1000,
endTime: new Date(end).getTime() * 1000,
relativeTimePeriod: null,
};
return rVal;
}
};
const getDisplayValue = () => {
if (selectedType.value === "relative") {
return `Past ${relativeValue.value} ${getPeriodLabel.value}`;
} else {
if (selectedDate.value != null) {
return `${selectedDate.value.from} ${selectedTime.value.startTime} - ${selectedDate.value.to} ${selectedTime.value.endTime}`;
} else {
const todayDate = new Date().toLocaleDateString("en-ZA");
return `${todayDate} ${selectedTime.value.startTime} - ${todayDate} ${selectedTime.value.endTime}`;
}
}
};
return {
datetimeBtn,
getImageURL,
onCustomPeriodSelect,
setRelativeDate,
relativePeriods,
relativeDates,
saveDate,
onBeforeShow,
selectedType,
selectedTime,
selectedDate,
relativePeriod,
relativeValue,
getPeriodLabel,
displayValue,
refresh,
};
},
});
</script>
<style lang="scss" scoped>
.date-time-container {
.date-time-button {
&.relativetype {
}
&.absolutetype {
min-width: 286px;
}
}
}
</style>
<style lang="scss">
.q-btn--rectangle {
border-radius: 3px;
@@ -375,10 +542,6 @@ export default defineComponent({
.block {
font-weight: 600;
&::before {
content: "Past ";
}
}
}
}
@@ -388,8 +551,8 @@ export default defineComponent({
.tab-button {
&.q-btn {
padding-bottom: 0.6rem;
padding-top: 0.6rem;
padding-bottom: 0.1rem;
padding-top: 0.1rem;
font-size: 0.75rem;
font-weight: 700;
@@ -452,7 +615,7 @@ export default defineComponent({
height: 32px;
width: 32px;
// border: $secondary;
background: rgba(0,0,0,0.07);
background: rgba(0, 0, 0, 0.07);
}
.rp-selector-selected {
+4 -16
View File
@@ -97,22 +97,10 @@ const defaultObject = {
histogram: <any>{},
editorValue: "",
datetime: {
tab: "relative",
relative: {
period: {
label: "Minutes",
value: "Minutes",
},
value: 15,
},
absolute: {
date: {
from: "",
to: "",
},
startTime: "00:00",
endTime: "23:59",
},
startTime: 0,
endTime: 0,
relativeTimePeriod: "15m",
type: "relative",
},
searchAround: {
indexTimestamp: 0,
+4 -16
View File
@@ -62,22 +62,10 @@ const defaultObject = {
streamResults: <any>[],
histogram: <any>{},
datetime: {
tab: "relative",
relative: {
period: {
label: "Minutes",
value: "Minutes",
},
value: 15,
},
absolute: {
date: {
from: "",
to: "",
},
startTime: "00:00",
endTime: "23:59",
},
startTime: "",
endTime: "",
relativeTimePeriod: "15m",
type: "relative",
},
},
};
+28
View File
@@ -0,0 +1,28 @@
import { useQuasar } from "quasar";
const useNotifications = () => {
const quasar = useQuasar();
const showErrorNotification = (message: string) => {
return quasar.notify({
type: "negative",
message: message,
timeout: 5000,
actions: [
{
icon: "close",
color: "white",
handler: () => {
/* ... */
},
},
],
});
};
return {
showErrorNotification,
};
};
export default useNotifications;
+6 -6
View File
@@ -15,7 +15,10 @@ const usePromlqSuggestions = () => {
open: (val: string) => {},
close: (val: string) => {},
},
dateTime: null,
dateTime: {
startTime: new Date().getTime() * 1000,
endTime: new Date().getTime() * 1000,
},
});
const autoCompleteKeywords: any = ref([]);
const store = useStore();
@@ -129,11 +132,8 @@ const usePromlqSuggestions = () => {
autoCompleteData.value.query
);
autoCompletePromqlKeywords.value = [];
const timestamps = getConsumableDateTime(autoCompleteData.value.dateTime);
const startISOTimestamp: any =
new Date(timestamps.start_time.toISOString()).getTime() * 1000;
const endISOTimestamp: any =
new Date(timestamps.end_time.toISOString()).getTime() * 1000;
const startISOTimestamp: any = autoCompleteData.value.dateTime.startTime;
const endISOTimestamp: any = autoCompleteData.value.dateTime.endTime;
// import search service and call search.get_promql_series
if (metricName) labels["__name__"] = metricName;
+4 -16
View File
@@ -94,22 +94,10 @@ const defaultObject = {
histogram: <any>{},
editorValue: "",
datetime: {
tab: "relative",
relative: {
period: {
label: "Minutes",
value: "Minutes",
},
value: 15,
},
absolute: {
date: {
from: "",
to: "",
},
startTime: "00:00",
endTime: "23:59",
},
startTime: 0,
endTime: 0,
relativeTimePeriod: "15m",
type: "relative",
},
searchAround: {
indexTimestamp: 0,
+77 -80
View File
@@ -213,7 +213,7 @@ import {
} from "@/utils/zincutils";
import segment from "@/services/segment_analytics";
import config from "@/aws-exports";
import { logsErrorMessage, showErrorNotification } from "@/utils/common";
import { logsErrorMessage } from "@/utils/common";
import {
verifyOrganizationStatus,
convertTimeFromMicroToMilli,
@@ -221,7 +221,11 @@ import {
import {
getQueryParamsForDuration,
getDurationObjectFromParams,
getConsumableRelativeTime,
} from "@/utils/date";
import useNotifications from "@/composables/useNotifications";
import { cloneDeep } from "lodash-es";
import { d } from "msw/lib/SetupApi-8ab693f7";
export default defineComponent({
name: "PageSearch",
@@ -294,7 +298,7 @@ export default defineComponent({
const searchBarRef = ref(null);
const parser = new Parser();
const expandedLogs = ref({});
const { showErrorNotification } = useNotifications();
const fieldValues = ref({});
searchObj.organizationIdetifier =
@@ -302,11 +306,11 @@ export default defineComponent({
const getStreamType = computed(() => searchObj.data.stream.streamType);
function Notify() {
return $q.notify({
function Notify(timeout: number | undefined | null = 0) {
const notify = {
type: "positive",
message: "Waiting for response...",
timeout: 10000,
timeout: timeout,
actions: [
{
icon: "close",
@@ -316,7 +320,9 @@ export default defineComponent({
},
},
],
});
};
return $q.notify(notify);
}
function getQueryTransform() {
@@ -588,68 +594,47 @@ export default defineComponent({
},
};
var timestamps: any = getConsumableDateTime();
var timestamps: any = searchObj.data.datetime.relativeTimePeriod
? getConsumableRelativeTime(
searchObj.data.datetime.relativeTimePeriod
)
: cloneDeep(searchObj.data.datetime);
if (
timestamps.start_time != "Invalid Date" &&
timestamps.end_time != "Invalid Date"
timestamps.startTime != "Invalid Date" &&
timestamps.endTime != "Invalid Date"
) {
searchObj.meta.resultGrid.chartKeyFormat = "HH:mm:ss";
const startISOTimestamp: any =
new Date(timestamps.start_time.toISOString()).getTime() * 1000;
const endISOTimestamp: any =
new Date(timestamps.end_time.toISOString()).getTime() * 1000;
// let timeRangeFilter: String =
// "(_timestamp >= [START_TIME] AND _timestamp < [END_TIME])";
// // let timeRangeFilter: String =
// // "time_range(\"_timestamp\", '[START_TIME]','[END_TIME]')";
// timeRangeFilter = timeRangeFilter.replace(
// "[START_TIME]",
// startISOTimestamp
// );
// timeRangeFilter = timeRangeFilter.replace(
// "[END_TIME]",
// endISOTimestamp
// );
// if (query.trim() != "") {
// query += " and " + timeRangeFilter;
// } else {
// query = timeRangeFilter;
// }
req.query.start_time = startISOTimestamp;
req.query.end_time = endISOTimestamp;
req.query.start_time = timestamps.startTime;
req.query.end_time = timestamps.endTime;
searchObj.meta.resultGrid.chartInterval = "10 second";
if (timestamps.end_time - timestamps.start_time >= 1000 * 60 * 30) {
if (req.query.end_time - req.query.start_time >= 1000 * 60 * 30) {
searchObj.meta.resultGrid.chartInterval = "15 second";
searchObj.meta.resultGrid.chartKeyFormat = "HH:mm:ss";
}
if (timestamps.end_time - timestamps.start_time >= 1000 * 60 * 60) {
if (req.query.end_time - req.query.start_time >= 1000 * 60 * 60) {
searchObj.meta.resultGrid.chartInterval = "30 second";
searchObj.meta.resultGrid.chartKeyFormat = "HH:mm:ss";
}
if (timestamps.end_time - timestamps.start_time >= 1000 * 3600 * 2) {
if (req.query.end_time - req.query.start_time >= 1000 * 3600 * 2) {
searchObj.meta.resultGrid.chartInterval = "1 minute";
searchObj.meta.resultGrid.chartKeyFormat = "MM-DD HH:mm";
}
if (timestamps.end_time - timestamps.start_time >= 1000 * 3600 * 6) {
if (req.query.end_time - req.query.start_time >= 1000 * 3600 * 6) {
searchObj.meta.resultGrid.chartInterval = "5 minute";
searchObj.meta.resultGrid.chartKeyFormat = "MM-DD HH:mm";
}
if (timestamps.end_time - timestamps.start_time >= 1000 * 3600 * 24) {
if (req.query.end_time - req.query.start_time >= 1000 * 3600 * 24) {
searchObj.meta.resultGrid.chartInterval = "30 minute";
searchObj.meta.resultGrid.chartKeyFormat = "MM-DD HH:mm";
}
if (timestamps.end_time - timestamps.start_time >= 1000 * 86400 * 7) {
if (req.query.end_time - req.query.start_time >= 1000 * 86400 * 7) {
searchObj.meta.resultGrid.chartInterval = "1 hour";
searchObj.meta.resultGrid.chartKeyFormat = "MM-DD HH:mm";
}
if (
timestamps.end_time - timestamps.start_time >=
1000 * 86400 * 30
) {
if (req.query.end_time - req.query.start_time >= 1000 * 86400 * 30) {
searchObj.meta.resultGrid.chartInterval = "1 day";
searchObj.meta.resultGrid.chartKeyFormat = "YYYY-MM-DD";
}
@@ -762,13 +747,22 @@ export default defineComponent({
return req;
} catch (e) {
searchObj.loading = false;
console.log(e);
showErrorNotification("Invalid SQL Syntax");
}
}
function restoreUrlQueryParams() {
const queryParams = router.currentRoute.value.query;
const date = getDurationObjectFromParams(queryParams);
if (!queryParams.stream) {
return;
}
const date = {
startTime: queryParams.from,
endTime: queryParams.to,
relativeTimePeriod: queryParams.period || null,
type: queryParams.period ? "relative" : "absolute",
};
if (date) {
searchObj.data.datetime = date;
}
@@ -783,17 +777,19 @@ export default defineComponent({
}
function updateUrlQueryParams() {
const date = getQueryParamsForDuration(searchObj.data.datetime);
const date = searchObj.data.datetime;
const query = {
stream: searchObj.data.stream.selectedStream.label,
};
if (date.period) {
query["period"] = date.period;
}
if (date.from && date.to) {
query["from"] = date.from;
query["to"] = date.to;
if (date.type == "relative") {
query["period"] = date.relativeTimePeriod;
} else {
query["from"] = date.startTime;
query["to"] = date.endTime;
}
query["refresh"] = searchObj.meta.refreshInterval;
if (searchObj.data.query) {
@@ -807,6 +803,7 @@ export default defineComponent({
}
function getQueryData() {
const dismiss = Notify();
try {
if (searchObj.data.stream.selectedStream.value == "") {
return false;
@@ -834,7 +831,6 @@ export default defineComponent({
};
// searchObj.data.editorValue = "";
}
dismiss = Notify();
const queryReq = buildSearch();
@@ -904,15 +900,12 @@ export default defineComponent({
// });
});
} catch (e) {
dismiss();
searchObj.loading = false;
showErrorNotification("Error while searching");
}
}
const resetFieldValues = () => {
fieldValues.value = {};
};
const updateFieldValues = (data) => {
const excludedFields = [
store.state.zoConfig.timestamp_column,
@@ -937,6 +930,10 @@ export default defineComponent({
});
};
const resetFieldValues = () => {
fieldValues.value = {};
};
function extractFields() {
try {
searchObj.data.stream.selectedStreamFields = [];
@@ -1220,7 +1217,7 @@ export default defineComponent({
document.getElementById("secondLevel");
if (secondWrapperElement != null) {
rect = secondWrapperElement.getBoundingClientRect();
secondWrapperElement.style.height = `calc(100vh - ${Math.round(
secondWrapperElement.style.height = `calc(100vh - ${Math.ceil(
rect.top
)}px)`;
}
@@ -1228,7 +1225,7 @@ export default defineComponent({
const thirdWrapperElement: any = document.getElementById("thirdLevel");
if (thirdWrapperElement != null) {
rect = thirdWrapperElement.getBoundingClientRect();
thirdWrapperElement.style.height = `calc(100vh - ${Math.round(
thirdWrapperElement.style.height = `calc(100vh - ${Math.ceil(
rect.top
)}px)`;
}
@@ -1236,23 +1233,23 @@ export default defineComponent({
const GridElement: any = document.getElementById("searchGridComponent");
if (GridElement != null) {
rect = GridElement.getBoundingClientRect();
GridElement.style.height = `calc(100vh - ${Math.round(rect.top)}px)`;
GridElement.style.height = `calc(100vh - ${Math.ceil(rect.top)}px)`;
}
const FLElement = document.getElementById("fieldList");
if (FLElement != null) {
rect = FLElement.getBoundingClientRect();
FLElement.style.height = `calc(100vh - ${Math.round(rect.top)}px)`;
FLElement.style.height = `calc(100vh - ${Math.ceil(rect.top)}px)`;
}
const logPagesecondWrapperElement: any =
document.getElementById("logPage");
if (logPagesecondWrapperElement != null) {
rect = logPagesecondWrapperElement.getBoundingClientRect();
logPagesecondWrapperElement.style.height = `calc(100vh - ${Math.round(
rect.top
logPagesecondWrapperElement.style.height = `calc(100vh - ${Math.ceil(
rect.top + 1
)}px)`;
logPagesecondWrapperElement.style.minHeight = `calc(100vh - ${Math.round(
logPagesecondWrapperElement.style.minHeight = `calc(100vh - ${Math.ceil(
rect.top + 20
)}px)`;
}
@@ -1323,8 +1320,8 @@ export default defineComponent({
};
const searchAroundData = (obj: any) => {
const dismiss = Notify();
try {
dismiss = Notify();
searchObj.data.errorCode = 0;
let query_context = "";
let query = searchObj.data.query;
@@ -1450,6 +1447,7 @@ export default defineComponent({
// });
});
} catch (e) {
dismiss();
console.log(e.message);
searchObj.loading = false;
showErrorNotification("Search around request failed.");
@@ -1597,21 +1595,19 @@ export default defineComponent({
this.loadPageData();
}
},
changeStream() {
if (this.searchObj.data.stream.selectedStream.hasOwnProperty("value")) {
this.searchObj.data.tempFunctionContent = "";
this.searchBarRef.resetFunctionContent();
this.searchObj.data.query = "";
this.setQuery(this.searchObj.meta.sqlMode);
setTimeout(() => {
this.runQueryFn();
}, 500);
}
},
changeRelativeDate() {
if (this.searchObj.data.datetime.tab == "relative") {
this.runQueryFn();
}
changeStream: {
handler(stream, streamOld) {
if (this.searchObj.data.stream.selectedStream.hasOwnProperty("value")) {
this.searchObj.data.tempFunctionContent = "";
this.searchBarRef.resetFunctionContent();
if (streamOld.value) this.searchObj.data.query = "";
if (streamOld.value) this.setQuery(this.searchObj.meta.sqlMode);
setTimeout(() => {
this.runQueryFn();
}, 500);
}
},
immediate: false,
},
updateSelectedColumns() {
this.searchObj.meta.resultGrid.manualRemoveFields = true;
@@ -1629,6 +1625,7 @@ export default defineComponent({
this.refreshData();
},
fullSQLMode(newVal) {
this.reDrawGrid();
this.setQuery(newVal);
},
getStreamType() {
+43 -25
View File
@@ -14,7 +14,10 @@
-->
<template>
<div class="column index-menu" :class="store.state.theme == 'dark' ? 'theme-dark' : 'theme-light'">
<div
class="column index-menu"
:class="store.state.theme == 'dark' ? 'theme-dark' : 'theme-light'"
>
<div>
<q-select
data-test="log-search-index-list-select-stream"
@@ -98,9 +101,7 @@
props.row.name
)
"
:name="
outlinedVisibility
"
:name="outlinedVisibility"
size="1.1rem"
title="Add field to table"
@click.stop="clickFieldFn(props.row, props.pageIndex)"
@@ -228,7 +229,14 @@
{{ value.count }}
</div>
</div>
<div class="flex row" :class="store.state.theme ==='dark'?'text-white':'text-black'">
<div
class="flex row"
:class="
store.state.theme === 'dark'
? 'text-white'
: 'text-black'
"
>
<q-btn
class="q-mr-xs"
size="6px"
@@ -254,10 +262,10 @@
title="Exclude Term"
round
>
<q-icon>
<NotEqualIcon></NotEqualIcon>
</q-icon>
</q-btn>
<q-icon>
<NotEqualIcon></NotEqualIcon>
</q-icon>
</q-btn>
</div>
</q-item>
</q-list>
@@ -292,7 +300,7 @@
</template>
<script lang="ts">
import { defineComponent, onActivated, ref, type Ref, watch } from "vue";
import { defineComponent, ref, type Ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useStore } from "vuex";
import { useQuasar } from "quasar";
@@ -305,11 +313,15 @@ import {
formatLargeNumber,
} from "../../utils/zincutils";
import streamService from "../../services/stream";
import { getConsumableDateTime } from "@/utils/commons";
import { Parser } from "node-sql-parser";
import { outlinedAdd, outlinedVisibility, outlinedVisibilityOff } from '@quasar/extras/material-icons-outlined'
import {
outlinedAdd,
outlinedVisibility,
outlinedVisibilityOff,
} from "@quasar/extras/material-icons-outlined";
import EqualIcon from "@/components/icons/EqualIcon.vue";
import NotEqualIcon from "@/components/icons/NotEqualIcon.vue";
import { getConsumableRelativeTime } from "@/utils/date";
interface Filter {
fieldName: string;
@@ -318,7 +330,7 @@ interface Filter {
}
export default defineComponent({
name: "ComponentSearchIndexSelect",
components:{ EqualIcon, NotEqualIcon },
components: { EqualIcon, NotEqualIcon },
setup() {
const store = useStore();
const router = useRouter();
@@ -394,7 +406,9 @@ export default defineComponent({
return;
}
let timestamps = getConsumableDateTime(searchObj.data.datetime);
let timestamps = getConsumableRelativeTime(
searchObj.data.datetime.relativeTimePeriod
);
if (searchObj.data.stream.streamType === "enrichment_tables") {
const stream = searchObj.data.streamResults.list.find(
@@ -403,20 +417,24 @@ export default defineComponent({
);
if (stream.stats) {
timestamps = {
start_time: new Date(
convertTimeFromMicroToMilli(stream.stats.doc_time_min - 300000000)
),
end_time: new Date(
convertTimeFromMicroToMilli(stream.stats.doc_time_max + 300000000)
),
startTime:
new Date(
convertTimeFromMicroToMilli(
stream.stats.doc_time_min - 300000000
)
).getTime() * 1000,
endTime:
new Date(
convertTimeFromMicroToMilli(
stream.stats.doc_time_max + 300000000
)
).getTime() * 1000,
};
}
}
const startISOTimestamp: any =
new Date(timestamps.start_time.toISOString()).getTime() * 1000;
const endISOTimestamp: any =
new Date(timestamps.end_time.toISOString()).getTime() * 1000;
const startISOTimestamp: number = timestamps?.startTime || 0;
const endISOTimestamp: number = timestamps?.endTime || 0;
fieldValues.value[name] = {
isLoading: true,
@@ -657,10 +675,10 @@ export default defineComponent({
&:hover {
.field-container {
// background-color: #ffffff;
}
}
}
}
}
.theme-dark {
.field_list {
+25 -12
View File
@@ -107,9 +107,19 @@
></q-btn>
<div class="float-left">
<date-time
:default-date="searchObj.data.datetime"
auto-apply
:default-type="
searchObj.data.datetime.relativeTimePeriod
? 'relative'
: 'absolute'
"
:default-absolute-time="{
startTime: searchObj.data.datetime.startTime,
endTime: searchObj.data.datetime.endTime,
}"
:default-relative-time="searchObj.data.datetime.relativeTimePeriod"
data-test="logs-search-bar-date-time-dropdown"
@date-change="updateDateTime"
@on:date-change="updateDateTime"
/>
</div>
<div class="search-time q-pl-sm float-left q-mr-sm">
@@ -208,6 +218,7 @@ import AutoRefreshInterval from "@/components/AutoRefreshInterval.vue";
import stream from "@/services/stream";
import { getConsumableDateTime } from "@/utils/commons";
import useSqlSuggestions from "@/composables/useSuggestions";
import { cloneDeep } from "lodash-es";
const defaultValue: any = () => {
return {
@@ -289,7 +300,7 @@ export default defineComponent({
getSuggestions,
updateFieldKeywords,
updateFunctionKeywords,
} = useSqlSuggestions(searchObj.data.datetime);
} = useSqlSuggestions();
const refreshTimeChange = (item) => {
searchObj.meta.refreshInterval = item.value;
@@ -367,26 +378,28 @@ export default defineComponent({
};
const updateDateTime = (value: object) => {
searchObj.data.datetime = value;
searchObj.data.datetime = {
startTime: value.startTime,
endTime: value.endTime,
relativeTimePeriod: value.relativeTimePeriod
? value.relativeTimePeriod
: searchObj.data.datetime.relativeTimePeriod,
type: value.relativeTimePeriod ? "relative" : "absolute",
};
if (config.isCloud == "true" && value.userChangedValue) {
let dateTimeVal;
if (value.tab === "relative") {
dateTimeVal = value.relative;
} else {
dateTimeVal = value.absolute;
}
segment.track("Button Click", {
button: "Date Change",
tab: value.tab,
value: dateTimeVal,
value: value,
//user_org: this.store.state.selectedOrganization.identifier,
//user_id: this.store.state.userInfo.email,
stream_name: searchObj.data.stream.selectedStream.value,
page: "Search Logs",
});
}
if (value.valueType === "relative") emit("searchdata");
};
const udpateQuery = () => {
+6 -18
View File
@@ -114,7 +114,8 @@
? column.prop(row)
: searchObj.data.resultGrid.columns.length > 2 &&
(column.prop(row, column.name)?.length || 0) > 100
? (column.prop(row, column.name)?.substr(0, 100) || '') + '...'
? (column.prop(row, column.name)?.substr(0, 100) || '') +
'...'
: column.name != '@timestamp'
? row[column.name]
: column.prop(row, column.name)
@@ -345,23 +346,10 @@ export default defineComponent({
},
onChartUpdate({ start, end }: { start: any; end: any }) {
this.searchObj.meta.showDetailTab = false;
this.searchObj.runQuery = true;
this.searchObj.data.datetime.tab = "absolute";
this.searchObj.data.datetime.absolute.date.from = start.split(" ")[0];
this.searchObj.data.datetime.absolute.date.to = end.split(" ")[0];
this.searchObj.data.datetime.absolute.startTime =
start.split(" ")[1].split(":")[0] +
":" +
start.split(" ")[1].split(":")[1] +
":" +
start.split(" ")[1].split(":")[2];
this.searchObj.data.datetime.absolute.endTime =
end.split(" ")[1].split(":")[0] +
":" +
end.split(" ")[1].split(":")[1] +
":" +
end.split(" ")[1].split(":")[2];
this.searchObj.data.datetime.type = "absolute";
this.searchObj.data.datetime.startTime = new Date(start).getTime() * 1000;
this.searchObj.data.datetime.endTime = new Date(end).getTime() * 1000;
console.log("onChartUpdate", this.searchObj.data.datetime);
this.searchObj.runQuery = true;
this.$emit("update:datetime");
},
+77 -38
View File
@@ -16,7 +16,7 @@
<!-- eslint-disable vue/attribute-hyphenation -->
<!-- eslint-disable vue/v-on-event-hyphenation -->
<template>
<q-page class="metrics-page" id="logPage">
<q-page class="metrics-page" id="metricsPage">
<div class="row scroll" style="width: 100%">
<!-- Note: Splitter max-height to be dynamically calculated with JS -->
<q-splitter
@@ -44,9 +44,22 @@
class="text-right q-px-lg q-py-sm flex align-center justify-end metrics-date-time"
>
<date-time
:default-date="searchObj.data.datetime"
ref="metricsDateTimeRef"
auto-apply
:default-type="
searchObj.data.datetime.relativeTimePeriod
? 'relative'
: 'absolute'
"
:default-absolute-time="{
startTime: searchObj.data.datetime.startTime,
endTime: searchObj.data.datetime.endTime,
}"
:default-relative-time="
searchObj.data.datetime.relativeTimePeriod
"
data-test="logs-search-bar-date-time-dropdown"
@date-change="updateDateTime"
@on:date-change="updateDateTime"
/>
<auto-refresh-interval
class="q-pr-sm"
@@ -188,7 +201,7 @@ import useMetrics from "@/composables/useMetrics";
import { Parser } from "node-sql-parser";
import streamService from "@/services/stream";
import { b64EncodeUnicode } from "@/utils/zincutils";
import { b64DecodeUnicode, b64EncodeUnicode } from "@/utils/zincutils";
import segment from "@/services/segment_analytics";
import config from "@/aws-exports";
import DateTime from "@/components/DateTime.vue";
@@ -201,12 +214,9 @@ import { cloneDeep } from "lodash-es";
import AddToDashboard from "./AddToDashboard.vue";
import { addPanel, getPanelId } from "@/utils/commons";
import usePromqlSuggestions from "@/composables/usePromqlSuggestions";
import {
getQueryParamsForDuration,
getDurationObjectFromParams,
} from "@/utils/date";
import { showErrorNotification } from "@/utils/common";
import { b64DecodeUnicode } from "@/utils/zincutils";
import useNotifications from "@/composables/useNotifications";
import { getConsumableRelativeTime } from "@/utils/date";
import { on } from "events";
export default defineComponent({
name: "AppMetrics",
@@ -258,6 +268,7 @@ export default defineComponent({
const promqlKeywords = ref([]);
const chartData = ref({});
const { showErrorNotification } = useNotifications();
searchObj.organizationIdetifier =
store.state.selectedOrganization.identifier;
@@ -541,7 +552,12 @@ export default defineComponent({
}
// dismiss = Notify();
dashboardPanelData.meta.dateTime = {
start_time: new Date(searchObj.data.datetime.startTime / 1000),
end_time: new Date(searchObj.data.datetime.endTime / 1000),
};
chartData.value = cloneDeep(dashboardPanelData.data);
console.log(chartData.value);
updateUrlQueryParams();
} catch (e) {
searchObj.loading = false;
@@ -598,27 +614,28 @@ export default defineComponent({
const setQuery = () => {};
const updateDateTime = (value: any) => {
searchObj.data.datetime = value;
const timestamp = getConsumableDateTime(searchObj.data.datetime);
dashboardPanelData.meta.dateTime = timestamp;
searchObj.data.datetime = {
startTime: value.startTime,
endTime: value.endTime,
relativeTimePeriod: value.relativeTimePeriod
? value.relativeTimePeriod
: searchObj.data.datetime.relativeTimePeriod,
type: value.relativeTimePeriod ? "relative" : "absolute",
};
if (config.isCloud == "true" && value.userChangedValue) {
let dateTimeVal;
if (value.tab === "relative") {
dateTimeVal = value.relative;
} else {
dateTimeVal = value.absolute;
}
segment.track("Button Click", {
button: "Date Change",
tab: value.tab,
value: dateTimeVal,
metric_name: searchObj.data.metrics.selectedMetric,
page: "Search Metrics",
value: value,
//user_org: this.store.state.selectedOrganization.identifier,
//user_id: this.store.state.userInfo.email,
stream_name: searchObj.data.stream.selectedStream.value,
page: "Search Logs",
});
}
if (value.valueType === "relative") runQuery();
};
const updateQueryValue = async (event, value) => {
@@ -674,12 +691,23 @@ export default defineComponent({
});
};
const onMetricChange = async (metric) => {
metricsQueryEditorRef.value.setValue(metric);
};
function restoreUrlQueryParams() {
const queryParams = router.currentRoute.value.query;
const date = getDurationObjectFromParams(queryParams);
if (!queryParams.stream) {
return;
}
const date = {
startTime: queryParams.from,
endTime: queryParams.to,
relativeTimePeriod: queryParams.period || null,
type: queryParams.period ? "relative" : "absolute",
};
if (date) {
searchObj.data.datetime = date;
updateDateTime(date);
}
if (queryParams.query) {
searchObj.data.query = b64DecodeUnicode(queryParams.query);
@@ -689,24 +717,28 @@ export default defineComponent({
searchObj.meta.refreshInterval = queryParams.refresh;
}
}
function updateUrlQueryParams() {
try {
const date = getQueryParamsForDuration(searchObj.data.datetime);
const date = searchObj.data.datetime;
const query = {
stream: searchObj.data.metrics.selectedMetric,
};
if (date.period) {
query["period"] = date.period;
}
if (date.from && date.to) {
query["from"] = date.from;
query["to"] = date.to;
if (date.type == "relative") {
query["period"] = date.relativeTimePeriod;
} else {
query["from"] = date.startTime;
query["to"] = date.endTime;
}
query["refresh"] = searchObj.meta.refreshInterval;
if (searchObj.data.query) {
query["query"] = b64EncodeUnicode(searchObj.data.query);
}
query["org_identifier"] = store.state.selectedOrganization.identifier;
router.push({ query });
} catch (err) {
console.log(err);
@@ -735,6 +767,7 @@ export default defineComponent({
addPanelToDashboard,
promqlKeywords,
autoCompletePromqlKeywords,
onMetricChange,
};
},
computed: {
@@ -744,7 +777,7 @@ export default defineComponent({
changeOrganization() {
return this.store.state.selectedOrganization.identifier;
},
selectedMetrics() {
selectedMetric() {
return this.searchObj.data.metrics.selectedMetric;
},
changeRelativeDate() {
@@ -766,10 +799,16 @@ export default defineComponent({
);
this.loadPageData();
},
changeRelativeDate() {
if (this.searchObj.data.datetime.tab == "relative") {
this.runQuery();
}
selectedMetric: {
deep: true,
handler: function (metric) {
if (this.searchObj.data.metrics.selectedMetric) {
this.onMetricChange(metric);
setTimeout(() => {
this.runQuery();
}, 500);
}
},
},
changeRefreshInterval() {
this.updateUrlQueryParams();
+11 -11
View File
@@ -33,6 +33,7 @@
hide-selected
fill-input
@filter="filterMetrics"
@update:model-value="onMetricChange"
>
<template #no-option>
<q-item>
@@ -194,12 +195,12 @@ import { useRouter } from "vue-router";
import useMetrics from "../../composables/useMetrics";
import { formatLargeNumber, getImageURL } from "../../utils/zincutils";
import { getConsumableDateTime } from "@/utils/commons";
import useDashboardPanelData from "@/composables/useDashboardPanel";
import stream from "@/services/stream";
export default defineComponent({
name: "MetricsList",
setup() {
emits: ["update:change-metric"],
setup(props, { emit }) {
const store = useStore();
const router = useRouter();
const { t } = useI18n();
@@ -215,7 +216,6 @@ export default defineComponent({
values: { key: string; count: number | string }[];
};
}> = ref({});
const { dashboardPanelData } = useDashboardPanelData();
watch(
() => searchObj.data.metrics.metricList.length,
@@ -263,19 +263,14 @@ export default defineComponent({
};
const openFilterCreator = (event: any, { name }: any) => {
console.log("open filter creator", name);
let timestamps = getConsumableDateTime(searchObj.data.datetime);
const startISOTimestamp: any =
new Date(timestamps.start_time.toISOString()).getTime() * 1000;
const endISOTimestamp: any =
new Date(timestamps.end_time.toISOString()).getTime() * 1000;
const startISOTimestamp: any = searchObj.data.datetime.startTime;
const endISOTimestamp: any = searchObj.data.datetime.endTime;
metricLabelValues.value[name] = {
isLoading: true,
values: [],
};
try {
stream
.fieldValues({
@@ -310,6 +305,10 @@ export default defineComponent({
}
};
const onMetricChange = () => {
updateMetricLabels();
};
return {
quasar,
t,
@@ -324,6 +323,7 @@ export default defineComponent({
filterMetricLabels,
openFilterCreator,
metricLabelValues,
onMetricChange,
};
},
});
-303
View File
@@ -1,303 +0,0 @@
<!-- Copyright 2022 Zinc Labs Inc. and Contributors
Licensed 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.
-->
<!-- eslint-disable vue/v-on-event-hyphenation -->
<!-- eslint-disable vue/attribute-hyphenation -->
<template>
<div ref="resultContainer" class="col column oveflow-hidden q-px-md q-pt-sm">
<div class="text-semi-bold text-h6 q-pl-sm q-pb-sm">
{{ searchObj.data.metrics.selectedMetric }}
</div>
<div class="search-list" style="width: 100%">
<MetricLineChart
data-test="logs-search-result-bar-chart"
ref="plotChart"
:data="searchObj.data.histogram.data"
:title="searchObj.data.histogram.layout.title"
@updated:chart="onChartUpdate"
></MetricLineChart>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref } from "vue";
import { useQuasar } from "quasar";
import { useStore } from "vuex";
import { useI18n } from "vue-i18n";
import { byString } from "../../utils/json";
import useMetrics from "../../composables/useMetrics";
import { getImageURL } from "../../utils/zincutils";
import MetricLineChart from "../../components/MetricLineChart.vue";
export default defineComponent({
name: "MetricsViewer",
components: {
MetricLineChart,
},
emits: [
"update:scroll",
"update:datetime",
"remove:searchTerm",
"search:timeboxed",
],
methods: {
onChartUpdate({ start, end }: { start: any; end: any }) {
this.searchObj.data.datetime.tab = "absolute";
this.searchObj.data.datetime.absolute.date.from = start.split(" ")[0];
this.searchObj.data.datetime.absolute.date.to = end.split(" ")[0];
this.searchObj.data.datetime.absolute.startTime =
start.split(" ")[1].split(":")[0] +
":" +
start.split(" ")[1].split(":")[1] +
":" +
start.split(" ")[1].split(":")[2];
this.searchObj.data.datetime.absolute.endTime =
end.split(" ")[1].split(":")[0] +
":" +
end.split(" ")[1].split(":")[1] +
":" +
end.split(" ")[1].split(":")[2];
this.$emit("update:datetime");
},
},
setup(props, { emit }) {
// Accessing nested JavaScript objects and arrays by string path
// https://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-and-arrays-by-string-path
const { t } = useI18n();
const store = useStore();
const $q = useQuasar();
const { searchObj } = useMetrics();
const totalHeight = ref(0);
const searchTableRef: any = ref(null);
const plotChart: any = ref(null);
const resultContainer = ref(null);
const reDrawChart = () => {
if (searchObj.data.histogram.data.length) {
plotChart.value.reDraw(searchObj.data.histogram);
plotChart.value.forceReLayout();
}
};
onMounted(() => {
reDrawChart();
});
return {
t,
store,
plotChart,
searchObj,
byString,
searchTableRef,
totalHeight,
reDrawChart,
getImageURL,
resultContainer,
};
},
});
</script>
<style lang="scss" scoped>
.max-result {
width: 170px;
}
.search-list {
width: 100%;
.chart {
width: 100%;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
.my-sticky-header-table {
.q-table__top,
.q-table__bottom,
thead tr:first-child th {
/* bg color is important for th; just specify one */
background-color: white;
}
thead tr th {
position: sticky;
z-index: 1;
}
thead tr:first-child th {
top: 0;
}
/* this is when the loading indicator appears */
&.q-table--loading thead tr:last-child th {
/* height of all previous header rows */
top: 48px;
}
}
.q-table__top {
padding-left: 0;
padding-top: 0;
}
.q-table thead tr,
.q-table tbody td,
.q-table th,
.q-table td {
height: 25px;
padding: 0px 5px;
font-size: 0.75rem;
}
.q-table__bottom {
width: 100%;
}
.q-table__bottom {
min-height: 40px;
padding-top: 0;
padding-bottom: 0;
}
.q-td {
overflow: hidden;
min-width: 100px;
.expanded {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-all;
}
}
.highlight {
background-color: rgb(255, 213, 0);
}
.table-header {
// text-transform: capitalize;
.table-head-chip {
background-color: $accent;
padding: 0px;
.q-chip__content {
margin-right: 0.5rem;
font-size: 0.75rem;
color: $dark;
}
.q-chip__icon--remove {
height: 1rem;
width: 1rem;
opacity: 1;
margin: 0;
&:hover {
opacity: 0.7;
}
}
.q-table th.sortable {
cursor: pointer;
text-transform: capitalize;
font-weight: bold;
}
}
&.isClosable {
padding-right: 26px;
position: relative;
.q-table-col-close {
transform: translateX(26px);
position: absolute;
margin-top: 2px;
color: grey;
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
}
}
.q-table th.sortable {
cursor: pointer;
text-transform: capitalize;
font-weight: bold;
}
}
}
.thead-sticky tr > *,
.tfoot-sticky tr > * {
position: sticky;
opacity: 1;
z-index: 1;
background: #f5f5f5;
color: #090909;
}
.thead-sticky tr:last-child > * {
top: 0;
}
.tfoot-sticky tr:first-child > * {
bottom: 0;
}
.field_list {
padding: 0px;
margin-bottom: 0.125rem;
position: relative;
overflow: visible;
cursor: default;
.field_overlay {
position: absolute;
height: 100%;
right: 0;
top: 0;
background-color: #ffffff;
border-radius: 6px;
padding: 0 6px;
visibility: hidden;
display: flex;
align-items: center;
transition: all 0.3s linear;
.q-icon {
cursor: pointer;
opacity: 0;
transition: all 0.3s linear;
margin: 0 1px;
}
}
&:hover {
.field_overlay {
visibility: visible;
.q-icon {
opacity: 1;
}
}
}
}
</style>
+36 -27
View File
@@ -180,9 +180,12 @@ import {
} from "@/utils/zincutils";
import segment from "@/services/segment_analytics";
import config from "@/aws-exports";
import { logsErrorMessage, showErrorNotification } from "@/utils/common";
import { logsErrorMessage } from "@/utils/common";
import { number } from "@intlify/core-base";
import { stringLiteral } from "@babel/types";
import useNotifications from "@/composables/useNotifications";
import { getConsumableRelativeTime } from "@/utils/date";
import { cloneDeep } from "lodash-es";
import {
getDurationObjectFromParams,
getQueryParamsForDuration,
@@ -260,6 +263,7 @@ export default defineComponent({
const searchBarRef = ref(null);
const parser = new Parser();
const fieldValues = ref({});
const { showErrorNotification } = useNotifications();
searchObj.organizationIdetifier =
store.state.selectedOrganization.identifier;
@@ -570,21 +574,16 @@ export default defineComponent({
searchObj.meta.resultGrid.rowsPerPage;
req.query.size = parseInt(searchObj.meta.resultGrid.rowsPerPage, 10);
var timestamps: any = getConsumableDateTime();
req.query.start_time = getISOTimestamp(timestamps.start_time);
req.query.end_time = getISOTimestamp(timestamps.end_time);
var timestamps: any =
searchObj.data.datetime.type == "relative"
? getConsumableRelativeTime(
searchObj.data.datetime.relativeTimePeriod
)
: cloneDeep(searchObj.data.datetime);
const chartSettings = getChartSettings(timestamps);
// if (chartSettings) {
// searchObj.meta.resultGrid.chartKeyFormat =
// chartSettings.chartKeyFormat;
// searchObj.meta.resultGrid.chartInterval = chartSettings.chartInterval;
req.query.start_time = timestamps.startTime;
req.query.end_time = timestamps.endTime;
// req.aggs.histogram = req.aggs.histogram.replaceAll(
// "[INTERVAL]",
// searchObj.meta.resultGrid.chartInterval
// );
// }
req.query["sql_mode"] = "full";
let parseQuery = query.split("|");
@@ -648,6 +647,7 @@ export default defineComponent({
updateUrlQueryParams();
return req;
} catch (e) {
console.log(e);
searchObj.loading = false;
showErrorNotification("Invalid SQL Syntax");
}
@@ -1223,7 +1223,16 @@ export default defineComponent({
function restoreUrlQueryParams() {
const queryParams = router.currentRoute.value.query;
const date = getDurationObjectFromParams(queryParams);
if (!queryParams.stream) {
return;
}
const date = {
startTime: queryParams.from,
endTime: queryParams.to,
relativeTimePeriod: queryParams.period || null,
type: queryParams.period ? "relative" : "absolute",
};
if (date) {
searchObj.data.datetime = date;
}
@@ -1233,7 +1242,7 @@ export default defineComponent({
}
function updateUrlQueryParams() {
const date = getQueryParamsForDuration(searchObj.data.datetime);
const date = searchObj.data.datetime;
const query = {};
if (date.period) {
@@ -1353,17 +1362,17 @@ export default defineComponent({
this.loadPageData();
}
},
changeStream() {
if (this.searchObj.data.stream.selectedStream.hasOwnProperty("value")) {
setTimeout(() => {
this.runQueryFn();
}, 500);
}
},
changeRelativeDate() {
if (this.searchObj.data.datetime.tab == "relative") {
this.runQueryFn();
}
changeStream: {
handler(stream, oldStream) {
if (this.searchObj.data.stream.selectedStream.hasOwnProperty("value")) {
if (oldStream.value) this.searchObj.data.query = "";
if (oldStream.value) this.setQuery(this.searchObj.meta.sqlMode);
setTimeout(() => {
this.runQueryFn();
}, 500);
}
},
immediate: false,
},
updateSelectedColumns() {
this.searchObj.meta.resultGrid.manualRemoveFields = true;
+24 -12
View File
@@ -34,9 +34,19 @@
></q-btn>
<div class="float-left">
<date-time
:default-date="searchObj.data.datetime"
auto-apply
:default-type="
searchObj.data.datetime.relativeTimePeriod
? 'relative'
: 'absolute'
"
:default-absolute-time="{
startTime: searchObj.data.datetime.startTime,
endTime: searchObj.data.datetime.endTime,
}"
:default-relative-time="searchObj.data.datetime.relativeTimePeriod"
data-test="logs-search-bar-date-time-dropdown"
@date-change="updateDateTime"
@on:date-change="updateDateTime"
/>
</div>
<div class="search-time q-pl-sm float-left">
@@ -111,7 +121,7 @@ export default defineComponent({
}
},
},
setup(props) {
setup(props, { emit }) {
const router = useRouter();
const { t } = useI18n();
const $q = useQuasar();
@@ -198,26 +208,28 @@ export default defineComponent({
};
const updateDateTime = (value: object) => {
searchObj.data.datetime = value;
searchObj.data.datetime = {
startTime: value.startTime,
endTime: value.endTime,
relativeTimePeriod: value.relativeTimePeriod
? value.relativeTimePeriod
: searchObj.data.datetime.relativeTimePeriod,
type: value.relativeTimePeriod ? "relative" : "absolute",
};
if (config.isCloud == "true" && value.userChangedValue) {
let dateTimeVal;
if (value.tab === "relative") {
dateTimeVal = value.relative;
} else {
dateTimeVal = value.absolute;
}
segment.track("Button Click", {
button: "Date Change",
tab: value.tab,
value: dateTimeVal,
value: value,
//user_org: this.store.state.selectedOrganization.identifier,
//user_id: this.store.state.userInfo.email,
stream_name: searchObj.data.stream.selectedStream.value,
page: "Search Logs",
});
}
if (value.valueType === "relative") emit("searchdata");
};
const udpateQuery = () => {
+3 -19
View File
@@ -168,8 +168,6 @@ import useTraces from "../../composables/useTraces";
import { getImageURL } from "../../utils/zincutils";
import TraceDetails from "./TraceDetails.vue";
import TraceChart from "./TraceChart.vue";
import { redraw } from "plotly.js";
import { cloneDeep } from "lodash";
export default defineComponent({
name: "SearchResult",
@@ -202,23 +200,9 @@ export default defineComponent({
onChartUpdate({ start, end }: { start: any; end: any }) {
if (!(start && end)) return;
this.searchObj.meta.showDetailTab = false;
this.searchObj.runQuery = true;
this.searchObj.data.datetime.tab = "absolute";
this.searchObj.data.datetime.absolute.date.from = start.split(" ")[0];
this.searchObj.data.datetime.absolute.date.to = end.split(" ")[0];
this.searchObj.data.datetime.absolute.startTime =
start.split(" ")[1].split(":")[0] +
":" +
start.split(" ")[1].split(":")[1] +
":" +
start.split(" ")[1].split(":")[2];
this.searchObj.data.datetime.absolute.endTime =
end.split(" ")[1].split(":")[0] +
":" +
end.split(" ")[1].split(":")[1] +
":" +
end.split(" ")[1].split(":")[2];
this.searchObj.data.datetime.type = "absolute";
this.searchObj.data.datetime.startTime = new Date(start).getTime() * 1000;
this.searchObj.data.datetime.endTime = new Date(end).getTime() * 1000;
this.searchObj.runQuery = true;
this.$emit("update:datetime");
},
-19
View File
@@ -15,11 +15,9 @@
import { ref } from "vue";
import organizationsService from "../services/organizations";
import { useLocalOrganization, getPath } from "./zincutils";
import { useQuasar } from "quasar";
const selectedOrg = ref("");
const orgOptions = ref([{ label: Number, value: String }]);
const $q = useQuasar();
export const getDefaultOrganization = async (
userInfo: any,
@@ -106,20 +104,3 @@ export const logsErrorMessage = (code: number) => {
return "";
}
};
export const showErrorNotification = (message: string) => {
$q.notify({
type: "negative",
message: message,
timeout: 5000,
actions: [
{
icon: "close",
color: "white",
handler: () => {
/* ... */
},
},
],
});
};
+96 -35
View File
@@ -1,5 +1,7 @@
// Copyright 2022 Zinc Labs Inc. and Contributors
import { date } from "quasar";
// Licensed 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
@@ -52,7 +54,7 @@ export const generateDurationLabel = (seconds: number) => {
{ label: "y", value: 31536000 },
];
let duration = seconds;
const duration = seconds;
let label = "";
for (let i = units.length - 1; i >= 0; i--) {
@@ -79,50 +81,50 @@ export const generateDurationLabel = (seconds: number) => {
return label;
};
export const getQueryParamsForDuration = (obj : any) => {
let params : any = {};
let tab = obj.tab;
let period = obj.relative.period;
let from = obj.absolute.date.from + " " + obj.absolute.startTime;
let to = obj.absolute.date.to + " " + obj.absolute.endTime;
export const getQueryParamsForDuration = (obj: any) => {
const params: any = {};
const tab = obj.tab;
const period = obj.relative.period;
const from = obj.absolute.date.from + " " + obj.absolute.startTime;
const to = obj.absolute.date.to + " " + obj.absolute.endTime;
if (tab === "relative") {
const labelsToUnitMapping : any = {
const labelsToUnitMapping: any = {
Minutes: "m",
Hours: "h",
Days: "d",
Weeks: "w",
Months: "M"
}
Months: "M",
};
let periodValue = obj.relative.value;
let periodLabel = period.label;
let periodUnit = labelsToUnitMapping[periodLabel];
const periodValue = obj.relative.value;
const periodLabel = period.label;
const periodUnit = labelsToUnitMapping[periodLabel];
if (periodLabel === "Minutes" && periodUnit === "m") {
params['period'] = `${periodValue}${periodUnit}`;
params["period"] = `${periodValue}${periodUnit}`;
} else if (periodLabel === "Hours" && periodUnit === "h") {
params['period'] = `${periodValue}${periodUnit}`;
params["period"] = `${periodValue}${periodUnit}`;
} else if (periodLabel === "Days" && periodUnit === "d") {
params['period'] = `${periodValue}${periodUnit}`;
params["period"] = `${periodValue}${periodUnit}`;
} else if (periodLabel === "Weeks" && periodUnit === "w") {
params['period'] = `${periodValue}${periodUnit}`;
params["period"] = `${periodValue}${periodUnit}`;
} else if (periodLabel === "Months" && periodUnit === "M") {
params['period'] = `${periodValue}${periodUnit}`;
params["period"] = `${periodValue}${periodUnit}`;
}
} else if (tab === "absolute") {
let fromTime = new Date(from).getTime();
let toTime = new Date(to).getTime();
const fromTime = new Date(from).getTime();
const toTime = new Date(to).getTime();
params['from'] = `${fromTime}`;
params['to'] = `${toTime}`;
params["from"] = `${fromTime}`;
params["to"] = `${toTime}`;
}
return params;
}
};
export const getDurationObjectFromParams = (params: any) => {
let obj = {
const obj = {
tab: "relative",
relative: {
period: { label: "Minutes", value: "Minutes" },
@@ -138,13 +140,13 @@ export const getDurationObjectFromParams = (params: any) => {
},
};
let period = params?.period?.match(/(\d+)([mhdwM])/);
let from = params?.from?.match(/(\d+)/);
let to = params?.to?.match(/(\d+)/);
const period = params?.period?.match(/(\d+)([mhdwM])/);
const from = params?.from?.match(/(\d+)/);
const to = params?.to?.match(/(\d+)/);
if (period) {
let periodValue = period[1];
let periodUnit = period[2];
const periodValue = period[1];
const periodUnit = period[2];
if (periodUnit === "m") {
obj.relative.period = { label: "Minutes", value: "Minutes" };
@@ -160,19 +162,78 @@ export const getDurationObjectFromParams = (params: any) => {
obj.relative.value = parseInt(periodValue);
} else if (from && to) {
let fromTime = parseInt(from[1]);
let toTime = parseInt(to[1]);
const fromTime = parseInt(from[1]);
const toTime = parseInt(to[1]);
obj.tab = "absolute";
obj.absolute.date.from = new Date(fromTime).toLocaleDateString("en-ZA");
obj.absolute.date.to = new Date(toTime).toLocaleDateString("en-ZA");
const startTimeDateObj = new Date(fromTime);
obj.absolute.startTime = `${startTimeDateObj.getHours().toString().padStart(2, '0')}:${startTimeDateObj.getMinutes().toString().padStart(2, '0')}`
obj.absolute.startTime = `${startTimeDateObj
.getHours()
.toString()
.padStart(2, "0")}:${startTimeDateObj
.getMinutes()
.toString()
.padStart(2, "0")}`;
const toTimeDateObj = new Date(toTime);
obj.absolute.endTime = `${toTimeDateObj.getHours().toString().padStart(2, '0')}:${toTimeDateObj.getMinutes().toString().padStart(2, '0')}`
obj.absolute.endTime = `${toTimeDateObj
.getHours()
.toString()
.padStart(2, "0")}:${toTimeDateObj
.getMinutes()
.toString()
.padStart(2, "0")}`;
}
return obj;
}
};
export const getConsumableRelativeTime = (period: string) => {
const periodString = period?.match(/(\d+)([mhdwM])/);
if (periodString) {
let periodValue: number = parseInt(periodString[1]);
let periodUnit: string = periodString[2];
const periodUnits = ["m", "h", "d", "w", "M"];
// quasar does not support arithmetic on weeks. convert to days.
if (periodUnit == "w") {
periodUnit = "days";
periodValue = periodValue * 7;
}
const subtractObject =
'{"' +
getRelativePeriod(periodUnit).toLowerCase() +
'":' +
periodValue +
"}";
const endTimeStamp = new Date();
const startTimeStamp = date.subtractFromDate(
endTimeStamp,
JSON.parse(subtractObject)
);
return {
startTime: new Date(startTimeStamp).getTime() * 1000,
endTime: new Date(endTimeStamp).getTime() * 1000,
};
}
};
export const getRelativePeriod = (period: string) => {
const mapping: { [key: string]: string } = {
m: "Minutes",
h: "Hours",
d: "Days",
w: "Weeks",
M: "Months",
};
return mapping[period];
};