diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-19 18:09:36 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-19 18:09:36 +0300 |
commit | 8bb837c4d180720d4d923ef2e7bd2c9a46ca97a0 (patch) | |
tree | 7dcb166661ba29fb6cd5935f0db34eee6c935388 /app/assets/javascripts/error_tracking | |
parent | eef2437c0a359ec3437d31d1b1ea959e54c71458 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/error_tracking')
4 files changed, 166 insertions, 8 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue index 182090e64f9..0151dbb0bf7 100644 --- a/app/assets/javascripts/error_tracking/components/error_details.vue +++ b/app/assets/javascripts/error_tracking/components/error_details.vue @@ -24,6 +24,7 @@ import { import { severityLevel, severityLevelVariant, errorStatus } from '../constants'; import Stacktrace from './stacktrace.vue'; import ErrorDetailsInfo from './error_details_info.vue'; +import TimelineChart from './timeline_chart.vue'; const SENTRY_TIMEOUT = 10000; @@ -42,6 +43,7 @@ export default { GlDropdownDivider, TimeAgoTooltip, ErrorDetailsInfo, + TimelineChart, }, props: { issueUpdatePath: { @@ -375,6 +377,11 @@ export default { <error-details-info :error="error" /> + <div v-if="error.frequency" class="gl-mt-8"> + <h3>{{ __('Last 24 hours') }}</h3> + <timeline-chart :timeline-data="error.frequency" :height="200" /> + </div> + <div v-if="loadingStacktrace" class="gl-py-5"> <gl-loading-icon size="lg" /> </div> diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue index e3784cc8b92..0c9a98f3b33 100644 --- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue +++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue @@ -30,10 +30,12 @@ import { } from '../events_tracking'; import { I18N_ERROR_TRACKING_LIST } from '../constants'; import ErrorTrackingActions from './error_tracking_actions.vue'; +import TimelineChart from './timeline_chart.vue'; const isValidErrorId = (errorId) => { return /^[0-9]+$/.test(errorId); }; +export const tableDataClass = 'gl-display-flex gl-md-display-table-cell gl-align-items-center'; export default { FIRST_PAGE: 1, PREV_PAGE: 1, @@ -43,30 +45,37 @@ export default { { key: 'error', label: __('Error'), - thClass: 'w-60p', + thClass: 'gl-w-40p', + tdClass: `${tableDataClass}`, + }, + { + key: 'timeline', + label: __('Timeline'), + thClass: 'gl-text-center gl-w-20p', + tdClass: `${tableDataClass} gl-text-center`, }, { key: 'events', label: __('Events'), - thClass: 'gl-text-right', - tdClass: 'gl-text-right', + thClass: 'gl-text-center gl-w-10p', + tdClass: `${tableDataClass} gl-text-center`, }, { key: 'users', label: __('Users'), - thClass: 'gl-text-right', - tdClass: 'gl-text-right', + thClass: 'gl-text-center gl-w-10p', + tdClass: `${tableDataClass} gl-text-center`, }, { key: 'lastSeen', label: __('Last seen'), - thClass: 'gl-w-15p', - tdClass: 'gl-text-left', + thClass: 'gl-text-center gl-w-10p', + tdClass: `${tableDataClass} gl-text-center`, }, { key: 'status', label: '', - tdClass: 'gl-text-center', + tdClass: `${tableDataClass}`, }, ], statusFilters: { @@ -95,6 +104,7 @@ export default { GlPagination, TimeAgo, ErrorTrackingActions, + TimelineChart, }, directives: { GlTooltip: GlTooltipDirective, @@ -438,6 +448,14 @@ export default { </div> </template> + <template #cell(timeline)="errors"> + <timeline-chart + v-if="errors.item.frequency" + :timeline-data="errors.item.frequency" + :height="70" + /> + </template> + <template #cell(events)="errors"> {{ errors.item.count }} </template> diff --git a/app/assets/javascripts/error_tracking/components/timeline_chart.vue b/app/assets/javascripts/error_tracking/components/timeline_chart.vue new file mode 100644 index 00000000000..51e0c900e4b --- /dev/null +++ b/app/assets/javascripts/error_tracking/components/timeline_chart.vue @@ -0,0 +1,129 @@ +<script> +import { GlChart } from '@gitlab/ui/dist/charts'; +import { dataVizBlue500 } from '@gitlab/ui/scss_to_js/scss_variables'; +import { hexToRgba } from '@gitlab/ui/dist/utils/utils'; +import { isNumber } from 'lodash'; +import { formatDate } from '~/lib/utils/datetime/date_format_utility'; +import { logError } from '~/lib/logger'; + +function parseTimelineData(timelineData) { + const xData = []; + const yData = []; + const invalidDataPoints = []; + timelineData.forEach((f) => { + let rawDate; + let count; + + if (Array.isArray(f)) { + [rawDate, count] = f; + } else if (f.count !== undefined && f.time !== undefined) { + rawDate = f.time; + count = f.count; + } + if (rawDate !== undefined && count !== undefined) { + // dates/timestamps are in seconds + const date = isNumber(rawDate) ? rawDate * 1000 : rawDate; + xData.push(formatDate(date)); + yData.push(count); + } else { + invalidDataPoints.push(f); + } + }); + if (invalidDataPoints.length > 0) { + // only log up to 5 invalid data points to reduce log size + logError(`Found invalid data points ${invalidDataPoints.slice(0, 5)}`); + } + return { xData, yData }; +} + +export default { + components: { + GlChart, + }, + props: { + timelineData: { + /** + * Array items can be: + * touples: [a_date: string | number, a_count: number] + * objects: {time: a_date, count: a_count}: {time: string | number, count: number} + * + * Dates can either be string or number/timestamp. + * When dates are timestamps, they are expected in seconds. + * + */ + type: Array, + required: true, + validator(value) { + for (const item of value) { + if (Array.isArray(item)) { + if (item.length !== 2 || !isNumber(item[1])) { + return false; + } + } else if (typeof item === 'object') { + if (!('time' in item) || !('count' in item)) { + return false; + } + } else { + return false; + } + } + return true; + }, + }, + height: { + type: Number, + required: true, + }, + }, + computed: { + chartOptions() { + if (!this.timelineData) { + return {}; + } + const { xData, yData } = parseTimelineData(this.timelineData); + + return { + xAxis: { + type: 'category', + data: xData, + show: true, + axisTick: { + show: false, + }, + axisLabel: { + show: false, + }, + axisLine: { + show: true, + lineStyle: { + width: 1, + color: '#ececec', + }, + }, + }, + yAxis: { + type: 'value', + show: false, + }, + series: [ + { + data: yData, + type: 'bar', + itemStyle: { color: hexToRgba(dataVizBlue500, 0.5) }, + }, + ], + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + }; + }, + }, +}; +</script> + +<template> + <gl-chart v-if="timelineData" :options="chartOptions" :height="height" /> +</template> diff --git a/app/assets/javascripts/error_tracking/queries/details.query.graphql b/app/assets/javascripts/error_tracking/queries/details.query.graphql index dd21b0f9c92..5745491c32d 100644 --- a/app/assets/javascripts/error_tracking/queries/details.query.graphql +++ b/app/assets/javascripts/error_tracking/queries/details.query.graphql @@ -20,6 +20,10 @@ query errorDetails($fullPath: ID!, $errorId: GitlabErrorTrackingDetailedErrorID! externalUrl externalBaseUrl firstReleaseVersion + frequency { + count + time + } lastReleaseVersion gitlabCommit gitlabCommitPath |