Metrics - Wire up filtered search to metrics details
What does this MR do and why?
This change is focused on wiring up the filtered search bar to the details component and observability client API
- Add
options
param to observability client’sfetchMetric
API to support dimensions, groupBy and date range filters. These are converted to proper query params, according to spec - Add
filters
to MetricsDetails component’s state and pass it down to the filtered bar component. This allows to define defaults at the parent level, and eventually store filters in query params - On filters change, reload metrics data with options
- Fixed a warning about duplicate key in a metric chart component
Notes
- This is part of gitlab-org/opstrace/opstrace#2488 (comment 1737633941)+
- Closes Set filters params to /search API call (gitlab-org/opstrace/opstrace#2623 - closed)
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
No visual changes
How to set up and validate locally
- Prerequisites: be logged in and running GDK with Ultimate license
- Enable
:observability_metrics
feature flag
Apply patch to load mocks ( copy the patch content below and run in your terminal: pbpaste | git apply --allow-empty
)
pbpaste | git apply --allow-empty
)diff --git a/app/assets/javascripts/observability/client.js b/app/assets/javascripts/observability/client.js
index 30880e339d08..3b3c87e7e054 100644
--- a/app/assets/javascripts/observability/client.js
+++ b/app/assets/javascripts/observability/client.js
@@ -1,22 +1,52 @@
+/* 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';
import { logError } from '~/lib/logger';
import { DEFAULT_SORTING_OPTION, SORTING_OPTIONS } from './constants';
+const MOCK_METRICS = {
+ metrics: [
+ {
+ name: 'app.ads.ad_requests',
+ description: 'Counts ad requests by request and response type',
+ type: 'Sum',
+ },
+ {
+ name: 'http.client.duration',
+ description: 'measures the duration of the outbound HTTP request',
+ type: 'Histogram',
+ },
+ {
+ name: 'http.server.duration',
+ description: 'Measures the duration of inbound HTTP requests.',
+ type: 'ExponentialHistogram',
+ },
+ {
+ name: 'kafka.consumer.assigned_partitions',
+ description: 'The number of partitions currently assigned to this consumer',
+ type: 'Gauge',
+ },
+ ],
+};
+
function reportErrorAndThrow(e) {
logError(e);
Sentry.captureException(e);
throw e;
}
+
+function mockReturnDataWithDelay(data) {
+ return new Promise((resolve) => {
+ setTimeout(() => resolve(data), 500);
+ });
+}
+
// 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);
}
@@ -25,11 +55,12 @@ 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 });
+ console.log('[DEBUG] Checking Observability Enabled');
+ const data = { status: 'ready' };
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;
+ return mockReturnDataWithDelay(true);
}
} catch (e) {
if (e.response.status === 404) {
@@ -311,14 +342,12 @@ async function fetchMetrics(metricsUrl, { filters = {}, limit } = {}) {
}
}
}
- const { data } = await axios.get(metricsUrl, {
- withCredentials: true,
- params,
- });
+ console.log(`[DEBUG] Fetching metrics with params: ${params.toString()}`);
+ const data = MOCK_METRICS;
if (!Array.isArray(data.metrics)) {
throw new Error('metrics are missing/invalid in the response'); // eslint-disable-line @gitlab/require-i18n-strings
}
- return data;
+ return mockReturnDataWithDelay(data);
} catch (e) {
return reportErrorAndThrow(e);
}
@@ -388,15 +417,121 @@ async function fetchMetric(searchUrl, name, type, { filters = {} } = {}) {
addGroupByFilterToQueryParams(filters.groupBy, params);
}
- const { data } = await axios.get(searchUrl, {
- params,
- withCredentials: true,
- });
-
+ console.log(`[DEBUG] Fetching metric with params: ${params.toString()}`);
+
+ const data = {
+ start_ts: 1705243529862125800,
+ end_ts: 1705329929862125800,
+ results: [
+ {
+ name: 'app.ads.ad_requests',
+ description: 'Counts ad requests by request and response type',
+ unit: '',
+ type: 'Sum',
+ attributes: {
+ 'app.ads.ad_request_type': 'NOT_TARGETED',
+ 'app.ads.ad_response_type': 'RANDOM',
+ },
+ values: [
+ [1705247947518101200, 2252],
+ [1705253554585113900, 2252],
+ [1705258930136038700, 2252],
+ [1705266254790955000, 2252],
+ [1705274618400971300, 2252],
+ [1705274618400971300, 2252],
+ [1705281863109378800, 2252],
+ [1705288569114644700, 2252],
+ [1705288790896014800, 2252],
+ [1705289034855967500, 2252],
+ [1705289034855967500, 2252],
+ [1705289512918064400, 2252],
+ [1705290250183041800, 2252],
+ [1705290310185114400, 2252],
+ [1705290736882757600, 2252],
+ [1705290796883791400, 2252],
+ [1705290856883483600, 2252],
+ [1705290987064807700, 2252],
+ [1705291047065788400, 2252],
+ [1705291107064986400, 2252],
+ [1705291167065962000, 2252],
+ [1705291227066536200, 2252],
+ ],
+ },
+ {
+ name: 'app.ads.ad_requests',
+ description: 'Counts ad requests by request and response type',
+ unit: '',
+ type: 'Sum',
+ attributes: {
+ 'app.ads.ad_request_type': 'TARGETED',
+ 'app.ads.ad_response_type': 'RANDOM',
+ },
+ values: [
+ [1705247947518101200, 2278],
+ [1705253554585113900, 2278],
+ [1705258930136038700, 2278],
+ [1705266254790955000, 2278],
+ [1705274618400971300, 2278],
+ [1705274618400971300, 2278],
+ [1705281863109378800, 2278],
+ [1705288569114644700, 2278],
+ [1705288790896014800, 2278],
+ [1705289034855967500, 2278],
+ [1705289034855967500, 2278],
+ [1705289512918064400, 2278],
+ [1705290250183041800, 2278],
+ [1705290310185114400, 2278],
+ [1705290736882757600, 2278],
+ [1705290796883791400, 2278],
+ [1705290856883483600, 2278],
+ [1705290987064807700, 2278],
+ [1705291047065788400, 2278],
+ [1705291107064986400, 2278],
+ [1705291167065962000, 2278],
+ [1705291227066536200, 2278],
+ [1705291633437533400, 2278],
+ [1705291693438488300, 2278],
+ [1705291753436971000, 2278],
+ [1705291813440958500, 2278],
+ ],
+ },
+ {
+ name: 'app.ads.ad_requests',
+ description: 'Counts ad requests by request and response type',
+ unit: '',
+ type: 'Sum',
+ attributes: {
+ 'app.ads.ad_request_type': 'TARGETED',
+ 'app.ads.ad_response_type': 'TARGETED',
+ },
+ values: [
+ [1705247947518101200, 11436],
+ [1705253554585113900, 11436],
+ [1705258930136038700, 11436],
+ [1705266254790955000, 11436],
+ [1705274618400971300, 11436],
+ [1705274618400971300, 11436],
+ [1705281863109378800, 11436],
+ [1705288569114644700, 11436],
+ [1705288790896014800, 11436],
+ [1705289034855967500, 11436],
+ [1705289034855967500, 11436],
+ [1705289512918064400, 11436],
+ [1705290250183041800, 11436],
+ [1705290310185114400, 11436],
+ [1705290736882757600, 11436],
+ [1705290796883791400, 11436],
+ [1705290856883483600, 11436],
+ [1705290987064807700, 11436],
+ [1705291047065788400, 11436],
+ ],
+ },
+ ],
+ };
if (!Array.isArray(data.results)) {
throw new Error('metrics are missing/invalid in the response'); // eslint-disable-line @gitlab/require-i18n-strings
}
- return data.results;
+ return mockReturnDataWithDelay(data.results);
} 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 b89c2624f81c..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.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);
- Go to https://local.gitlab.com:3443/flightjs/Flight/-/metrics/app.ads.ad_requests?type=Sum
- Play around with the query builder and check on the console that the proper request is being made. ( Note currently it's required to click on the Search button to kick off any search, even when selecting values from the dropdowns )
Edited by Daniele Rossetti