Add link detection to tracing drawer
What does this MR do and why?
Tracing Details - Detect links in attributes drawer and make them clickable
Closes gitlab-org/opstrace/opstrace#2571 (closed)
MR acceptance checklist
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..df301594f2e7 100644
--- a/app/assets/javascripts/observability/client.js
+++ b/app/assets/javascripts/observability/client.js
@@ -1,21 +1,38 @@
+/* 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: [],
+ total_traces: 500,
+ next_page_token:
+ 'eyJsYXN0X3NlZW5fdGltZXN0YW1wIjoiMjAyMy0xMS0xMCAxNjoyNzo0Ny4xMDQ0NzcwOTIiLCJzZWVuX3RyYWNlX2lkcyI6WyI2ZTI0NjFjZS04MWVmLTRkYWItN2ZmYS1hMTRlMDRiODhmNWUiXX0=',
+};
+
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 +41,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 +58,141 @@ 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-30T10:18:50.197Z',
+ timestamp_nano: 1701339530197000000,
+ trace_id: '85d44543-4c9c-2ced-5e75-18845b4157c9',
+ service_name: 'GitLab pipelines',
+ operation: 'https://gitlab.com/gitlab-org/gitlab/-/pipelines/1090600528',
+ status_code: 'STATUS_CODE_UNSET',
+ duration_nano: 4143985000000,
+ total_spans: 231,
+ spans: [
+ {
+ timestamp: '2023-11-30T10:18:50.197Z',
+ timestamp_nano: 1701339530197000000,
+ span_id: 'EE5CBB255408D598',
+ trace_id: '85d44543-4c9c-2ced-5e75-18845b4157c9',
+ service_name: 'GitLab pipelines',
+ operation: 'https://gitlab.com/gitlab-org/gitlab/-/pipelines/1090600528',
+ duration_nano: 4143985000000,
+ parent_span_id: '',
+ status_code: 'STATUS_CODE_UNSET',
+ status_message: '',
+ trace_state: '',
+ span_kind: 'SPAN_KIND_INTERNAL',
+ scope_name: '',
+ scope_version: '',
+ span_attributes: {
+ created_at: '2023-11-30T10:18:36.367Z',
+ duration: '4147',
+ finished_at: '2023-11-30T11:27:54.182Z',
+ queued_duration: '13',
+ ref: 'refs/merge-requests/138112/merge',
+ started_at: '2023-11-30T10:18:50.197Z',
+ status: 'success',
+ updated_at: '2023-11-30T11:27:54.197Z',
+ user: 'gitlab-bot',
+ web_url: 'https://gitlab.com/gitlab-org/gitlab/-/pipelines/1090600528',
+ },
+ resource_attributes: {
+ 'process.command': '/Users/gitlab/.asdf/installs/ruby/3.2.2/bin/rake',
+ 'process.pid': '33954',
+ 'process.runtime.description':
+ 'ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]',
+ 'process.runtime.name': 'ruby',
+ 'process.runtime.version': '3.2.2',
+ 'service.name': 'GitLab pipelines',
+ 'telemetry.sdk.language': 'ruby',
+ 'telemetry.sdk.name': 'opentelemetry',
+ 'telemetry.sdk.version': '1.3.1',
+ },
+ },
+ {
+ timestamp: '2023-11-30T10:18:45.411Z',
+ timestamp_nano: 1701339525411000000,
+ span_id: 'F924C183EA08B48F',
+ trace_id: '85d44543-4c9c-2ced-5e75-18845b4157c9',
+ service_name: 'GitLab pipelines',
+ operation: 'rails-production-server-boot-puma-example',
+ duration_nano: 250128000000,
+ parent_span_id: 'EE5CBB255408D598',
+ status_code: 'STATUS_CODE_UNSET',
+ status_message: '',
+ trace_state: '',
+ span_kind: 'SPAN_KIND_INTERNAL',
+ scope_name: '',
+ scope_version: '',
+ span_attributes: {
+ allow_failure: 'false',
+ duration: '250.128455',
+ finished_at: '2023-11-30T10:22:55.539Z',
+ id: '5651404193',
+ job_name: 'rails-production-server-boot-puma-example',
+ queued_duration: '0.310679',
+ stage: 'preflight',
+ started_at: '2023-11-30T10:18:45.411Z',
+ status: 'success',
+ user: 'gitlab-bot',
+ web_url: 'https://gitlab.com/gitlab-org/gitlab/-/jobs/5651404193',
+ },
+ resource_attributes: {
+ 'process.command': '/Users/gitlab/.asdf/installs/ruby/3.2.2/bin/rake',
+ 'process.pid': '33954',
+ 'process.runtime.description':
+ 'ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]',
+ 'process.runtime.name': 'ruby',
+ 'process.runtime.version': '3.2.2',
+ 'service.name': 'GitLab pipelines',
+ 'telemetry.sdk.language': 'ruby',
+ 'telemetry.sdk.name': 'opentelemetry',
+ 'telemetry.sdk.version': '1.3.1',
+ },
+ },
+ {
+ timestamp: '2023-11-30T10:18:46.145Z',
+ timestamp_nano: 1701339526145000000,
+ span_id: 'EBBE05DFF04D1F40',
+ trace_id: '85d44543-4c9c-2ced-5e75-18845b4157c9',
+ service_name: 'GitLab pipelines',
+ operation: 'rails-production-server-boot-puma-cng',
+ duration_nano: 227918000000,
+ parent_span_id: 'EE5CBB255408D598',
+ status_code: 'STATUS_CODE_UNSET',
+ status_message: '',
+ trace_state: '',
+ span_kind: 'SPAN_KIND_INTERNAL',
+ scope_name: '',
+ scope_version: '',
+ span_attributes: {
+ allow_failure: 'false',
+ duration: '227.918133',
+ finished_at: '2023-11-30T10:22:34.063Z',
+ id: '5651404196',
+ job_name: 'rails-production-server-boot-puma-cng',
+ queued_duration: '0.748236',
+ stage: 'preflight',
+ started_at: '2023-11-30T10:18:46.145Z',
+ status: 'success',
+ user: 'gitlab-bot',
+ web_url: 'https://gitlab.com/gitlab-org/gitlab/-/jobs/5651404196',
+ },
+ resource_attributes: {
+ 'process.command': '/Users/gitlab/.asdf/installs/ruby/3.2.2/bin/rake',
+ 'process.pid': '33954',
+ 'process.runtime.description':
+ 'ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]',
+ 'process.runtime.name': 'ruby',
+ 'process.runtime.version': '3.2.2',
+ 'service.name': 'GitLab pipelines',
+ 'telemetry.sdk.language': 'ruby',
+ 'telemetry.sdk.name': 'opentelemetry',
+ 'telemetry.sdk.version': '1.3.1',
+ },
+ },
+ ],
+ });
}
/**
@@ -198,15 +338,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 +354,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 +379,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 +416,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);
Edited by Daniele Rossetti