Add context info badge to tracing table row
What does this MR do and why?
Add info badge to tracing table rows
Closes https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2381+
Screenshots or screen recordings
Before | After |
---|---|
How to set up and validate locally
- Enable
observability_tracing
feature flag
Apply patch to load mocks ( 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 3a793c9dc140..272b4c72f91f 100644
--- a/app/assets/javascripts/observability/client.js
+++ b/app/assets/javascripts/observability/client.js
@@ -1,21 +1,169 @@
+/* eslint-disable @gitlab/require-i18n-strings */
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: [],
+};
+
+const MOCK_TRACES = {
+ project_id: 51792562,
+ traces: [
+ {
+ timestamp: '2023-12-15T04:13:41.864575716Z',
+ timestamp_nano: 1702613621864575716,
+ trace_id: '0a6a946f-a29d-c8e9-dde0-abf0330680e3',
+ service_name: 'loadgenerator',
+ operation: 'GET',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 11626167,
+ total_spans: 1,
+ matched_span_count: 1,
+ totalSpans: 1,
+ statusCode: '',
+ },
+ {
+ timestamp: '2023-12-15T04:13:41.840480675Z',
+ timestamp_nano: 1702613621840480675,
+ trace_id: '4a69f8c5-d2f6-b06f-a9d6-12628d8463ed',
+ service_name: 'productcatalogservice',
+ operation: 'oteldemo.ProductCatalogService/ListProducts',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 22917,
+ total_spans: 5,
+ matched_span_count: 5,
+ totalSpans: 5,
+ statusCode: '',
+ },
+ {
+ timestamp: '2023-12-15T04:13:41.824047675Z',
+ timestamp_nano: 1702613621824047675,
+ trace_id: 'df48841f-b569-7617-f376-6a8cd3a8a71b',
+ service_name: 'loadgenerator',
+ operation: 'GET',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 23461541,
+ total_spans: 1,
+ matched_span_count: 1,
+ totalSpans: 1,
+ statusCode: '',
+ },
+ {
+ timestamp: '2023-12-15T04:13:40.972834174Z',
+ timestamp_nano: 1702613620972834174,
+ trace_id: '51a50df3-f290-f278-42d4-7fba20a11006',
+ service_name: 'loadgenerator',
+ operation: 'GET',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 27841167,
+ total_spans: 1,
+ matched_span_count: 1,
+ totalSpans: 1,
+ statusCode: '',
+ },
+ {
+ timestamp: '2023-12-15T04:13:40.604914341Z',
+ timestamp_nano: 1702613620604914341,
+ trace_id: '4d2d4ab2-d24b-cb81-9d3a-5b6b98417633',
+ service_name: 'loadgenerator',
+ operation: 'GET',
+ status_code: 'STATUS_CODE_ERROR',
+ duration_nano: 20185083,
+ total_spans: 1,
+ matched_span_count: 1,
+ totalSpans: 1,
+ statusCode: '',
+ },
+ {
+ timestamp: '2023-12-15T04:13:39.952610715Z',
+ timestamp_nano: 1702613619952610715,
+ trace_id: '472401eb-b691-c0c8-3696-1a26c97970c0',
+ service_name: 'loadgenerator',
+ operation: 'GET',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 24003750,
+ total_spans: 2,
+ matched_span_count: 2,
+ totalSpans: 2,
+ statusCode: '',
+ },
+ {
+ timestamp: '2023-12-15T04:13:38.817981048Z',
+ timestamp_nano: 1702613618817981048,
+ trace_id: '22eef29b-89be-b91e-e00d-1c9639d3a8a0',
+ service_name: 'productcatalogservice',
+ operation: 'oteldemo.ProductCatalogService/GetProduct',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 27500,
+ total_spans: 1,
+ matched_span_count: 1,
+ totalSpans: 1,
+ statusCode: '',
+ },
+ {
+ timestamp: '2023-12-15T04:13:38.808547673Z',
+ timestamp_nano: 1702613618808547673,
+ trace_id: 'ec7ff018-969a-223e-07bc-99e9527e532a',
+ service_name: 'loadgenerator',
+ operation: 'GET',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 14730500,
+ total_spans: 1,
+ matched_span_count: 1,
+ totalSpans: 1,
+ statusCode: '',
+ },
+ {
+ timestamp: '2023-12-15T04:13:38.660488381Z',
+ timestamp_nano: 1702613618660488381,
+ trace_id: '2122dad9-40aa-c19e-269a-648e87219fc2',
+ service_name: 'adservice',
+ operation: 'oteldemo.AdService/GetAds',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 959584,
+ total_spans: 2,
+ matched_span_count: 2,
+ totalSpans: 2,
+ statusCode: '',
+ },
+ {
+ timestamp: '2023-12-15T04:13:38.640527673Z',
+ timestamp_nano: 1702613618640527673,
+ trace_id: 'ebe87f9e-722b-e375-67b2-6e70b09475ce',
+ service_name: 'loadgenerator',
+ operation: 'GET',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 23356083,
+ total_spans: 5,
+ matched_span_count: 5,
+ totalSpans: 5,
+ statusCode: '',
+ },
+ ],
+ total_traces: 10,
+ next_page_token:
+ 'eyJsYXN0X3NlZW5fdGltZXN0YW1wIjoiMjAyMy0xMi0xNSAwNDoxMzozOC42NDA1Mjc2NzMiLCJzZWVuX3RyYWNlX2lkcyI6WyJlYmU4N2Y5ZS03MjJiLWUzNzUtNjdiMi02ZTcwYjA5NDc1Y2UiXX0=',
+};
+
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);
}
@@ -24,11 +172,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) {
@@ -40,19 +189,119 @@ async function isObservabilityEnabled(provisioningUrl) {
}
async function fetchTrace(tracingUrl, traceId) {
- try {
- if (!traceId) {
- throw new Error('traceId is required.');
- }
-
- const { data } = await axios.get(`${tracingUrl}/${traceId}`, {
- withCredentials: true,
- });
-
- return data;
- } catch (e) {
- return reportErrorAndThrow(e);
- }
+ console.log(`[DEBUG] Fetch trace ${traceId} from ${tracingUrl}`);
+ return mockReturnDataWithDelay({
+ timestamp: '2023-11-06T14:58:38.892999936Z',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'frontend',
+ operation: 'HTTP POST',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 6870528,
+ spans: [
+ {
+ timestamp: '2023-11-06T14:58:38.892999936Z',
+ span_id: '86C2CAF54D03A839',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'frontend',
+ operation: 'HTTP POST',
+ duration_nano: 6870528,
+ parent_span_id: '',
+ status_code: 'STATUS_CODE_UNSET',
+ statusCode: 'STATUS_CODE_UNSET',
+ },
+ {
+ timestamp: '2023-11-06T14:58:38.792999900Z',
+ span_id: '5E95BA1D4DCA629C',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'frontend',
+ operation: 'grpc.oteldemo.CartService/AddItem',
+ duration_nano: 4674123,
+ parent_span_id: '86C2CAF54D03A839',
+ status_code: 'STATUS_CODE_UNSET',
+ statusCode: 'STATUS_CODE_UNSET',
+ },
+ {
+ timestamp: '2023-11-06T14:58:38.897313Z',
+ span_id: '79A1A33CCC36DC44',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'cartservice',
+ operation: 'oteldemo.CartService/AddItem',
+ duration_nano: 1138200,
+ parent_span_id: '5E95BA1D4DCA629C',
+ status_code: 'STATUS_CODE_UNSET',
+ statusCode: 'STATUS_CODE_UNSET',
+ },
+ {
+ timestamp: '2023-11-06T14:58:38.8974467Z',
+ span_id: 'B43E6CFFD9AF4A68',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'cartservice',
+ operation: 'HGET',
+ duration_nano: 360700,
+ parent_span_id: '79A1A33CCC36DC44',
+ status_code: 'STATUS_CODE_UNSET',
+ statusCode: 'STATUS_CODE_UNSET',
+ },
+ {
+ timestamp: '2023-11-06T14:58:38.8978547Z',
+ span_id: '80169B2C61AF41EF',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'cartservice',
+ operation: 'HMSET',
+ duration_nano: 249500,
+ parent_span_id: '79A1A33CCC36DC44',
+ status_code: 'STATUS_CODE_UNSET',
+ statusCode: 'STATUS_CODE_UNSET',
+ },
+ {
+ timestamp: '2023-11-06T14:58:38.897999872Z',
+ span_id: '6C4E28FE982F2F73',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'frontend',
+ operation: 'grpc.oteldemo.CartService/GetCart',
+ duration_nano: 1346816,
+ parent_span_id: '86C2CAF54D03A839',
+ status_code: 'STATUS_CODE_UNSET',
+ statusCode: 'STATUS_CODE_UNSET',
+ },
+ {
+ timestamp: '2023-11-06T14:58:38.8981128Z',
+ span_id: '427F06B0B498A482',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'cartservice',
+ operation: 'EXPIRE',
+ duration_nano: 252200,
+ parent_span_id: '79A1A33CCC36DC44',
+ status_code: 'STATUS_CODE_UNSET',
+ statusCode: 'STATUS_CODE_UNSET',
+ },
+ {
+ timestamp: '2023-11-06T14:58:38.8995004Z',
+ span_id: 'FF45FE0F8C45FD68',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'cartservice',
+ operation: 'oteldemo.CartService/GetCart',
+ duration_nano: 512400,
+ parent_span_id: '6C4E28FE982F2F73',
+ status_code: 'STATUS_CODE_UNSET',
+ statusCode: 'STATUS_CODE_UNSET',
+ },
+ {
+ timestamp: '2023-11-06T14:58:38.8996313Z',
+ span_id: 'F6D0D268E8A84A38',
+ trace_id: 'cfa0e008-002f-5505-0d05-31855d493ea0',
+ service_name: 'cartservice',
+ operation: 'HGET',
+ duration_nano: 290700,
+ parent_span_id: 'FF45FE0F8C45FD68',
+ status_code: 'STATUS_CODE_UNSET',
+ statusCode: 'STATUS_CODE_UNSET',
+ },
+ ],
+ total_spans: 9,
+ totalSpans: 9,
+ statusCode: 'STATUS_CODE_UNSET',
+ });
}
/**
@@ -198,15 +447,15 @@ async function fetchTraces(tracingUrl, { filters = {}, pageToken, pageSize, sort
: DEFAULT_SORTING_OPTION;
params.append('sort', sortOrder);
+ console.log(`[DEBUG] Fetching traces with params: ${params.toString()}`);
+
try {
- const { data } = await axios.get(tracingUrl, {
- withCredentials: true,
- params,
- });
+ const data = MOCK_TRACES;
+
if (!Array.isArray(data.traces)) {
throw new Error('traces are missing/invalid in the response'); // eslint-disable-line @gitlab/require-i18n-strings
}
- return data;
+ return mockReturnDataWithDelay(data);
} catch (e) {
return reportErrorAndThrow(e);
}
@@ -214,15 +463,17 @@ async function fetchTraces(tracingUrl, { filters = {}, pageToken, pageSize, sort
async function fetchServices(servicesUrl) {
try {
- const { data } = await axios.get(servicesUrl, {
- withCredentials: true,
- });
+ console.log(`[DEBUG] Fetching services from ${servicesUrl}`);
+ const uniqueServices = new Set(
+ MOCK_TRACES.traces.map((t) => t.spans.map((s) => s.service_name)).flat(),
+ );
+ const data = { services: Array.from(uniqueServices).map((s) => ({ name: s })) };
if (!Array.isArray(data.services)) {
throw new Error('failed to fetch services. invalid response'); // eslint-disable-line @gitlab/require-i18n-strings
}
- return data.services;
+ return mockReturnDataWithDelay(data.services);
} catch (e) {
return reportErrorAndThrow(e);
}
@@ -237,15 +488,21 @@ async function fetchOperations(operationsUrl, serviceName) {
throw new Error('fetchOperations() - operationsUrl must contain $SERVICE_NAME$');
}
const url = operationsUrl.replace('$SERVICE_NAME$', serviceName);
- const { data } = await axios.get(url, {
- withCredentials: true,
- });
+
+ console.log('[DEBUG] fetching operations suggestions from', url); // eslint-disable-line @gitlab/require-i18n-strings
+ const uniqOps = new Set(
+ MOCK_TRACES.traces
+ .map((t) => t.spans.filter((s) => s.service_name === serviceName))
+ .flat()
+ .map((s) => s.operation),
+ );
+ const data = { operations: Array.from(uniqOps).map((s) => ({ name: s })) };
if (!Array.isArray(data.operations)) {
throw new Error('failed to fetch operations. invalid response'); // eslint-disable-line @gitlab/require-i18n-strings
}
- return data.operations;
+ return mockReturnDataWithDelay(data.operations);
} catch (e) {
return reportErrorAndThrow(e);
}
@@ -268,14 +525,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);
}
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);
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.
Edited by Daniele Rossetti