Logs Volume improvements
What does this MR do and why?
Last set of changes for Add Volume chart to Logs List UI (gitlab-org/opstrace/opstrace#2647 - closed)
- Hide chart if it has no data
- Add zoomer
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Screenshots or screen recordings
How to set up and validate locally
- Prerequisites: be logged in and running GDK with Ultimate license
- Enable
:observability_logs
feature flag
Apply patch to load mocks ( copy the patch content below and run in your terminal: pbpaste | git apply
)
pbpaste | git apply
)diff --git a/app/assets/javascripts/observability/client.js b/app/assets/javascripts/observability/client.js
index 81ca75579c06..68eae7a0f4f4 100644
--- a/app/assets/javascripts/observability/client.js
+++ b/app/assets/javascripts/observability/client.js
@@ -1,3 +1,4 @@
+/* eslint-disable @gitlab/require-i18n-strings */
import { isValidDate } from '~/lib/utils/datetime_utility';
import * as Sentry from '~/sentry/sentry_browser_wrapper';
import axios from '~/lib/utils/axios_utils';
@@ -15,15 +16,17 @@ function reportErrorAndThrow(e) {
* Provisioning API
*
* ***** */
+function mockReturnDataWithDelay(data) {
+ return new Promise((resolve) => {
+ setTimeout(() => resolve(data), 1000);
+ });
+}
// Provisioning API spec: https://gitlab.com/gitlab-org/opstrace/opstrace/-/blob/main/provisioning-api/pkg/provisioningapi/routes.go#L59
async function enableObservability(provisioningUrl) {
try {
- // Note: axios.put(url, undefined, {withCredentials: true}) does not send cookies properly, so need to use the API below for the correct behaviour
- return await axios(provisioningUrl, {
- method: 'put',
- withCredentials: true,
- });
+ console.log('[DEBUG] Enabling Observability');
+ return mockReturnDataWithDelay();
} catch (e) {
return reportErrorAndThrow(e);
}
@@ -32,19 +35,14 @@ async function enableObservability(provisioningUrl) {
// Provisioning API spec: https://gitlab.com/gitlab-org/opstrace/opstrace/-/blob/main/provisioning-api/pkg/provisioningapi/routes.go#L37
async function isObservabilityEnabled(provisioningUrl) {
try {
- const { data } = await axios.get(provisioningUrl, { withCredentials: true });
- if (data && data.status) {
- // we currently ignore the 'status' payload and just check if the request was successful
- // We might improve this as part of https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2315
- return true;
- }
+ console.log('[DEBUG] Checking Observability provisioning');
+ return mockReturnDataWithDelay(true);
} catch (e) {
if (e.response.status === 404) {
return false;
}
return reportErrorAndThrow(e);
}
- return reportErrorAndThrow(new Error('Failed to check provisioning')); // eslint-disable-line @gitlab/require-i18n-strings
}
/** ****
@@ -597,17 +595,64 @@ export async function fetchLogs(logsSearchUrl, { pageToken, pageSize, filters =
if (pageSize) {
params.append('page_size', pageSize);
}
- const { data } = await axios.get(logsSearchUrl, {
- withCredentials: true,
- params,
+
+ console.log(`[DEBUG] Fetching logs with params: ${params.toString()}`);
+
+ const data = [
+ {
+ timestamp: '2024-02-19T16:10:15.4433398Z',
+ trace_id: ']?4ft^T\u0011\ufffd\u0013\ufffd6\ufffdbj\ufffd',
+ span_id: "'\u0013\ufffd\ufffd_\ufffd\ufffd\ufffd",
+ trace_flags: 1,
+ severity_text: 'Information',
+ severity_number: 9,
+ service_name: 'cartservice',
+ body:
+ 'AddItemAsync called with userId={userId}, productId={productId}, quantity={quantity} AddItemAsync called with userId={userId}, productId={productId}, quantity={quantity} AddItemAsync called with userId={userId}, productId={productId}, quantity={quantity} AddItemAsync called with userId={userId}, productId={productId}, quantity={quantity} AddItemAsync called with userId={userId}, productId={productId}, quantity={quantity} AddItemAsync called with userId={userId}, productId={productId}, quantity={quantity} ',
+ resource_attributes: {
+ 'container.id': 'ae165f3300bf7da7cc83b360e955683f31f959268241895747f23ad165014005',
+ 'k8s.deployment.name': 'otel-demo-cartservice',
+ 'k8s.namespace.name': 'otel-demo-app',
+ 'k8s.node.name': 'opstrace-worker2',
+ 'k8s.pod.ip': '192.168.62.215',
+ 'k8s.pod.name': 'otel-demo-cartservice-6dcc867f5f-pcm68',
+ 'k8s.pod.start_time': '2024-02-12T13:32:12Z',
+ 'k8s.pod.uid': 'bb0991d5-d2f9-486d-aed6-fb431da28bcb',
+ 'service.name': 'cartservice',
+ 'service.namespace': 'opentelemetry-demo',
+ 'telemetry.sdk.language': 'dotnet',
+ 'telemetry.sdk.name': 'opentelemetry',
+ 'telemetry.sdk.version': '1.6.0',
+ },
+ log_attributes: {
+ productId: 'LS4PSXUNUM',
+ quantity: '5',
+ userId: '5e22f0f6-cf41-11ee-a97f-768346730ebf',
+ },
+ },
+ ];
+
+ return mockReturnDataWithDelay({
+ logs: [
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ...data,
+ ].map((l) => ({
+ ...l,
+ fingerprint: crypto.randomUUID(),
+ })),
});
- if (!Array.isArray(data.results)) {
- throw new Error('logs are missing/invalid in the response'); // eslint-disable-line @gitlab/require-i18n-strings
- }
- return {
- logs: data.results,
- nextPageToken: data.next_page_token,
- };
} catch (e) {
return reportErrorAndThrow(e);
}
@@ -633,347 +678,349 @@ export async function fetchLogsSearchMetadata(_logsSearchMetadataUrl, { filters
// });
// return data;
- return {
+ return mockReturnDataWithDelay({
start_ts: 1713513680617331200,
end_ts: 1714723280617331200,
summary: {
service_names: ['adservice', 'cartservice', 'quoteservice', 'recommendationservice'],
trace_flags: [0, 1],
- severity_names: ['info', 'warn'],
- severity_numbers: [9, 13],
+ severity_names: ['info', 'warn', 'error'],
+ severity_numbers: [9, 13, 17],
},
severity_numbers_counts: [
{
time: 1713519360000000000,
counts: {
- 13: 0,
- 9: 0,
+ 13: 1001,
+ 17: 2201,
},
},
{
time: 1713545280000000000,
counts: {
- 13: 0,
- 9: 0,
+ 13: 1231,
+ 17: 501,
},
},
{
time: 1713571200000000000,
counts: {
- 13: 0,
- 9: 0,
+ 13: 5555,
+ 17: 2342,
},
},
{
time: 1713597120000000000,
counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713623040000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713648960000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713674880000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713700800000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713726720000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713752640000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713778560000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713804480000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713830400000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713856320000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713882240000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713908160000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713934080000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713960000000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1713985920000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714011840000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714037760000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714063680000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714089600000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714115520000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714141440000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714167360000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714193280000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714219200000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714245120000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714271040000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714296960000000000,
- counts: {
- 13: 0,
- 9: 0,
- },
- },
- {
- time: 1714322880000000000,
- counts: {
- 13: 1,
- 9: 26202,
- },
- },
- {
- time: 1714348800000000000,
- counts: {
- 13: 0,
- 9: 53103,
- },
- },
- {
- time: 1714374720000000000,
- counts: {
- 13: 0,
- 9: 52854,
- },
- },
- {
- time: 1714400640000000000,
- counts: {
- 13: 0,
- 9: 49598,
- },
- },
- {
- time: 1714426560000000000,
- counts: {
- 13: 0,
- 9: 45266,
- },
- },
- {
- time: 1714452480000000000,
- counts: {
- 13: 0,
- 9: 44951,
- },
- },
- {
- time: 1714478400000000000,
- counts: {
- 13: 0,
- 9: 45096,
- },
- },
- {
- time: 1714504320000000000,
- counts: {
- 13: 0,
- 9: 45301,
- },
- },
- {
- time: 1714530240000000000,
- counts: {
- 13: 0,
- 9: 44894,
- },
- },
- {
- time: 1714556160000000000,
- counts: {
- 13: 0,
- 9: 45444,
- },
- },
- {
- time: 1714582080000000000,
- counts: {
- 13: 0,
- 9: 45067,
- },
- },
- {
- time: 1714608000000000000,
- counts: {
- 13: 0,
- 9: 45119,
- },
- },
- {
- time: 1714633920000000000,
- counts: {
- 13: 0,
- 9: 45817,
- },
- },
- {
- time: 1714659840000000000,
- counts: {
- 13: 0,
- 9: 44574,
- },
- },
- {
- time: 1714685760000000000,
- counts: {
- 13: 0,
- 9: 44652,
- },
- },
- {
- time: 1714711680000000000,
- counts: {
- 13: 0,
- 9: 20470,
+ 13: 1234,
+ 17: 5555,
},
},
+ // {
+ // time: 1713623040000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713648960000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713674880000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713700800000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713726720000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713752640000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713778560000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713804480000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713830400000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713856320000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713882240000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713908160000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713934080000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713960000000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1713985920000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714011840000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714037760000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714063680000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714089600000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714115520000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714141440000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714167360000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714193280000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714219200000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714245120000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714271040000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714296960000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 0,
+ // },
+ // },
+ // {
+ // time: 1714322880000000000,
+ // counts: {
+ // 13: 1,
+ // 9: 26202,
+ // },
+ // },
+ // {
+ // time: 1714348800000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 53103,
+ // },
+ // },
+ // {
+ // time: 1714374720000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 52854,
+ // },
+ // },
+ // {
+ // time: 1714400640000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 49598,
+ // },
+ // },
+ // {
+ // time: 1714426560000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 45266,
+ // },
+ // },
+ // {
+ // time: 1714452480000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 44951,
+ // },
+ // },
+ // {
+ // time: 1714478400000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 45096,
+ // },
+ // },
+ // {
+ // time: 1714504320000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 45301,
+ // },
+ // },
+ // {
+ // time: 1714530240000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 44894,
+ // },
+ // },
+ // {
+ // time: 1714556160000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 45444,
+ // },
+ // },
+ // {
+ // time: 1714582080000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 45067,
+ // },
+ // },
+ // {
+ // time: 1714608000000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 45119,
+ // },
+ // },
+ // {
+ // time: 1714633920000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 45817,
+ // },
+ // },
+ // {
+ // time: 1714659840000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 44574,
+ // },
+ // },
+ // {
+ // time: 1714685760000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 44652,
+ // },
+ // },
+ // {
+ // time: 1714711680000000000,
+ // counts: {
+ // 13: 0,
+ // 9: 20470,
+ // },
+ // },
],
- };
+ // uncomment next line to test chart with no data
+ // severity_numbers_counts: [{ time: 1715341043520000000, counts: {} }],
+ });
} catch (e) {
return reportErrorAndThrow(e);
}
diff --git a/app/assets/javascripts/observability/components/observability_container.vue b/app/assets/javascripts/observability/components/observability_container.vue
index d0902505ca73..f6cbf7ee771f 100644
--- a/app/assets/javascripts/observability/components/observability_container.vue
+++ b/app/assets/javascripts/observability/components/observability_container.vue
@@ -27,12 +27,12 @@ export default {
// TODO: Improve local GDK dev experience with tracing https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2308
// Uncomment the lines below to to test this locally
- // setTimeout(() => {
- // this.messageHandler({
- // data: { type: 'AUTH_COMPLETION', status: 'success' },
- // origin: new URL(this.apiConfig.oauthUrl).origin,
- // });
- // }, 2000);
+ setTimeout(() => {
+ this.messageHandler({
+ data: { type: 'AUTH_COMPLETION', status: 'success' },
+ origin: new URL(this.apiConfig.oauthUrl).origin,
+ });
+ }, 2000);
},
destroyed() {
window.removeEventListener('message', this.messageHandler);
diff --git a/ee/app/assets/javascripts/logs/list/logs_list.vue b/ee/app/assets/javascripts/logs/list/logs_list.vue
index 900d919eb684..4f0deb244306 100644
--- a/ee/app/assets/javascripts/logs/list/logs_list.vue
+++ b/ee/app/assets/javascripts/logs/list/logs_list.vue
@@ -1,5 +1,6 @@
<script>
import { GlLoadingIcon, GlInfiniteScroll, GlSprintf } from '@gitlab/ui';
+import { throttle } from 'lodash';
import { s__ } from '~/locale';
import { createAlert } from '~/alert';
import { contentTop } from '~/lib/utils/common_utils';
@@ -8,8 +9,8 @@ import LogsTable from './logs_table.vue';
import LogsDrawer from './logs_drawer.vue';
import LogsFilteredSearch from './filter_bar/logs_filtered_search.vue';
import { queryToFilterObj, filterObjToQuery, selectedLogQueryObject } from './filter_bar/filters';
+import LogsVolume from './logs_volume.vue';
-const LIST_V_PADDING = 100;
const PAGE_SIZE = 100;
export default {
@@ -21,6 +22,7 @@ export default {
GlSprintf,
LogsFilteredSearch,
UrlSync,
+ LogsVolume,
},
i18n: {
infiniteScrollLegend: s__(`ObservabilityLogs|Showing %{count} logs`),
@@ -41,23 +43,38 @@ export default {
isDrawerOpen: false,
selectedLog: null,
nextPageToken: null,
+ logsVolumeChartHeight: 0,
+ listHeight: 0,
};
},
computed: {
- listHeight() {
- return window.innerHeight - contentTop() - LIST_V_PADDING;
- },
query() {
if (this.selectedLog) {
return selectedLogQueryObject(this.selectedLog);
}
return filterObjToQuery(this.filters);
},
+ logsSearchMetadata() {
+ return this.metadata?.summary;
+ },
+ logsVolumeCount() {
+ return this.metadata?.severity_numbers_counts;
+ },
},
created() {
this.fetchLogs();
this.fetchMetadata();
},
+ mounted() {
+ this.resize();
+ this.resizeThrottled = throttle(() => {
+ this.resize();
+ }, 400);
+ window.addEventListener('resize', this.resizeThrottled);
+ },
+ beforeDestroy() {
+ window.removeEventListener('resize', this.resizeThrottled, false);
+ },
methods: {
async fetchLogs() {
this.loadingLogs = true;
@@ -110,6 +127,7 @@ export default {
onFilter({ dateRange, attributes }) {
this.nextPageToken = null;
this.logs = [];
+ this.metadata = {};
this.filters = {
dateRange,
attributes,
@@ -118,45 +136,59 @@ export default {
this.fetchLogs();
this.fetchMetadata();
},
+ resize() {
+ const searchBarHeight = this.$refs.filteredSearch.$el.clientHeight;
+ const containerHeight = window.innerHeight - contentTop() - searchBarHeight;
+ this.logsVolumeChartHeight = Math.max(100, (containerHeight * 20) / 100);
+ const LIST_V_PADDING = 80;
+ this.listHeight = containerHeight - this.logsVolumeChartHeight - LIST_V_PADDING;
+ },
},
};
</script>
<template>
- <div>
+ <div class="gl-px-4">
<url-sync :query="query" />
+ <logs-filtered-search
+ ref="filteredSearch"
+ :date-range-filter="filters.dateRange"
+ :attributes-filters="filters.attributes"
+ :search-metadata="logsSearchMetadata"
+ @filter="onFilter"
+ />
+
+ <logs-volume
+ :logs-count="logsVolumeCount"
+ :loading="loadingMetadata"
+ :height="logsVolumeChartHeight"
+ />
+
<div v-if="loadingLogs && logs.length === 0" class="gl-py-5">
<gl-loading-icon size="lg" />
</div>
- <div v-else class="gl-px-4">
- <logs-filtered-search
- :date-range-filter="filters.dateRange"
- :attributes-filters="filters.attributes"
- @filter="onFilter"
- />
+ <gl-infinite-scroll
+ v-else
+ :max-list-height="listHeight"
+ :fetched-items="logs.length"
+ @bottomReached="bottomReached"
+ >
+ <template #items>
+ <logs-table :logs="logs" @reload="fetchLogs" @log-selected="onToggleDrawer" />
+ </template>
- <gl-infinite-scroll
- :max-list-height="listHeight"
- :fetched-items="logs.length"
- @bottomReached="bottomReached"
- >
- <template #items>
- <logs-table :logs="logs" @reload="fetchLogs" @log-selected="onToggleDrawer" />
- </template>
+ <template #default>
+ <gl-loading-icon v-if="loadingLogs" size="md" />
+ <span v-else data-testid="logs-infinite-scrolling-legend">
+ <gl-sprintf v-if="logs.length" :message="$options.i18n.infiniteScrollLegend">
+ <template #count>{{ logs.length }}</template>
+ </gl-sprintf>
+ </span>
+ </template>
+ </gl-infinite-scroll>
- <template #default>
- <gl-loading-icon v-if="loadingLogs" size="md" />
- <span v-else data-testid="logs-infinite-scrolling-legend">
- <gl-sprintf v-if="logs.length" :message="$options.i18n.infiniteScrollLegend">
- <template #count>{{ logs.length }}</template>
- </gl-sprintf>
- </span>
- </template>
- </gl-infinite-scroll>
-
- <logs-drawer :log="selectedLog" :open="Boolean(selectedLog)" @close="closeDrawer" />
- </div>
+ <logs-drawer :log="selectedLog" :open="Boolean(selectedLog)" @close="closeDrawer" />
</div>
</template>
- Go to https://local.gitlab.com:3443/flightjs/Flight/-/logs
- To test chart with no data go to
app/assets/javascripts/observability/client.js
and uncomment line1022
Edited by Daniele Rossetti