Skip to content

Add trace link to logs

Daniele Rossetti requested to merge drosse/link-traces-logs into master

What does this MR do and why?

Make "trace-id" section in logs drawer a link to the tracing details page of that given trace.

Part of Correlation: Link logs and traces (gitlab-org/opstrace/opstrace#2856 - 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

image

How to set up and validate locally

  • Prerequisites: be logged in and running GDK with Ultimate license
  • Enable :observability_tracing feature flag
  • Enable :observability_logs feature flag

Apply patch to load mocks ( copy the patch content below and run in your terminal: pbpaste | git apply )

diff --git a/app/assets/javascripts/observability/client.js b/app/assets/javascripts/observability/client.js
index 57efb690804e..b835e9d042cc 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
 }
 
 /** ****
@@ -147,11 +145,113 @@ async function fetchTrace(tracingUrl, traceId) {
       throw new Error('traceId is required.');
     }
 
-    const { data } = await axios.get(`${tracingUrl}/${traceId}`, {
-      withCredentials: true,
+    // const { data } = await axios.get(`${tracingUrl}/${traceId}`, {
+    //   withCredentials: true,
+    // });
+
+    // return data;
+    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',
+        },
+        {
+          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',
+        },
+        {
+          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',
+        },
+        {
+          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',
+        },
+        {
+          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',
+        },
+        {
+          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',
+        },
+        {
+          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',
+        },
+        {
+          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',
+        },
+        {
+          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',
+        },
+      ],
+      total_spans: 9,
+      totalSpans: 9,
     });
-
-    return data;
   } catch (e) {
     return reportErrorAndThrow(e);
   }
@@ -595,18 +695,64 @@ export async function fetchLogs(
     if (pageSize) {
       params.append('page_size', pageSize);
     }
-    const { data } = await axios.get(logsSearchUrl, {
-      withCredentials: true,
-      params,
-      signal: abortController?.signal,
+
+    console.log(`[DEBUG] Fetching logs with params: ${params.toString()}`);
+
+    const data = [
+      {
+        timestamp: '2024-02-19T16:10:15.4433398Z',
+        trace_id: 'abcde-fegoaisdjgio-asdgoinasoigj',
+        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);
   }
@@ -628,12 +774,356 @@ export async function fetchLogsSearchMetadata(
       addLogsAttributesFiltersToQueryParams(attributes, params);
     }
 
-    const { data } = await axios.get(logsSearchMetadataUrl, {
-      withCredentials: true,
-      params,
-      signal: abortController?.signal,
+    // TODO remove mocks (and add UTs) when API is ready https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2782
+    // const { data } = await axios.get(logsSearchMetadataUrl, {
+    //   withCredentials: true,
+    //   params,
+    // });
+    // return data;
+
+    return mockReturnDataWithDelay({
+      start_ts: 1713513680617331200,
+      end_ts: 1714723280617331200,
+      summary: {
+        service_names: ['adservice', 'cartservice', 'quoteservice', 'recommendationservice'],
+        trace_flags: [0, 1],
+        severity_names: ['info', 'warn', 'error'],
+        severity_numbers: [9, 13, 17],
+      },
+      severity_numbers_counts: [
+        {
+          time: 1713519360000000000,
+          counts: {
+            13: 1001,
+            17: 2201,
+          },
+        },
+        {
+          time: 1713545280000000000,
+          counts: {
+            13: 1231,
+            17: 501,
+          },
+        },
+        {
+          time: 1713571200000000000,
+          counts: {
+            13: 5555,
+            17: 2342,
+          },
+        },
+        {
+          time: 1713597120000000000,
+          counts: {
+            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: [],
     });
-    return 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 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);
Edited by Daniele Rossetti

Merge request reports