From 3d77ff4190d74bf7e0402fbe94286e6d259f13e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Tue, 2 Sep 2025 12:29:48 +0200 Subject: [PATCH 01/14] [Discover][APM] Unify Span/Transaction into single Overview tab --- .../shared/kbn-discover-utils/index.ts | 2 + .../shared/kbn-discover-utils/src/types.ts | 11 ++ .../src/utils/get_trace_document_overview.ts | 55 ++++++ .../kbn-discover-utils/src/utils/index.ts | 1 + .../observability_profile_providers.ts | 6 +- .../accessors/doc_viewer.tsx | 14 +- .../accessors/index.ts | 0 .../accessors => document_profile}/index.ts | 2 +- .../profile.test.ts | 6 +- .../profile.ts | 4 +- .../span_document_profile/index.ts | 10 -- .../accessors/doc_viewer.tsx | 52 ------ .../transaction_document_profile/index.ts | 10 -- .../profile.test.ts | 109 ------------ .../transaction_document_profile/profile.ts | 69 ------- .../full_screen_waterfall/index.tsx | 2 +- .../span_flyout/span_flyout_body.tsx | 29 +-- .../traces/components/service_name_link.tsx | 16 +- .../observability/traces/components/trace.tsx | 12 +- .../components/transaction_name_link.tsx | 21 ++- .../hooks/use_latency_chart.test.tsx} | 14 +- .../hooks/use_latency_chart.tsx} | 147 ++++++++++++--- .../hooks}/use_root_span.test.tsx | 6 +- .../hooks/use_root_span.ts} | 2 +- .../hooks}/use_root_transaction.test.tsx | 6 +- .../hooks/use_root_transaction.ts} | 2 +- .../index.ts | 4 +- .../lazy_doc_viewer_obs_traces_overview.tsx} | 2 +- .../overview.stories.tsx} | 8 +- .../overview.tsx} | 80 +++++---- .../resources/fields.ts | 9 +- .../resources/get_field_configuration.tsx} | 33 +++- .../sub_components/dependency_name_link.tsx | 16 +- .../sub_components/duration_summary.tsx} | 36 ++-- .../sub_components/summary_field.tsx} | 6 +- .../sub_components/summary_title.test.tsx} | 28 ++- .../sub_components/summary_title.tsx | 121 +++++++++++++ .../hooks/use_span_latency_chart/index.ts | 141 --------------- .../sub_components/span_summary_title.tsx | 84 --------- .../use_transaction_latency_chart.test.tsx | 133 -------------- .../doc_viewer_transaction_overview/index.ts | 14 -- ...viewer_obs_traces_transaction_overview.tsx | 20 --- .../resources/fields.ts | 29 --- .../get_transaction_field_configuration.tsx | 51 ------ .../transaction_duration_summary/index.tsx | 116 ------------ .../transaction_summary_field.tsx | 52 ------ .../transaction_summary_title.test.tsx | 142 --------------- .../transaction_summary_title.tsx | 104 ----------- .../transaction_overview.stories.tsx | 46 ----- .../transaction_overview.tsx | 168 ------------------ .../resources/get_field_configuration.tsx | 10 +- .../unified_doc_viewer/public/index.tsx | 3 +- 52 files changed, 516 insertions(+), 1548 deletions(-) create mode 100644 src/platform/packages/shared/kbn-discover-utils/src/utils/get_trace_document_overview.ts rename src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/{span_document_profile => document_profile}/accessors/doc_viewer.tsx (78%) rename src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/{span_document_profile => document_profile}/accessors/index.ts (100%) rename src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/{transaction_document_profile/accessors => document_profile}/index.ts (85%) rename src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/{span_document_profile => document_profile}/profile.test.ts (95%) rename src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/{span_document_profile => document_profile}/profile.ts (94%) delete mode 100644 src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/index.ts delete mode 100644 src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/accessors/doc_viewer.tsx delete mode 100644 src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/index.ts delete mode 100644 src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/profile.test.ts delete mode 100644 src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/profile.ts rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/hooks/use_span_latency_chart/use_span_latency_chart.test.tsx => doc_viewer_overview/hooks/use_latency_chart.test.tsx} (88%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_transaction_overview/hooks/use_transaction_latency_chart/index.ts => doc_viewer_overview/hooks/use_latency_chart.tsx} (50%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/hooks/use_root_span => doc_viewer_overview/hooks}/use_root_span.test.tsx (96%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/hooks/use_root_span/index.ts => doc_viewer_overview/hooks/use_root_span.ts} (98%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_transaction_overview/hooks/use_root_transaction => doc_viewer_overview/hooks}/use_root_transaction.test.tsx (96%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_transaction_overview/hooks/use_root_transaction/index.ts => doc_viewer_overview/hooks/use_root_transaction.ts} (98%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview => doc_viewer_overview}/index.ts (87%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/lazy_doc_viewer_obs_traces_span_overview.tsx => doc_viewer_overview/lazy_doc_viewer_obs_traces_overview.tsx} (88%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/span_overview.stories.tsx => doc_viewer_overview/overview.stories.tsx} (88%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/span_overview.tsx => doc_viewer_overview/overview.tsx} (65%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview => doc_viewer_overview}/resources/fields.ts (69%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/resources/get_span_field_configuration.tsx => doc_viewer_overview/resources/get_field_configuration.tsx} (77%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview => doc_viewer_overview}/sub_components/dependency_name_link.tsx (91%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/sub_components/span_duration_summary/index.tsx => doc_viewer_overview/sub_components/duration_summary.tsx} (80%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/sub_components/span_summary_field.tsx => doc_viewer_overview/sub_components/summary_field.tsx} (95%) rename src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/{doc_viewer_span_overview/sub_components/span_summary_title.test.tsx => doc_viewer_overview/sub_components/summary_title.test.tsx} (80%) create mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_span_latency_chart/index.ts delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/span_summary_title.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_transaction_latency_chart/use_transaction_latency_chart.test.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/index.ts delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/lazy_doc_viewer_obs_traces_transaction_overview.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/resources/fields.ts delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/resources/get_transaction_field_configuration.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_duration_summary/index.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_field.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_title.test.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_title.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/transaction_overview.stories.tsx delete mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/transaction_overview.tsx diff --git a/src/platform/packages/shared/kbn-discover-utils/index.ts b/src/platform/packages/shared/kbn-discover-utils/index.ts index 09bd292700ae3..085ea9c72a083 100644 --- a/src/platform/packages/shared/kbn-discover-utils/index.ts +++ b/src/platform/packages/shared/kbn-discover-utils/index.ts @@ -42,6 +42,8 @@ export { getLogDocumentOverview, getTransactionDocumentOverview, getSpanDocumentOverview, + getTraceDocumentOverview, + getFlattenedTraceDocumentOverview, getIgnoredReason, getMessageFieldWithFallbacks, getShouldShowFieldHandler, diff --git a/src/platform/packages/shared/kbn-discover-utils/src/types.ts b/src/platform/packages/shared/kbn-discover-utils/src/types.ts index ed4a5eaf35772..b92cd53c31cb4 100644 --- a/src/platform/packages/shared/kbn-discover-utils/src/types.ts +++ b/src/platform/packages/shared/kbn-discover-utils/src/types.ts @@ -131,6 +131,17 @@ export interface SpanDocumentOverview 'resource.attributes.telemetry.sdk.language'?: string; } +export interface TraceDocumentOverview + extends TraceFields, + Partial, + Partial, + Partial, + Partial { + duration?: number; + kind?: string; + 'resource.attributes.telemetry.sdk.language'?: string; +} + export interface TraceFields { '@timestamp': number; 'trace.id': string; diff --git a/src/platform/packages/shared/kbn-discover-utils/src/utils/get_trace_document_overview.ts b/src/platform/packages/shared/kbn-discover-utils/src/utils/get_trace_document_overview.ts new file mode 100644 index 0000000000000..3b0eafcbc053c --- /dev/null +++ b/src/platform/packages/shared/kbn-discover-utils/src/utils/get_trace_document_overview.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { DataView } from '@kbn/data-views-plugin/common'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { DataTableRecord, TraceDocumentOverview } from '../types'; +import { fieldConstants } from '..'; +import { getFormattedFields } from './get_formatted_fields'; +import { getFlattenedFields } from './get_flattened_fields'; + +const fields: Array = [ + fieldConstants.TIMESTAMP_FIELD, + fieldConstants.PARENT_ID_FIELD, + fieldConstants.HTTP_RESPONSE_STATUS_CODE_FIELD, + fieldConstants.TRACE_ID_FIELD, + fieldConstants.SERVICE_NAME_FIELD, + fieldConstants.SERVICE_ENVIRONMENT_FIELD, + fieldConstants.AGENT_NAME_FIELD, + fieldConstants.TRANSACTION_ID_FIELD, + fieldConstants.TRANSACTION_NAME_FIELD, + fieldConstants.TRANSACTION_DURATION_FIELD, + fieldConstants.TRANSACTION_TYPE_FIELD, + fieldConstants.USER_AGENT_NAME_FIELD, + fieldConstants.USER_AGENT_VERSION_FIELD, + fieldConstants.SPAN_NAME_FIELD, + fieldConstants.SPAN_ID_FIELD, + fieldConstants.SPAN_ACTION_FIELD, + fieldConstants.SPAN_DURATION_FIELD, + fieldConstants.SPAN_TYPE_FIELD, + fieldConstants.SPAN_SUBTYPE_FIELD, + fieldConstants.SPAN_DESTINATION_SERVICE_RESOURCE_FIELD, + fieldConstants.USER_AGENT_NAME_FIELD, + fieldConstants.USER_AGENT_VERSION_FIELD, + fieldConstants.PROCESSOR_EVENT_FIELD, + fieldConstants.OTEL_DURATION, + fieldConstants.OTEL_SPAN_KIND, + fieldConstants.OTEL_RESOURCE_ATTRIBUTES_TELEMETRY_SDK_LANGUAGE, +]; + +export function getTraceDocumentOverview( + doc: DataTableRecord, + { dataView, fieldFormats }: { dataView: DataView; fieldFormats: FieldFormatsStart } +): TraceDocumentOverview { + return getFormattedFields(doc, fields, { dataView, fieldFormats }); +} + +export function getFlattenedTraceDocumentOverview(doc: DataTableRecord): TraceDocumentOverview { + return getFlattenedFields(doc, fields); +} diff --git a/src/platform/packages/shared/kbn-discover-utils/src/utils/index.ts b/src/platform/packages/shared/kbn-discover-utils/src/utils/index.ts index 4b8c30543f4d9..903873a0f54a3 100644 --- a/src/platform/packages/shared/kbn-discover-utils/src/utils/index.ts +++ b/src/platform/packages/shared/kbn-discover-utils/src/utils/index.ts @@ -15,6 +15,7 @@ export * from './format_value'; export * from './get_doc_id'; export * from './get_ignored_reason'; export * from './get_log_document_overview'; +export * from './get_trace_document_overview'; export * from './get_transaction_document_overview'; export * from './get_span_document_overview'; export * from './get_message_field_with_fallbacks'; diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/observability_profile_providers.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/observability_profile_providers.ts index cf51f868efa19..8e4e2de43b289 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/observability_profile_providers.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/observability_profile_providers.ts @@ -9,15 +9,13 @@ import type { ProfileProviderServices } from '../profile_provider_services'; import { createObservabilityLogDocumentProfileProvider } from './log_document_profile'; -import { createObservabilityTracesSpanDocumentProfileProvider } from './traces_document_profile/span_document_profile'; -import { createObservabilityTracesTransactionDocumentProfileProvider } from './traces_document_profile/transaction_document_profile'; +import { createObservabilityTracesDocumentProfileProvider } from './traces_document_profile/document_profile'; export const createObservabilityDocumentProfileProviders = ( providerServices: ProfileProviderServices ) => { return [ createObservabilityLogDocumentProfileProvider(providerServices), - createObservabilityTracesSpanDocumentProfileProvider(providerServices), - createObservabilityTracesTransactionDocumentProfileProvider(providerServices), + createObservabilityTracesDocumentProfileProvider(providerServices), ]; }; diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/accessors/doc_viewer.tsx b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/accessors/doc_viewer.tsx similarity index 78% rename from src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/accessors/doc_viewer.tsx rename to src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/accessors/doc_viewer.tsx index 49d5851e618db..cdcc097c4fb12 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/accessors/doc_viewer.tsx +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/accessors/doc_viewer.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { UnifiedDocViewerObservabilityTracesSpanOverview } from '@kbn/unified-doc-viewer-plugin/public'; +import { UnifiedDocViewerObservabilityTracesOverview } from '@kbn/unified-doc-viewer-plugin/public'; import type { DocViewsRegistry } from '@kbn/unified-doc-viewer'; import type { DocumentProfileProvider } from '../../../../../profiles'; import type { DocViewerExtensionParams, DocViewerExtension } from '../../../../../types'; @@ -22,19 +22,19 @@ export const createGetDocViewer = (prev: (params: DocViewerExtensionParams) => DocViewerExtension) => (params: DocViewerExtensionParams) => { const prevDocViewer = prev(params); - const tabTitle = i18n.translate('discover.docViews.observability.traces.spanOverview.title', { - defaultMessage: 'Span overview', + const tabTitle = i18n.translate('discover.docViews.observability.traces.traceOverview.title', { + defaultMessage: 'Trace Overview', }); return { ...prevDocViewer, docViewsRegistry: (registry: DocViewsRegistry) => { registry.add({ - id: 'doc_view_obs_traces_span_overview', + id: 'doc_view_obs_traces_overview', title: tabTitle, order: 0, - component: (props) => { - return ; - }, + component: (props) => ( + + ), }); return prevDocViewer.docViewsRegistry(registry); diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/accessors/index.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/accessors/index.ts similarity index 100% rename from src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/accessors/index.ts rename to src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/accessors/index.ts diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/accessors/index.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/index.ts similarity index 85% rename from src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/accessors/index.ts rename to src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/index.ts index 07c68375c440c..3ec1c1a7431f3 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/accessors/index.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/index.ts @@ -7,4 +7,4 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export { createGetDocViewer } from './doc_viewer'; +export { createObservabilityTracesDocumentProfileProvider } from './profile'; diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/profile.test.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts similarity index 95% rename from src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/profile.test.ts rename to src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts index 275023f5a7c10..1023d8b6764e5 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/profile.test.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts @@ -11,7 +11,7 @@ import { buildDataTableRecord } from '@kbn/discover-utils'; import type { DataSourceContext, RootContext } from '../../../../profiles'; import { DataSourceCategory, DocumentType, SolutionType } from '../../../../profiles'; import { createContextAwarenessMocks } from '../../../../__mocks__'; -import { createObservabilityTracesSpanDocumentProfileProvider } from './profile'; +import { createObservabilityTracesDocumentProfileProvider } from './profile'; import type { ContextWithProfileId } from '../../../../profile_service'; import { OBSERVABILITY_ROOT_PROFILE_ID } from '../../consts'; import type { ProfileProviderServices } from '../../../profile_provider_services'; @@ -52,7 +52,7 @@ describe('spanDocumentProfileProvider', () => { const profileId = OBSERVABILITY_ROOT_PROFILE_ID; const spanDocumentProfileProvider = - createObservabilityTracesSpanDocumentProfileProvider(mockServices); + createObservabilityTracesDocumentProfileProvider(mockServices); it('matches records with the correct data stream type and the correct processor event', () => { expect( @@ -120,7 +120,7 @@ describe('spanDocumentProfileProvider', () => { const profileId = 'another-profile'; const solutionType = SolutionType.Security; const spanDocumentProfileProvider = - createObservabilityTracesSpanDocumentProfileProvider(mockServices); + createObservabilityTracesDocumentProfileProvider(mockServices); it('does not match records with the correct data stream type and the correct processor event', () => { expect( diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/profile.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts similarity index 94% rename from src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/profile.ts rename to src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts index 2b8cb09f23eb5..365359aba341c 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/profile.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts @@ -17,7 +17,7 @@ import { createGetDocViewer } from './accessors'; const OBSERVABILITY_TRACES_SPAN_DOCUMENT_PROFILE_ID = 'observability-traces-span-document-profile'; -export const createObservabilityTracesSpanDocumentProfileProvider = ({ +export const createObservabilityTracesDocumentProfileProvider = ({ tracesContextService, apmErrorsContextService, logsContextService, @@ -65,7 +65,7 @@ const isSpanDocument = (record: DataTableRecord) => { const dataStreamType = getFieldValue(record, DATASTREAM_TYPE_FIELD); const processorEvent = getFieldValue(record, PROCESSOR_EVENT_FIELD); - const isApmSpan = processorEvent === 'span'; + const isApmSpan = processorEvent === 'span' || processorEvent === 'transaction'; const isOtelSpan = processorEvent == null; return dataStreamType === 'traces' && (isApmSpan || isOtelSpan); diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/index.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/index.ts deleted file mode 100644 index 18a0109782d24..0000000000000 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/span_document_profile/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export { createObservabilityTracesSpanDocumentProfileProvider } from './profile'; diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/accessors/doc_viewer.tsx b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/accessors/doc_viewer.tsx deleted file mode 100644 index 7b5f94e2f8266..0000000000000 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/accessors/doc_viewer.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { UnifiedDocViewerObservabilityTracesTransactionOverview } from '@kbn/unified-doc-viewer-plugin/public'; -import type { DocViewsRegistry } from '@kbn/unified-doc-viewer'; -import type { DocumentProfileProvider } from '../../../../..'; -import type { DocViewerExtensionParams, DocViewerExtension } from '../../../../../types'; - -export const createGetDocViewer = - (indexes: { - apm: { errors: string; traces: string }; - logs: string; - }): DocumentProfileProvider['profile']['getDocViewer'] => - (prev: (params: DocViewerExtensionParams) => DocViewerExtension) => - (params: DocViewerExtensionParams) => { - const prevDocViewer = prev(params); - const tabTitle = i18n.translate( - 'discover.docViews.observability.traces.transactionOverview.title', - { - defaultMessage: 'Transaction overview', - } - ); - - return { - ...prevDocViewer, - docViewsRegistry: (registry: DocViewsRegistry) => { - registry.add({ - id: 'doc_view_obs_traces_transaction_overview', - title: tabTitle, - order: 0, - component: (props) => { - return ( - - ); - }, - }); - - return prevDocViewer.docViewsRegistry(registry); - }, - }; - }; diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/index.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/index.ts deleted file mode 100644 index 8195c581ca6d8..0000000000000 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export { createObservabilityTracesTransactionDocumentProfileProvider } from './profile'; diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/profile.test.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/profile.test.ts deleted file mode 100644 index 5295e3e8f68de..0000000000000 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/profile.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { buildDataTableRecord } from '@kbn/discover-utils'; -import type { DataSourceContext, RootContext } from '../../../../profiles'; -import { DataSourceCategory, DocumentType, SolutionType } from '../../../../profiles'; -import { createObservabilityTracesTransactionDocumentProfileProvider } from './profile'; -import type { ContextWithProfileId } from '../../../../profile_service'; -import { OBSERVABILITY_ROOT_PROFILE_ID } from '../../consts'; -import { createContextAwarenessMocks } from '../../../../__mocks__'; -import type { ProfileProviderServices } from '../../../profile_provider_services'; - -describe('transactionDocumentProfileProvider', () => { - const getRootContext = ({ - profileId, - solutionType, - }: { - profileId: string; - solutionType?: SolutionType; - }): ContextWithProfileId => { - return { - profileId, - solutionType: solutionType ?? SolutionType.Observability, - }; - }; - - const DATA_SOURCE_CONTEXT: ContextWithProfileId = { - profileId: 'traces-transaction-document-profile', - category: DataSourceCategory.Traces, - }; - const RESOLUTION_MATCH = { - isMatch: true, - context: { - type: DocumentType.Transaction, - }, - }; - const RESOLUTION_MISMATCH = { - isMatch: false, - }; - - const mockServices: ProfileProviderServices = { - ...createContextAwarenessMocks().profileProviderServices, - }; - - describe('when root profile is observability', () => { - const profileId = OBSERVABILITY_ROOT_PROFILE_ID; - const transactionDocumentProfileProvider = - createObservabilityTracesTransactionDocumentProfileProvider(mockServices); - - it('matches records with the correct data stream type and the correct processor event', () => { - expect( - transactionDocumentProfileProvider.resolve({ - rootContext: getRootContext({ profileId }), - dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildMockRecord('index', { - 'data_stream.type': ['traces'], - 'processor.event': ['transaction'], - }), - }) - ).toEqual(RESOLUTION_MATCH); - }); - - it('does not match records with neither characteristic', () => { - expect( - transactionDocumentProfileProvider.resolve({ - rootContext: getRootContext({ profileId }), - dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildMockRecord('another-index'), - }) - ).toEqual(RESOLUTION_MISMATCH); - }); - }); - - describe('when solutionType is NOT observability', () => { - const profileId = OBSERVABILITY_ROOT_PROFILE_ID; - const solutionType = SolutionType.Default; - const transactionDocumentProfileProvider = - createObservabilityTracesTransactionDocumentProfileProvider(mockServices); - - it('does not match records with the correct data stream type and the correct processor event', () => { - expect( - transactionDocumentProfileProvider.resolve({ - rootContext: getRootContext({ profileId, solutionType }), - dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildMockRecord('index', { - 'data_stream.type': ['traces'], - 'processor.event': ['transaction'], - }), - }) - ).toEqual(RESOLUTION_MISMATCH); - }); - }); -}); - -const buildMockRecord = (index: string, fields: Record = {}) => - buildDataTableRecord({ - _id: '', - _index: index, - fields: { - _index: index, - ...fields, - }, - }); diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/profile.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/profile.ts deleted file mode 100644 index 57fb3e4cfff0c..0000000000000 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/transaction_document_profile/profile.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { DataTableRecord } from '@kbn/discover-utils'; -import { DATASTREAM_TYPE_FIELD, getFieldValue, PROCESSOR_EVENT_FIELD } from '@kbn/discover-utils'; -import { TRACES_PRODUCT_FEATURE_ID } from '../../../../../../common/constants'; -import type { DocumentProfileProvider } from '../../../../profiles'; -import { DocumentType, SolutionType } from '../../../../profiles'; -import { createGetDocViewer } from './accessors'; -import type { ProfileProviderServices } from '../../../profile_provider_services'; - -const OBSERVABILITY_TRACES_TRANSACTION_DOCUMENT_PROFILE_ID = - 'observability-traces-transaction-document-profile'; - -export const createObservabilityTracesTransactionDocumentProfileProvider = ({ - tracesContextService, - apmErrorsContextService, - logsContextService, -}: ProfileProviderServices): DocumentProfileProvider => ({ - profileId: OBSERVABILITY_TRACES_TRANSACTION_DOCUMENT_PROFILE_ID, - restrictedToProductFeature: TRACES_PRODUCT_FEATURE_ID, - profile: { - getDocViewer: createGetDocViewer({ - apm: { - errors: apmErrorsContextService.getErrorsIndexPattern(), - traces: tracesContextService.getAllTracesIndexPattern(), - }, - logs: logsContextService.getAllLogsIndexPattern() ?? '', - }), - }, - resolve: ({ record, rootContext }) => { - const isObservabilitySolutionView = rootContext.solutionType === SolutionType.Observability; - - if (!isObservabilitySolutionView) { - return { isMatch: false }; - } - - const isTransactionRecord = getIsTransactionRecord({ - record, - }); - - if (!isTransactionRecord) { - return { isMatch: false }; - } - - return { - isMatch: true, - context: { - type: DocumentType.Transaction, - }, - }; - }, -}); - -const getIsTransactionRecord = ({ record }: { record: DataTableRecord }) => { - return isTransactionDocument(record); -}; - -const isTransactionDocument = (record: DataTableRecord) => { - const dataStreamType = getFieldValue(record, DATASTREAM_TYPE_FIELD); - const processorEvent = getFieldValue(record, PROCESSOR_EVENT_FIELD); - return dataStreamType === 'traces' && processorEvent === 'transaction'; -}; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/index.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/index.tsx index b56804e98f98c..2d46f6840169d 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/index.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/index.tsx @@ -24,7 +24,7 @@ import { i18n } from '@kbn/i18n'; import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; import { getUnifiedDocViewerServices } from '../../../../../plugin'; import { SpanFlyout } from './span_flyout'; -import { useRootTransactionContext } from '../../doc_viewer_transaction_overview/hooks/use_root_transaction'; +import { useRootTransactionContext } from '../../doc_viewer_overview/hooks/use_root_transaction'; import { useDataSourcesContext } from '../../hooks/use_data_sources'; import { ExitFullScreenButton } from './exit_full_screen_button'; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/span_flyout/span_flyout_body.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/span_flyout/span_flyout_body.tsx index 2574cba1b7958..0b865df97a0af 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/span_flyout/span_flyout_body.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/span_flyout/span_flyout_body.tsx @@ -12,12 +12,10 @@ import type { DataTableRecord } from '@kbn/discover-utils'; import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; -import SpanOverview from '../../../doc_viewer_span_overview'; -import TransactionOverview from '../../../doc_viewer_transaction_overview'; +import Overview from '../../../doc_viewer_overview'; import DocViewerTable from '../../../../../doc_viewer_table'; import DocViewerSource from '../../../../../doc_viewer_source'; import { useDataSourcesContext } from '../../../hooks/use_data_sources'; -import { isSpanHit } from '../helpers/is_span'; const tabIds = { OVERVIEW: 'unifiedDocViewerTracesSpanFlyoutOverview', @@ -59,7 +57,6 @@ export interface SpanFlyoutProps { export const SpanFlyoutBody = ({ hit, loading, dataView }: SpanFlyoutProps) => { const [selectedTabId, setSelectedTabId] = useState(tabIds.OVERVIEW); - const isSpan = isSpanHit(hit); const { indexes } = useDataSourcesContext(); const onSelectedTabChanged = (id: string) => setSelectedTabId(id); @@ -85,23 +82,13 @@ export const SpanFlyoutBody = ({ hit, loading, dataView }: SpanFlyoutProps) => { {selectedTabId === tabIds.OVERVIEW && ( - {isSpan ? ( - - ) : ( - - )} + )} diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx index c26a8613e3e2a..74c17a90ddbcc 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx @@ -17,8 +17,8 @@ import { getUnifiedDocViewerServices } from '../../../../plugin'; const SERVICE_OVERVIEW_LOCATOR_ID = 'serviceOverviewLocator'; interface ServiceNameLinkProps { - serviceName: string; - agentName: string; + serviceName?: string; + agentName?: string; formattedServiceName: React.ReactNode; } @@ -43,11 +43,13 @@ export function ServiceNameLink({ rangeTo: string; }>(SERVICE_OVERVIEW_LOCATOR_ID); - const href = apmLinkToServiceEntityLocator?.getRedirectUrl({ - serviceName, - rangeFrom: timeRangeFrom, - rangeTo: timeRangeTo, - }); + const href = + serviceName && + apmLinkToServiceEntityLocator?.getRedirectUrl({ + serviceName, + rangeFrom: timeRangeFrom, + rangeTo: timeRangeTo, + }); const routeLinkProps = href ? getRouterLinkProps({ diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/trace.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/trace.tsx index 9f1f3c1d88606..ef6373ee850a4 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/trace.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/trace.tsx @@ -13,10 +13,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle, EuiButtonEmpty } from ' import { i18n } from '@kbn/i18n'; import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; import type { DataViewField } from '@kbn/data-views-plugin/common'; -import { spanTraceFields } from '../doc_viewer_span_overview/resources/fields'; -import { transactionTraceFields } from '../doc_viewer_transaction_overview/resources/fields'; -import { SpanSummaryField } from '../doc_viewer_span_overview/sub_components/span_summary_field'; -import { TransactionSummaryField } from '../doc_viewer_transaction_overview/sub_components/transaction_summary_field'; +import { transactionTraceFields, traceFields } from '../doc_viewer_overview/resources/fields'; +import { SummaryField } from '../doc_viewer_overview/sub_components/summary_field'; import { getUnifiedDocViewerServices } from '../../../../plugin'; import type { FieldConfiguration } from '../resources/get_field_configuration'; import { FullScreenWaterfall } from './full_screen_waterfall'; @@ -72,8 +70,8 @@ export const Trace = ({ const fieldRows = displayType === 'span' - ? spanTraceFields.map((fieldId: string) => ( - ( + )) : transactionTraceFields.map((fieldId: string) => ( - React.ReactNode; + serviceName?: string; + transactionName?: string; + renderContent?: (name?: string) => React.ReactNode; } export function TransactionNameLink({ @@ -41,12 +41,15 @@ export function TransactionNameLink({ rangeTo: string; }>(TRANSACTION_DETAILS_BY_NAME_LOCATOR); - const href = apmLinkToTransactionByNameLocator?.getRedirectUrl({ - serviceName, - transactionName, - rangeFrom: timeRangeFrom, - rangeTo: timeRangeTo, - }); + const href = + serviceName && + transactionName && + apmLinkToTransactionByNameLocator?.getRedirectUrl({ + serviceName, + transactionName, + rangeFrom: timeRangeFrom, + rangeTo: timeRangeTo, + }); const routeLinkProps = href ? getRouterLinkProps({ href, diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_span_latency_chart/use_span_latency_chart.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.test.tsx similarity index 88% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_span_latency_chart/use_span_latency_chart.test.tsx rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.test.tsx index a266607ceda76..15d61af07ce1e 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_span_latency_chart/use_span_latency_chart.test.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.test.tsx @@ -8,11 +8,11 @@ */ import { renderHook, waitFor } from '@testing-library/react'; -import { useSpanLatencyChart } from '.'; -import { getUnifiedDocViewerServices } from '../../../../../../plugin'; +import { useLatencyChart } from './use_latency_chart'; +import { getUnifiedDocViewerServices } from '../../../../../plugin'; import type { EuiThemeComputed } from '@elastic/eui'; -jest.mock('../../../../../../plugin', () => ({ +jest.mock('../../../../../plugin', () => ({ getUnifiedDocViewerServices: jest.fn(), })); @@ -78,14 +78,14 @@ describe('useSpanLatencyChart', () => { percentileThresholdValue: 456, }); - const { result } = renderHook(() => useSpanLatencyChart(params)); + const { result } = renderHook(() => useLatencyChart(params)); await waitFor(() => !result.current.loading); expect(result.current.loading).toBe(false); expect(result.current.hasError).toBe(false); expect(result.current.data).toBeDefined(); - expect(result.current.data?.spanDistributionChartData).toHaveLength(1); + expect(result.current.data?.distributionChartData).toHaveLength(1); expect(result.current.data?.percentileThresholdValue).toBe(456); }); }); @@ -93,7 +93,7 @@ describe('useSpanLatencyChart', () => { describe('when parameters are missing', () => { it('should return null data and stop loading', async () => { const { result } = renderHook(() => - useSpanLatencyChart({ + useLatencyChart({ spanName: '', serviceName: '', }) @@ -112,7 +112,7 @@ describe('useSpanLatencyChart', () => { it('should handle error and show toast', async () => { mockHttpPost.mockRejectedValue(new Error('Fetch error')); - const { result } = renderHook(() => useSpanLatencyChart(params)); + const { result } = renderHook(() => useLatencyChart(params)); await waitFor(() => !result.current.loading); diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_transaction_latency_chart/index.ts b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.tsx similarity index 50% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_transaction_latency_chart/index.ts rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.tsx index 878f19e95c8aa..4b835e526cc4f 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_transaction_latency_chart/index.ts +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.tsx @@ -13,10 +13,10 @@ import { useEffect } from 'react'; import type { EuiThemeComputed } from '@elastic/eui'; import { useEuiTheme } from '@elastic/eui'; +import type { HistogramItem } from '@kbn/apm-types-shared'; import type { DurationDistributionChartData } from '@kbn/apm-ui-shared'; import { useAbortableAsync } from '@kbn/react-hooks'; -import type { HistogramItem } from '@kbn/apm-types-shared'; -import { getUnifiedDocViewerServices } from '../../../../../../plugin'; +import { getUnifiedDocViewerServices } from '../../../../../plugin'; interface GetTransactionDistributionChartDataParams { euiTheme: EuiThemeComputed; @@ -82,48 +82,133 @@ const getTransactionLatencyChart = ({ }); }; -interface TransactionLatencyChartData { - transactionDistributionChartData: DurationDistributionChartData[]; +export interface LatencyChartData { + distributionChartData: DurationDistributionChartData[]; percentileThresholdValue?: number; } -interface UseTransactionLatencyChartParams { - transactionName: string; - transactionType: string; +interface GetSpanDistributionChartDataParams { + euiTheme: EuiThemeComputed; + spanHistogram?: HistogramItem[]; +} + +export const getSpanDistributionChartData = ({ + euiTheme, + spanHistogram, +}: GetSpanDistributionChartDataParams): DurationDistributionChartData[] => + Array.isArray(spanHistogram) + ? [ + { + id: i18n.translate( + 'unifiedDocViewer.observability.traces.useSpanLatencyChart.allSpansLabel', + { + defaultMessage: 'All spans', + } + ), + histogram: spanHistogram, + areaSeriesColor: euiTheme.colors.vis.euiColorVis1, + }, + ] + : []; + +interface GetSpanLatencyChartParams { + core: CoreStart; + signal: AbortSignal; + spanName: string; serviceName: string; + isOtelSpan: boolean; } -export const useTransactionLatencyChart = ({ +const getSpanLatencyChart = ({ + core, + signal, + spanName, + serviceName, + isOtelSpan, +}: GetSpanLatencyChartParams): Promise<{ + overallHistogram?: HistogramItem[]; + percentileThresholdValue?: number; +}> => { + const { data } = getUnifiedDocViewerServices(); + const timeFilter = data.query.timefilter.timefilter.getAbsoluteTime(); + + return core.http.post('/internal/apm/latency/overall_distribution/spans', { + body: JSON.stringify({ + spanName, + serviceName, + chartType: 'spanLatency', + isOtel: isOtelSpan, + end: timeFilter.to, + environment: 'ENVIRONMENT_ALL', + kuery: '', + percentileThreshold: 95, + start: timeFilter.from, + }), + signal, + }); +}; + +interface UseLatencyChartParams { + spanName?: string; + serviceName?: string; + transactionName?: string; + transactionType?: string; + isOtelSpan?: boolean; +} + +export const useLatencyChart = ({ + spanName, + serviceName, transactionName, transactionType, - serviceName, -}: UseTransactionLatencyChartParams) => { + isOtelSpan = false, +}: UseLatencyChartParams) => { const { core } = getUnifiedDocViewerServices(); const { euiTheme } = useEuiTheme(); - const { loading, value, error } = useAbortableAsync( + const { loading, value, error } = useAbortableAsync( async ({ signal }) => { - if (!transactionName || !transactionType || !serviceName) { - return null; + if (!serviceName) { + return undefined; } - const result = await getTransactionLatencyChart({ - core, - signal, - transactionName, - transactionType, - serviceName, - }); + if (transactionName && transactionType) { + const result = await getTransactionLatencyChart({ + core, + signal, + transactionName, + transactionType, + serviceName, + }); + + return { + distributionChartData: getTransactionDistributionChartData({ + euiTheme, + transactionHistogram: result.overallHistogram, + }), + percentileThresholdValue: result.percentileThresholdValue, + }; + } - return { - transactionDistributionChartData: getTransactionDistributionChartData({ - euiTheme, - transactionHistogram: result.overallHistogram, - }), - percentileThresholdValue: result.percentileThresholdValue, - }; + if (spanName) { + const result = await getSpanLatencyChart({ + core, + signal, + spanName, + serviceName, + isOtelSpan, + }); + + return { + distributionChartData: getSpanDistributionChartData({ + euiTheme, + spanHistogram: result.overallHistogram, + }), + percentileThresholdValue: result.percentileThresholdValue, + }; + } }, - [core, euiTheme, serviceName, transactionName, transactionType] + [core, euiTheme, serviceName, spanName] ); useEffect(() => { @@ -140,5 +225,9 @@ export const useTransactionLatencyChart = ({ } }, [error, core]); - return { loading, hasError: !!error, data: value as TransactionLatencyChartData | null }; + return { + loading, + hasError: !!error, + data: value, + }; }; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_root_span/use_root_span.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.test.tsx similarity index 96% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_root_span/use_root_span.test.tsx rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.test.tsx index e09aec06947a5..a67146555be95 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_root_span/use_root_span.test.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.test.tsx @@ -10,11 +10,11 @@ import React from 'react'; import { renderHook, waitFor } from '@testing-library/react'; import { lastValueFrom } from 'rxjs'; -import { getUnifiedDocViewerServices } from '../../../../../../plugin'; -import { RootSpanProvider, useRootSpanContext } from '.'; +import { getUnifiedDocViewerServices } from '../../../../../plugin'; +import { RootSpanProvider, useRootSpanContext } from './use_root_span'; import { TRANSACTION_DURATION_FIELD, TRANSACTION_NAME_FIELD } from '@kbn/discover-utils'; -jest.mock('../../../../../../plugin', () => ({ +jest.mock('../../../../../plugin', () => ({ getUnifiedDocViewerServices: jest.fn(), })); diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_root_span/index.ts b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.ts similarity index 98% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_root_span/index.ts rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.ts index d81fe8eb6dcb8..6f9af07e9b6cd 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_root_span/index.ts +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.ts @@ -23,7 +23,7 @@ import { TRANSACTION_ID_FIELD, TRANSACTION_NAME_FIELD, } from '@kbn/discover-utils'; -import { getUnifiedDocViewerServices } from '../../../../../../plugin'; +import { getUnifiedDocViewerServices } from '../../../../../plugin'; interface UseTransactionPrams { traceId?: string; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_root_transaction/use_root_transaction.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.test.tsx similarity index 96% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_root_transaction/use_root_transaction.test.tsx rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.test.tsx index 6478e0b1d801a..514225e5a0fd9 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_root_transaction/use_root_transaction.test.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.test.tsx @@ -10,8 +10,8 @@ import React from 'react'; import { renderHook, waitFor } from '@testing-library/react'; import { lastValueFrom } from 'rxjs'; -import { getUnifiedDocViewerServices } from '../../../../../../plugin'; -import { RootTransactionProvider, useRootTransactionContext } from '.'; +import { getUnifiedDocViewerServices } from '../../../../../plugin'; +import { RootTransactionProvider, useRootTransactionContext } from './use_root_transaction'; import { SERVICE_NAME_FIELD, SPAN_ID_FIELD, @@ -20,7 +20,7 @@ import { TRANSACTION_NAME_FIELD, } from '@kbn/discover-utils'; -jest.mock('../../../../../../plugin', () => ({ +jest.mock('../../../../../plugin', () => ({ getUnifiedDocViewerServices: jest.fn(), })); diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_root_transaction/index.ts b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.ts similarity index 98% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_root_transaction/index.ts rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.ts index efb16abad8336..fd48b60e848e8 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_root_transaction/index.ts +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.ts @@ -20,7 +20,7 @@ import { } from '@kbn/discover-utils'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { lastValueFrom } from 'rxjs'; -import { getUnifiedDocViewerServices } from '../../../../../../plugin'; +import { getUnifiedDocViewerServices } from '../../../../../plugin'; interface UseRootTransactionParams { traceId: string; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/index.ts b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/index.ts similarity index 87% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/index.ts rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/index.ts index b21d64c34471d..741b806df2e6b 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/index.ts +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/index.ts @@ -7,8 +7,8 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { SpanOverview } from './span_overview'; +import { Overview } from './overview'; // Required for usage in React.lazy // eslint-disable-next-line import/no-default-export -export default SpanOverview; +export default Overview; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/lazy_doc_viewer_obs_traces_span_overview.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/lazy_doc_viewer_obs_traces_overview.tsx similarity index 88% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/lazy_doc_viewer_obs_traces_span_overview.tsx rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/lazy_doc_viewer_obs_traces_overview.tsx index 73119adbb2b02..351eb85dcae00 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/lazy_doc_viewer_obs_traces_span_overview.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/lazy_doc_viewer_obs_traces_overview.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { EuiDelayRender, EuiSkeletonText } from '@elastic/eui'; import { dynamic } from '@kbn/shared-ux-utility'; -export const UnifiedDocViewerObservabilityTracesSpanOverview = dynamic(() => import('.'), { +export const UnifiedDocViewerObservabilityTracesOverview = dynamic(() => import('.'), { fallback: ( diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/span_overview.stories.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.stories.tsx similarity index 88% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/span_overview.stories.tsx rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.stories.tsx index f90dd3c0abaf8..cfde0fa338e90 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/span_overview.stories.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.stories.tsx @@ -12,13 +12,13 @@ import type { UnifiedDocViewerStorybookArgs } from '../../../../../.storybook/pr import minimalAPMFixture from '../../../../__fixtures__/span_apm_minimal.json'; import minimalOtelFixture from '../../../../__fixtures__/span_otel_minimal.json'; import redisClientOtelFixture from '../../../../__fixtures__/span_otel_redis_client.json'; -import { SpanOverview, type SpanOverviewProps } from './span_overview'; +import { Overview, type OverviewProps } from './overview'; -type Args = UnifiedDocViewerStorybookArgs; +type Args = UnifiedDocViewerStorybookArgs; const meta = { title: 'Span overview', - component: SpanOverview, -} satisfies Meta; + component: Overview, +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/span_overview.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx similarity index 65% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/span_overview.tsx rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx index 3a4bb6716408b..a3ebec3e6a06c 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/span_overview.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx @@ -15,10 +15,13 @@ import { SPAN_ID_FIELD, SPAN_NAME_FIELD, TRACE_ID_FIELD, + TRANSACTION_DURATION_FIELD, TRANSACTION_ID_FIELD, - getSpanDocumentOverview, + getTraceDocumentOverview, + getFlattenedTraceDocumentOverview, + TRANSACTION_NAME_FIELD, + TRANSACTION_TYPE_FIELD, } from '@kbn/discover-utils'; -import { getFlattenedSpanDocumentOverview } from '@kbn/discover-utils/src'; import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; import React, { useMemo, useState } from 'react'; import { css } from '@emotion/react'; @@ -27,19 +30,19 @@ import { useDataViewFields } from '../../../../hooks/use_data_view_fields'; import { getUnifiedDocViewerServices } from '../../../../plugin'; import { Trace } from '../components/trace'; import { RootSpanProvider } from './hooks/use_root_span'; -import { spanFields, allSpanFields } from './resources/fields'; -import { getSpanFieldConfiguration } from './resources/get_span_field_configuration'; -import { SpanDurationSummary } from './sub_components/span_duration_summary'; -import { SpanSummaryField } from './sub_components/span_summary_field'; -import { SpanSummaryTitle } from './sub_components/span_summary_title'; -import { RootTransactionProvider } from '../doc_viewer_transaction_overview/hooks/use_root_transaction'; +import { fields, allFields } from './resources/fields'; +import { getFieldConfiguration } from './resources/get_field_configuration'; +import { DurationSummary } from './sub_components/duration_summary'; +import { SummaryField } from './sub_components/summary_field'; +import { SummaryTitle } from './sub_components/summary_title'; +import { RootTransactionProvider } from './hooks/use_root_transaction'; import { DataSourcesProvider } from '../hooks/use_data_sources'; import { getTabContentAvailableHeight, DEFAULT_MARGIN_BOTTOM, } from '../../../doc_viewer_source/get_height'; -export type SpanOverviewProps = DocViewRenderProps & { +export type OverviewProps = DocViewRenderProps & { indexes: { apm: { traces: string; @@ -51,7 +54,7 @@ export type SpanOverviewProps = DocViewRenderProps & { showActions?: boolean; }; -export function SpanOverview({ +export function Overview({ columns, hit, filter, @@ -63,31 +66,36 @@ export function SpanOverview({ dataView, columnsMeta, decreaseAvailableHeightBy = DEFAULT_MARGIN_BOTTOM, -}: SpanOverviewProps) { +}: OverviewProps) { const [containerRef, setContainerRef] = useState(null); const { fieldFormats } = getUnifiedDocViewerServices(); const { formattedDoc, flattenedDoc } = useMemo( () => ({ - formattedDoc: getSpanDocumentOverview(hit, { dataView, fieldFormats }), - flattenedDoc: getFlattenedSpanDocumentOverview(hit), + formattedDoc: getTraceDocumentOverview(hit, { dataView, fieldFormats }), + flattenedDoc: getFlattenedTraceDocumentOverview(hit), }), [dataView, fieldFormats, hit] ); - const { dataViewFields } = useDataViewFields({ fields: allSpanFields, dataView, columnsMeta }); + const { dataViewFields } = useDataViewFields({ fields: allFields, dataView, columnsMeta }); const fieldConfigurations = useMemo( - () => getSpanFieldConfiguration({ attributes: formattedDoc, flattenedDoc }), + () => getFieldConfiguration({ attributes: formattedDoc, flattenedDoc }), [formattedDoc, flattenedDoc] ); - const isOtelSpan = - flattenedDoc[SPAN_DURATION_FIELD] == null && flattenedDoc[OTEL_DURATION] != null; + const id = flattenedDoc[TRANSACTION_ID_FIELD] || flattenedDoc[SPAN_ID_FIELD]!; + const formattedId = formattedDoc[TRANSACTION_ID_FIELD] || formattedDoc[SPAN_ID_FIELD]; + const formattedName = formattedDoc[TRANSACTION_NAME_FIELD] || formattedDoc[SPAN_NAME_FIELD]; - const spanDuration = isOtelSpan - ? flattenedDoc[OTEL_DURATION]! * 0.001 - : flattenedDoc[SPAN_DURATION_FIELD]; + const displayType = formattedDoc[TRANSACTION_NAME_FIELD] ? 'transaction' : 'span'; + + const apmDurationField = + flattenedDoc[TRANSACTION_DURATION_FIELD] ?? flattenedDoc[SPAN_DURATION_FIELD]; + + const isOtelSpan = apmDurationField == null && flattenedDoc[OTEL_DURATION] != null; + + const duration = isOtelSpan ? flattenedDoc[OTEL_DURATION]! * 0.001 : apmDurationField; const traceId = flattenedDoc[TRACE_ID_FIELD]; - const transactionId = flattenedDoc[TRANSACTION_ID_FIELD]; const containerHeight = containerRef ? getTabContentAvailableHeight(containerRef, decreaseAvailableHeightBy) @@ -98,7 +106,7 @@ export function SpanOverview({ - - {spanFields.map((fieldId) => ( - ( + - {spanDuration && ( + {duration && ( - @@ -158,9 +170,9 @@ export function SpanOverview({ => { return { ...getCommonFieldConfiguration({ attributes, flattenedDoc }), @@ -98,5 +100,28 @@ export const getSpanFieldConfiguration = ({ value: flattenedDoc[SPAN_SUBTYPE_FIELD], formattedValue: attributes[SPAN_SUBTYPE_FIELD], }, + [USER_AGENT_NAME_FIELD]: { + title: i18n.translate('unifiedDocViewer.observability.traces.details.userAgent.title', { + defaultMessage: 'User agent', + }), + content: (value, formattedValue) => ( + + ), + value: flattenedDoc[USER_AGENT_NAME_FIELD], + formattedValue: attributes[USER_AGENT_NAME_FIELD], + }, + [USER_AGENT_VERSION_FIELD]: { + title: i18n.translate( + 'unifiedDocViewer.observability.traces.details.userAgentVersion.title', + { + defaultMessage: 'User agent version', + } + ), + content: (value, formattedValue) => ( + + ), + value: flattenedDoc[USER_AGENT_VERSION_FIELD], + formattedValue: attributes[USER_AGENT_VERSION_FIELD], + }, }; }; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/dependency_name_link.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/dependency_name_link.tsx similarity index 91% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/dependency_name_link.tsx rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/dependency_name_link.tsx index f5557bf7b4db3..741522eb78475 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/dependency_name_link.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/dependency_name_link.tsx @@ -18,7 +18,7 @@ interface DependencyNameLinkProps { dependencyName: string; spanType?: string; spanSubtype?: string; - environment: string; + environment?: string; formattedDependencyName?: React.ReactNode; } @@ -46,12 +46,14 @@ export function DependencyNameLink({ rangeTo: string; }>(DEPENDENCY_OVERVIEW_LOCATOR_ID); - const href = apmLinkToDependencyOverviewLocator?.getRedirectUrl({ - dependencyName, - environment, - rangeFrom: timeRangeFrom, - rangeTo: timeRangeTo, - }); + const href = + environment && + apmLinkToDependencyOverviewLocator?.getRedirectUrl({ + dependencyName, + environment, + rangeFrom: timeRangeFrom, + rangeTo: timeRangeTo, + }); const routeLinkProps = href ? getRouterLinkProps({ diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/span_duration_summary/index.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/duration_summary.tsx similarity index 80% rename from src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/span_duration_summary/index.tsx rename to src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/duration_summary.tsx index a1f1444323f57..3074ab075ebde 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/span_duration_summary/index.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/duration_summary.tsx @@ -12,32 +12,38 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiText, EuiTitle } from import { i18n } from '@kbn/i18n'; import { Duration, DurationDistributionChart } from '@kbn/apm-ui-shared'; import { ProcessorEvent } from '@kbn/apm-types-shared'; -import { useRootSpanContext } from '../../hooks/use_root_span'; -import { useSpanLatencyChart } from '../../hooks/use_span_latency_chart'; -import { FieldWithoutActions } from '../../../components/field_without_actions'; -import { Section } from '../../../components/section'; +import { useRootSpanContext } from '../hooks/use_root_span'; +import { useLatencyChart } from '../hooks/use_latency_chart'; +import { FieldWithoutActions } from '../../components/field_without_actions'; +import { Section } from '../../components/section'; -export interface SpanDurationSummaryProps { - spanDuration: number; - spanName: string; - serviceName: string; +export interface DurationSummaryProps { + duration: number; + spanName?: string; + serviceName?: string; + transactionName?: string; + transactionType?: string; isOtelSpan: boolean; } -export function SpanDurationSummary({ - spanDuration, +export function DurationSummary({ + duration, spanName, serviceName, + transactionName, + transactionType, isOtelSpan, -}: SpanDurationSummaryProps) { +}: DurationSummaryProps) { const { trace, loading } = useRootSpanContext(); const { data: latencyChartData, loading: latencyChartLoading, hasError: latencyChartHasError, - } = useSpanLatencyChart({ + } = useLatencyChart({ spanName, serviceName, + transactionName, + transactionType, isOtelSpan, }); @@ -67,7 +73,7 @@ export function SpanDurationSummary({ > @@ -90,9 +96,9 @@ export function SpanDurationSummary({ {(latencyChartLoading || latencyChartData) && ( { }); it('renders spanName with formattedSpanName and id with formattedId', () => { const { getByText, container } = render( - ); @@ -56,7 +56,7 @@ describe('SpanSummaryTitle', () => { it('renders only id with formattedId when spanName is not provided', () => { const { getByText, container } = render( - + ); expect(getByText('123')).toBeInTheDocument(); @@ -65,11 +65,11 @@ describe('SpanSummaryTitle', () => { it('renders FieldHoverActionPopover for spanName and id', () => { const { getByText } = render( - ); @@ -78,9 +78,7 @@ describe('SpanSummaryTitle', () => { }); it('renders FieldHoverActionPopover if showActions is undefined', () => { - const { container } = render( - - ); + const { container } = render(); expect( container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) @@ -89,7 +87,7 @@ describe('SpanSummaryTitle', () => { it('renders FieldHoverActionPopover if showActions is true', () => { const { container } = render( - + ); expect( @@ -99,7 +97,7 @@ describe('SpanSummaryTitle', () => { it('does not render FieldHoverActionPopover if showActions is false', () => { const { container } = render( - + ); expect( diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx new file mode 100644 index 0000000000000..98f1312c1e5b1 --- /dev/null +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { EuiTitle } from '@elastic/eui'; +import { + SERVICE_NAME_FIELD, + SPAN_ID_FIELD, + SPAN_NAME_FIELD, + TRANSACTION_ID_FIELD, + TRANSACTION_NAME_FIELD, +} from '@kbn/discover-utils'; +import React from 'react'; +import { FieldHoverActionPopover } from '../../components/field_with_actions/field_hover_popover_action'; +import { HighlightField } from '../../components/highlight_field'; +import { TransactionNameLink } from '../../components/transaction_name_link'; + +export interface SummaryTitleProps { + spanName?: string; + transactionName?: string; + formattedName?: string; + serviceName?: string; + id?: string; + formattedId?: string; + showActions?: boolean; +} + +const FieldContent = ({ + children, + field, + title, + value, + showActions, +}: { + children: React.ReactNode; + field: string; + title: string; + value: string; + showActions: boolean; +}) => { + return showActions ? ( + + <>{children} + + ) : ( + <>{children} + ); +}; + +export const SummaryTitle = ({ + spanName, + transactionName, + serviceName, + id, + formattedId, + formattedName, + showActions = true, +}: SummaryTitleProps) => { + const name = transactionName || spanName; + + const idField = transactionName ? TRANSACTION_ID_FIELD : SPAN_ID_FIELD; + const nameField = transactionName ? TRANSACTION_NAME_FIELD : SPAN_NAME_FIELD; + + let nameContent; + let idContent; + + if (name) { + nameContent = ( + <> + + +

+ + {transactionName && serviceName + ? ({ content }) => ( + content} + /> + ) + : undefined} + +

+
+
+ + ); + } else if (serviceName) { + nameContent = ( + + {serviceName} + + ); + } + + if (id) { + idContent = ( + + + + ); + } + + return ( + <> + {nameContent} + {idContent} + + ); +}; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_span_latency_chart/index.ts b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_span_latency_chart/index.ts deleted file mode 100644 index 02d29d77c6a7f..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/hooks/use_span_latency_chart/index.ts +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreStart } from '@kbn/core/public'; -import { i18n } from '@kbn/i18n'; -import { useEffect } from 'react'; - -import type { EuiThemeComputed } from '@elastic/eui'; -import { useEuiTheme } from '@elastic/eui'; -import type { HistogramItem } from '@kbn/apm-types-shared'; -import type { DurationDistributionChartData } from '@kbn/apm-ui-shared'; -import { useAbortableAsync } from '@kbn/react-hooks'; -import { getUnifiedDocViewerServices } from '../../../../../../plugin'; - -interface GetSpanDistributionChartDataParams { - euiTheme: EuiThemeComputed; - spanHistogram?: HistogramItem[]; -} - -export const getSpanDistributionChartData = ({ - euiTheme, - spanHistogram, -}: GetSpanDistributionChartDataParams): DurationDistributionChartData[] => - Array.isArray(spanHistogram) - ? [ - { - id: i18n.translate( - 'unifiedDocViewer.observability.traces.useSpanLatencyChart.allSpansLabel', - { - defaultMessage: 'All spans', - } - ), - histogram: spanHistogram, - areaSeriesColor: euiTheme.colors.vis.euiColorVis1, - }, - ] - : []; - -interface GetLatencyChartParams { - core: CoreStart; - signal: AbortSignal; - spanName: string; - serviceName: string; - isOtelSpan: boolean; -} - -const getSpanLatencyChart = ({ - core, - signal, - spanName, - serviceName, - isOtelSpan, -}: GetLatencyChartParams): Promise<{ - overallHistogram?: HistogramItem[]; - percentileThresholdValue?: number; -}> => { - const { data } = getUnifiedDocViewerServices(); - const timeFilter = data.query.timefilter.timefilter.getAbsoluteTime(); - - return core.http.post('/internal/apm/latency/overall_distribution/spans', { - body: JSON.stringify({ - spanName, - serviceName, - chartType: 'spanLatency', - isOtel: isOtelSpan, - end: timeFilter.to, - environment: 'ENVIRONMENT_ALL', - kuery: '', - percentileThreshold: 95, - start: timeFilter.from, - }), - signal, - }); -}; - -interface SpanLatencyChartData { - spanDistributionChartData: DurationDistributionChartData[]; - percentileThresholdValue?: number; -} - -interface UseSpanLatencyChartParams { - spanName: string; - serviceName: string; - isOtelSpan?: boolean; -} - -export const useSpanLatencyChart = ({ - spanName, - serviceName, - isOtelSpan = false, -}: UseSpanLatencyChartParams) => { - const { core } = getUnifiedDocViewerServices(); - const { euiTheme } = useEuiTheme(); - - const { loading, value, error } = useAbortableAsync( - async ({ signal }) => { - if (!spanName || !serviceName) { - return null; - } - - const result = await getSpanLatencyChart({ - core, - signal, - spanName, - serviceName, - isOtelSpan, - }); - - return { - spanDistributionChartData: getSpanDistributionChartData({ - euiTheme, - spanHistogram: result.overallHistogram, - }), - percentileThresholdValue: result.percentileThresholdValue, - }; - }, - [core, euiTheme, serviceName, spanName] - ); - - useEffect(() => { - if (error) { - core.notifications.toasts.addDanger({ - title: i18n.translate( - 'unifiedDocViewer.observability.traces.useTransactionLatencyChart.error', - { - defaultMessage: 'An error occurred while fetching the latency histogram', - } - ), - text: error.message, - }); - } - }, [error, core]); - - return { loading, hasError: !!error, data: value as SpanLatencyChartData | null }; -}; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/span_summary_title.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/span_summary_title.tsx deleted file mode 100644 index 40870ee0cc688..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_span_overview/sub_components/span_summary_title.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EuiTitle } from '@elastic/eui'; -import { SPAN_ID_FIELD, SPAN_NAME_FIELD } from '@kbn/discover-utils'; -import React from 'react'; -import { FieldHoverActionPopover } from '../../components/field_with_actions/field_hover_popover_action'; -import { HighlightField } from '../../components/highlight_field'; - -export interface SpanSummaryTitleProps { - spanName?: string; - formattedSpanName?: string; - spanId: string; - formattedSpanId: string; - showActions?: boolean; -} - -export const SpanSummaryTitle = ({ - spanName, - spanId, - formattedSpanId, - formattedSpanName, - showActions = true, -}: SpanSummaryTitleProps) => { - const FieldContent = ({ - children, - field, - title, - value, - }: { - children: React.ReactNode; - field: string; - title: string; - value: string; - showActions: boolean; - }) => { - return showActions ? ( - - <>{children} - - ) : ( - <>{children} - ); - }; - - return spanName ? ( - <> -
- - -

- -

-
-
-
- - - - - ) : ( - - - - - - ); -}; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_transaction_latency_chart/use_transaction_latency_chart.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_transaction_latency_chart/use_transaction_latency_chart.test.tsx deleted file mode 100644 index da400fe0b2759..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/hooks/use_transaction_latency_chart/use_transaction_latency_chart.test.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { renderHook, waitFor } from '@testing-library/react'; -import { useTransactionLatencyChart } from '.'; -import { getUnifiedDocViewerServices } from '../../../../../../plugin'; -import type { EuiThemeComputed } from '@elastic/eui'; - -jest.mock('../../../../../../plugin', () => ({ - getUnifiedDocViewerServices: jest.fn(), -})); - -const mockHttpPost = jest.fn(); -const mockAddDanger = jest.fn(); -const mockTimefilter = { - getAbsoluteTime: () => ({ - from: '2023-01-01T00:00:00.000Z', - to: '2023-01-01T01:00:00.000Z', - }), -}; - -const mockTheme: EuiThemeComputed = { - colors: { - vis: { - euiColorVis1: 'euiColorVis1', - }, - }, -} as EuiThemeComputed; - -(getUnifiedDocViewerServices as jest.Mock).mockReturnValue({ - core: { - http: { - post: mockHttpPost, - }, - notifications: { - toasts: { - addDanger: mockAddDanger, - }, - }, - }, - data: { - query: { - timefilter: { - timefilter: mockTimefilter, - }, - }, - }, -}); - -jest.mock('@elastic/eui', () => { - const originalModule = jest.requireActual('@elastic/eui'); - return { - ...originalModule, - useEuiTheme: () => ({ euiTheme: mockTheme }), - }; -}); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -describe('useTransactionLatencyChart', () => { - const params = { - transactionName: 'test-name', - transactionType: 'test-type', - serviceName: 'test-service', - }; - - describe('when parameters are NOT missing', () => { - it('should fetch and set data successfully', async () => { - mockHttpPost.mockResolvedValue({ - overallHistogram: [{ x: 1, y: 2 }], - percentileThresholdValue: 123, - }); - - const { result } = renderHook(() => useTransactionLatencyChart(params)); - - await waitFor(() => !result.current.loading); - await waitFor(() => !!result.current.data); - - expect(result.current.loading).toBe(false); - expect(result.current.hasError).toBe(false); - expect(result.current.data).toBeDefined(); - expect(result.current.data?.transactionDistributionChartData).toHaveLength(1); - expect(result.current.data?.percentileThresholdValue).toBe(123); - }); - }); - - describe('when parameters are missing', () => { - it('should return null data and stop loading ', async () => { - const { result } = renderHook(() => - useTransactionLatencyChart({ - transactionName: '', - transactionType: '', - serviceName: '', - }) - ); - - await waitFor(() => !result.current.loading); - - expect(result.current.loading).toBe(false); - expect(result.current.hasError).toBe(false); - expect(result.current.data).toBeNull(); - expect(mockHttpPost).not.toHaveBeenCalled(); - }); - }); - - describe('when an error occurs', () => { - it('should handle error and show toast', async () => { - mockHttpPost.mockRejectedValue(new Error('Fetch error')); - - const { result } = renderHook(() => useTransactionLatencyChart(params)); - - await waitFor(() => !result.current.loading); - - expect(result.current.loading).toBe(false); - expect(result.current.hasError).toBe(true); - expect(result.current.data).toBeUndefined(); - expect(mockAddDanger).toHaveBeenCalledWith( - expect.objectContaining({ - title: 'An error occurred while fetching the latency histogram', - text: 'Fetch error', - }) - ); - }); - }); -}); diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/index.ts b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/index.ts deleted file mode 100644 index 73ea789c72cca..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { TransactionOverview } from './transaction_overview'; - -// Required for usage in React.lazy -// eslint-disable-next-line import/no-default-export -export default TransactionOverview; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/lazy_doc_viewer_obs_traces_transaction_overview.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/lazy_doc_viewer_obs_traces_transaction_overview.tsx deleted file mode 100644 index c8237adb1b131..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/lazy_doc_viewer_obs_traces_transaction_overview.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { EuiDelayRender, EuiSkeletonText } from '@elastic/eui'; -import { dynamic } from '@kbn/shared-ux-utility'; - -export const UnifiedDocViewerObservabilityTracesTransactionOverview = dynamic(() => import('.'), { - fallback: ( - - - - ), -}); diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/resources/fields.ts b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/resources/fields.ts deleted file mode 100644 index 6ab4efe7513e5..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/resources/fields.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { - HTTP_RESPONSE_STATUS_CODE_FIELD, - SERVICE_NAME_FIELD, - TIMESTAMP_FIELD, - TRACE_ID_FIELD, - USER_AGENT_NAME_FIELD, - USER_AGENT_VERSION_FIELD, -} from '@kbn/discover-utils'; - -export const transactionFields = [ - SERVICE_NAME_FIELD, - TIMESTAMP_FIELD, - HTTP_RESPONSE_STATUS_CODE_FIELD, - USER_AGENT_NAME_FIELD, - USER_AGENT_VERSION_FIELD, -]; - -export const transactionTraceFields = [TRACE_ID_FIELD]; - -export const allTransactionFields = [...transactionFields, ...transactionTraceFields]; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/resources/get_transaction_field_configuration.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/resources/get_transaction_field_configuration.tsx deleted file mode 100644 index ddd9a61d9a7dc..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/resources/get_transaction_field_configuration.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { TransactionDocumentOverview } from '@kbn/discover-utils'; -import { USER_AGENT_NAME_FIELD, USER_AGENT_VERSION_FIELD } from '@kbn/discover-utils'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import type { FieldConfiguration } from '../../resources/get_field_configuration'; -import { getCommonFieldConfiguration } from '../../resources/get_field_configuration'; -import { HighlightField } from '../../components/highlight_field'; - -export const getTransactionFieldConfiguration = ({ - attributes, - flattenedDoc, -}: { - attributes: TransactionDocumentOverview; - flattenedDoc: TransactionDocumentOverview; -}): Record => { - return { - ...getCommonFieldConfiguration({ attributes, flattenedDoc }), - [USER_AGENT_NAME_FIELD]: { - title: i18n.translate('unifiedDocViewer.observability.traces.details.userAgent.title', { - defaultMessage: 'User agent', - }), - content: (value, formattedValue) => ( - - ), - value: flattenedDoc[USER_AGENT_NAME_FIELD], - formattedValue: attributes[USER_AGENT_NAME_FIELD], - }, - [USER_AGENT_VERSION_FIELD]: { - title: i18n.translate( - 'unifiedDocViewer.observability.traces.details.userAgentVersion.title', - { - defaultMessage: 'User agent version', - } - ), - content: (value, formattedValue) => ( - - ), - value: flattenedDoc[USER_AGENT_VERSION_FIELD], - formattedValue: attributes[USER_AGENT_VERSION_FIELD], - }, - }; -}; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_duration_summary/index.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_duration_summary/index.tsx deleted file mode 100644 index 33560ae1d7b82..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_duration_summary/index.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiText, EuiTitle } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { Duration, DurationDistributionChart } from '@kbn/apm-ui-shared'; -import { ProcessorEvent } from '@kbn/apm-types-shared'; -import { useRootTransactionContext } from '../../hooks/use_root_transaction'; -import { useTransactionLatencyChart } from '../../hooks/use_transaction_latency_chart'; -import { Section } from '../../../components/section'; -import { FieldWithoutActions } from '../../../components/field_without_actions'; - -export interface TransactionDurationSummaryProps { - transactionDuration: number; - transactionName: string; - transactionType: string; - serviceName: string; -} - -export function TransactionDurationSummary({ - transactionDuration, - transactionName, - transactionType, - serviceName, -}: TransactionDurationSummaryProps) { - const { transaction: rootTransaction, loading: rootTransactionLoading } = - useRootTransactionContext(); - - const { - data: latencyChartData, - loading: latencyChartLoading, - hasError: latencyChartHasError, - } = useTransactionLatencyChart({ - transactionName, - transactionType, - serviceName, - }); - - return ( -
- <> - - - - - - - - - - -

- {i18n.translate( - 'unifiedDocViewer.observability.traces.docViewerTransactionOverview.spanDurationSummary.latency.title', - { - defaultMessage: 'Latency', - } - )} -

-
- - {(latencyChartLoading || latencyChartData) && ( - - )} -
-
- -
- ); -} diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_field.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_field.tsx deleted file mode 100644 index 4d65e995aef73..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_field.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EuiHorizontalRule } from '@elastic/eui'; -import React from 'react'; -import type { DataViewField } from '@kbn/data-views-plugin/common'; -import type { FieldConfiguration } from '../../resources/get_field_configuration'; -import { FieldWithActions } from '../../components/field_with_actions/field_with_actions'; - -export interface TransactionSummaryFieldProps { - fieldId: string; - fieldConfiguration: FieldConfiguration; - fieldMapping?: DataViewField; - showActions?: boolean; -} - -export function TransactionSummaryField({ - fieldConfiguration, - fieldId, - fieldMapping, - showActions = true, -}: TransactionSummaryFieldProps) { - if (!fieldConfiguration.value) { - return null; - } - - return ( - <> - -
- {fieldConfiguration.content(fieldConfiguration.value, fieldConfiguration.formattedValue)} -
-
- - - ); -} diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_title.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_title.test.tsx deleted file mode 100644 index 5a6575521f330..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_title.test.tsx +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ -import React from 'react'; -import { render } from '@testing-library/react'; -import { TransactionSummaryTitle } from './transaction_summary_title'; - -const fieldHoverActionPopoverDataTestSubj = 'FieldHoverActionPopover'; - -jest.mock('../../components/field_with_actions/field_hover_popover_action', () => ({ - FieldHoverActionPopover: ({ children }: { children: React.ReactNode }) => ( -
{children}
- ), -})); - -jest.mock('../../components/highlight_field', () => ({ - HighlightField: ({ - value, - formattedValue, - as, - children, - }: { - value?: string; - formattedValue?: string; - as?: keyof JSX.IntrinsicElements; - children?: (props: { content: React.ReactNode }) => React.ReactNode; - }) => { - const Tag = as || 'span'; - const content = ; - return children ? children({ content }) : content; - }, -})); - -jest.mock('../../components/transaction_name_link', () => ({ - TransactionNameLink: ({ renderContent }: { renderContent: () => React.ReactNode }) => ( -
{renderContent()}
- ), -})); - -describe('TransactionSummaryTitle', () => { - afterAll(() => { - jest.clearAllMocks(); - }); - - it('renders transactionName with formattedTransactionName and id with formattedId', () => { - const { getByText, container } = render( - - ); - - expect(container.querySelector('strong')).toHaveTextContent('Test Transaction'); - expect(container.querySelector('strong')?.innerHTML).toBe('Test Transaction'); - - expect(getByText('123')).toBeInTheDocument(); - expect(container.querySelector('span')?.innerHTML).toBe('123'); - }); - - it('renders only serviceName when transactionName is not provided', () => { - const { getByText } = render( - - ); - - expect(getByText('Test Service')).toBeInTheDocument(); - - expect(getByText('123')).toBeInTheDocument(); - }); - - it('renders FieldHoverActionPopover for transactionName and id', () => { - const { getByText } = render( - - ); - - expect(getByText('Test Transaction')).toBeInTheDocument(); - expect(getByText('123')).toBeInTheDocument(); - }); - - it('renders TransactionNameLink with transactionName and formattedTransactionName', () => { - const { getByText, container } = render( - - ); - - expect(getByText('Test Transaction')).toBeInTheDocument(); - expect(container.querySelector('strong')?.innerHTML).toBe('Test Transaction'); - }); - - it('renders id with formattedId when provided', () => { - const { getByText, container } = render( - - ); - - expect(getByText('123')).toBeInTheDocument(); - expect(container.querySelector('span')?.innerHTML).toBe('123'); - }); - - it('renders FieldHoverActionPopover if showActions is undefined', () => { - const { container } = render(); - - expect( - container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) - ).not.toBeNull(); - }); - - it('renders FieldHoverActionPopover if showActions is true', () => { - const { container } = render( - - ); - - expect( - container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) - ).not.toBeNull(); - }); - - it('does not render FieldHoverActionPopover if showActions is false', () => { - const { container } = render( - - ); - - expect( - container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) - ).toBeNull(); - }); -}); diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_title.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_title.tsx deleted file mode 100644 index 09df9675ca866..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/sub_components/transaction_summary_title.tsx +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EuiTitle } from '@elastic/eui'; -import { - SERVICE_NAME_FIELD, - TRANSACTION_ID_FIELD, - TRANSACTION_NAME_FIELD, -} from '@kbn/discover-utils'; -import React from 'react'; -import { FieldHoverActionPopover } from '../../components/field_with_actions/field_hover_popover_action'; -import { HighlightField } from '../../components/highlight_field'; -import { TransactionNameLink } from '../../components/transaction_name_link'; - -export interface TransactionSummaryTitleProps { - serviceName: string; - transactionName?: string; - formattedTransactionName?: string; - id?: string; - formattedId?: string; - showActions?: boolean; -} - -export const TransactionSummaryTitle = ({ - serviceName, - transactionName, - id, - formattedId, - formattedTransactionName, - showActions = true, -}: TransactionSummaryTitleProps) => { - const FieldContent = ({ - children, - field, - title, - value, - }: { - children: React.ReactNode; - field: string; - title: string; - value: string; - showActions: boolean; - }) => { - return showActions ? ( - - <>{children} - - ) : ( - <>{children} - ); - }; - - return ( - <> - -

- {transactionName ? ( - - - {({ content }) => ( - content} - /> - )} - - - ) : ( - - {serviceName} - - )} -

-
- - {id && ( - - - - )} - - ); -}; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/transaction_overview.stories.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/transaction_overview.stories.tsx deleted file mode 100644 index beb8748ef6046..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/transaction_overview.stories.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { Meta, StoryObj } from '@storybook/react'; -import type { UnifiedDocViewerStorybookArgs } from '../../../../../.storybook/preview'; -import httpServerApmFixture from '../../../../__fixtures__/transaction_http_server_apm.json'; -import httpServerOtelFixture from '../../../../__fixtures__/transaction_http_server_otel.json'; -import { TransactionOverview, type TransactionOverviewProps } from './transaction_overview'; - -type Args = UnifiedDocViewerStorybookArgs; -const meta = { - title: 'Transaction overview', - component: TransactionOverview, -} satisfies Meta; -export default meta; - -type Story = StoryObj; - -/** - * APM HTTP transaction - */ -export const ApmHttpServer: Story = { - name: 'APM HTTP server transaction', - args: { - hit: httpServerApmFixture, - }, - tags: ['transaction', 'span', 'http', 'server', 'apm'], -}; - -/** - * OpenTelemetry HTTP server span. - * Processed by the elasticapmprocessor to add APM transaction and span attributes. - */ -export const OtelHttpServer: Story = { - name: 'OpenTelemetry HTTP server transaction', - args: { - hit: httpServerOtelFixture, - }, - tags: ['otel', 'transaction', 'span', 'http', 'server', 'apm'], -}; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/transaction_overview.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/transaction_overview.tsx deleted file mode 100644 index 7cdad4612d6c4..0000000000000 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_transaction_overview/transaction_overview.tsx +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React, { useMemo, useState } from 'react'; -import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { - SERVICE_NAME_FIELD, - TRACE_ID_FIELD, - TRANSACTION_DURATION_FIELD, - TRANSACTION_NAME_FIELD, - TRANSACTION_TYPE_FIELD, - getTransactionDocumentOverview, - TRANSACTION_ID_FIELD, -} from '@kbn/discover-utils'; -import { getFlattenedTransactionDocumentOverview } from '@kbn/discover-utils/src'; -import { css } from '@emotion/react'; -import { useDataViewFields } from '../../../../hooks/use_data_view_fields'; -import { FieldActionsProvider } from '../../../../hooks/use_field_actions'; -import { transactionFields, allTransactionFields } from './resources/fields'; -import { getTransactionFieldConfiguration } from './resources/get_transaction_field_configuration'; -import { TransactionSummaryField } from './sub_components/transaction_summary_field'; -import { TransactionDurationSummary } from './sub_components/transaction_duration_summary'; -import { RootTransactionProvider } from './hooks/use_root_transaction'; -import { Trace } from '../components/trace'; -import { TransactionSummaryTitle } from './sub_components/transaction_summary_title'; -import { getUnifiedDocViewerServices } from '../../../../plugin'; -import { DataSourcesProvider } from '../hooks/use_data_sources'; -import { - DEFAULT_MARGIN_BOTTOM, - getTabContentAvailableHeight, -} from '../../../doc_viewer_source/get_height'; - -export type TransactionOverviewProps = DocViewRenderProps & { - indexes: { - apm: { - traces: string; - errors: string; - }; - logs: string; - }; - showWaterfall?: boolean; - showActions?: boolean; -}; - -export function TransactionOverview({ - columns, - hit, - filter, - onAddColumn, - onRemoveColumn, - indexes, - showWaterfall = true, - showActions = true, - dataView, - columnsMeta, - decreaseAvailableHeightBy = DEFAULT_MARGIN_BOTTOM, -}: TransactionOverviewProps) { - const [containerRef, setContainerRef] = useState(null); - const { fieldFormats } = getUnifiedDocViewerServices(); - const { formattedDoc, flattenedDoc } = useMemo( - () => ({ - formattedDoc: getTransactionDocumentOverview(hit, { dataView, fieldFormats }), - flattenedDoc: getFlattenedTransactionDocumentOverview(hit), - }), - [dataView, fieldFormats, hit] - ); - const { dataViewFields } = useDataViewFields({ - fields: allTransactionFields, - dataView, - columnsMeta, - }); - const transactionDuration = flattenedDoc[TRANSACTION_DURATION_FIELD]; - const fieldConfigurations = useMemo( - () => getTransactionFieldConfiguration({ attributes: formattedDoc, flattenedDoc }), - [formattedDoc, flattenedDoc] - ); - const traceId = flattenedDoc[TRACE_ID_FIELD]; - const transactionId = flattenedDoc[TRANSACTION_ID_FIELD]; - - const containerHeight = containerRef - ? getTabContentAvailableHeight(containerRef, decreaseAvailableHeightBy) - : 0; - - return ( - - - - - - - - - - {transactionFields.map((fieldId) => ( - - ))} - - {transactionDuration !== undefined && ( - - - - )} - - {traceId && transactionId && ( - <> - - - - )} - - - - - - ); -} diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/resources/get_field_configuration.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/resources/get_field_configuration.tsx index 589b51e711469..a0aad190b9d5c 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/resources/get_field_configuration.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/resources/get_field_configuration.tsx @@ -7,7 +7,11 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { SpanDocumentOverview, TransactionDocumentOverview } from '@kbn/discover-utils'; +import type { + SpanDocumentOverview, + TransactionDocumentOverview, + TraceDocumentOverview, +} from '@kbn/discover-utils'; import { SERVICE_NAME_FIELD, TRACE_ID_FIELD, @@ -39,8 +43,8 @@ export const getCommonFieldConfiguration = ({ attributes, flattenedDoc, }: { - attributes: TransactionDocumentOverview | SpanDocumentOverview; - flattenedDoc: TransactionDocumentOverview | SpanDocumentOverview; + attributes: TransactionDocumentOverview | SpanDocumentOverview | TraceDocumentOverview; + flattenedDoc: TransactionDocumentOverview | SpanDocumentOverview | TraceDocumentOverview; }): Record => { return { [TRANSACTION_NAME_FIELD]: { diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/index.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/index.tsx index eb4da3bc9b8da..6bec941968fb5 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/index.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/index.tsx @@ -36,8 +36,7 @@ export type { } from './components/doc_viewer_logs_overview/logs_overview'; export { UnifiedDocViewerLogsOverview } from './components/lazy_doc_viewer_logs_overview'; -export { UnifiedDocViewerObservabilityTracesSpanOverview } from './components/observability/traces/doc_viewer_span_overview/lazy_doc_viewer_obs_traces_span_overview'; -export { UnifiedDocViewerObservabilityTracesTransactionOverview } from './components/observability/traces/doc_viewer_transaction_overview/lazy_doc_viewer_obs_traces_transaction_overview'; +export { UnifiedDocViewerObservabilityTracesOverview } from './components/observability/traces/doc_viewer_overview/lazy_doc_viewer_obs_traces_overview'; export { UnifiedDocViewerObservabilityAttributesOverview } from './components/observability/attributes/doc_viewer_attributes_overview/lazy_doc_viewer_obs_attributes_overview'; export const plugin = () => new UnifiedDocViewerPublicPlugin(); From 4c8b5bd04e8c8da23173f4961dcf224fcb0514f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Tue, 2 Sep 2025 12:48:24 +0200 Subject: [PATCH 02/14] Fix merge conflict mess --- .../traces/doc_viewer_overview/hooks/use_root_span.test.tsx | 2 +- .../doc_viewer_overview/hooks/use_root_transaction.test.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.test.tsx index f4d34e5fd927f..8c277d30b9b39 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.test.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_span.test.tsx @@ -18,7 +18,7 @@ jest.mock('../../../../../plugin', () => ({ getUnifiedDocViewerServices: jest.fn(), })); -jest.mock('../../../hooks/use_data_sources', () => ({ +jest.mock('../../hooks/use_data_sources', () => ({ useDataSourcesContext: () => ({ indexes: { apm: { traces: 'test-index' } }, }), diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.test.tsx index 305c96ef450c2..b12fdc7547ee7 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.test.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_root_transaction.test.tsx @@ -32,7 +32,7 @@ jest.mock('rxjs', () => { }; }); -jest.mock('../../../hooks/use_data_sources', () => ({ +jest.mock('../../hooks/use_data_sources', () => ({ useDataSourcesContext: () => ({ indexes: { apm: { traces: 'test-index' } }, }), From 567e3e17ede7c77c8d3656cd11859e73429ca216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Tue, 2 Sep 2025 13:10:22 +0200 Subject: [PATCH 03/14] Fix summary title tests for spans --- .../sub_components/summary_title.test.tsx | 2 +- .../sub_components/summary_title.tsx | 44 ++++++++++++------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.test.tsx index e1dc023abe46c..524f86064795c 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.test.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.test.tsx @@ -60,7 +60,7 @@ describe('SpanSummaryTitle', () => { ); expect(getByText('123')).toBeInTheDocument(); - expect(container.querySelector('h2')?.innerHTML).toBe('123'); + expect(container.querySelector('h2')?.innerHTML).toBe('123'); }); it('renders FieldHoverActionPopover for spanName and id', () => { diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx index 98f1312c1e5b1..fb03b1f3d0f46 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx @@ -52,6 +52,16 @@ const FieldContent = ({ ); }; +const Title = ({ isTitle, children }: { isTitle: boolean; children: React.ReactNode }) => { + return isTitle ? ( + +

{children}

+
+ ) : ( + children + ); +}; + export const SummaryTitle = ({ spanName, transactionName, @@ -73,21 +83,19 @@ export const SummaryTitle = ({ nameContent = ( <> - -

- - {transactionName && serviceName - ? ({ content }) => ( - content} - /> - ) - : undefined} - -

-
+ + <HighlightField textSize="m" value={name} formattedValue={formattedName} as="strong"> + {transactionName && serviceName + ? ({ content }) => ( + <TransactionNameLink + serviceName={serviceName} + transactionName={transactionName} + renderContent={() => content} + /> + ) + : undefined} + </HighlightField> +
); @@ -99,7 +107,7 @@ export const SummaryTitle = ({ field={SERVICE_NAME_FIELD} showActions={showActions} > - {serviceName} + {serviceName} ); } @@ -107,7 +115,9 @@ export const SummaryTitle = ({ if (id) { idContent = ( - + + <HighlightField value={id} formattedValue={formattedId} /> + ); } From fc9e9851b671fb4712416bf4f0a8ee0808c0723d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Tue, 2 Sep 2025 14:57:46 +0200 Subject: [PATCH 04/14] Fix Similar Spans --- .../traces/components/similar_spans/index.tsx | 8 ++++---- .../components/similar_spans/similar_spans.test.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/index.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/index.tsx index 78f2becf4ff04..310ee5c6b6bfb 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/index.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/index.tsx @@ -13,12 +13,12 @@ import { DurationDistributionChart } from '@kbn/apm-ui-shared'; import { ProcessorEvent } from '@kbn/apm-types-shared'; import { ContentFrameworkChart } from '../../../../content_framework/chart'; import { ContentFrameworkSection } from '../../../../content_framework/section'; -import type { SpanLatencyChartData } from '../../doc_viewer_span_overview/hooks/use_span_latency_chart'; +import type { LatencyChartData } from '../../doc_viewer_overview/hooks/use_latency_chart'; export interface SimilarSpansProps { spanDuration: number; latencyChart: { - data: SpanLatencyChartData | null; // TODO move this interface + data: LatencyChartData | null; // TODO move this interface loading: boolean; hasError: boolean; }; @@ -49,10 +49,10 @@ export function SimilarSpans({ esqlQuery={!latencyChart.hasError && esqlQuery ? esqlQuery : undefined} > { spanDuration: 1200, latencyChart: { data: { - spanDistributionChartData: mockSpanDistributionChartData, + distributionChartData: mockSpanDistributionChartData, percentileThresholdValue: 1000, }, loading: false, @@ -104,7 +104,7 @@ describe('SimilarSpans', () => { {...defaultProps} latencyChart={{ data: { - spanDistributionChartData: [], + distributionChartData: [], }, loading: false, hasError: true, From a2a9d3bd03797672c1f771fcd5df3182bc4d57ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Tue, 2 Sep 2025 15:55:01 +0200 Subject: [PATCH 05/14] Fix and migrate tests and test cases --- .../document_profile_table.test.tsx | 7 +- .../document_profile/profile.test.ts | 108 +++++++- .../document_profile/profile.ts | 12 +- .../profiles/document_profile.ts | 3 +- .../hooks/use_latency_chart.test.tsx | 163 ++++++++---- .../doc_viewer_overview/overview.stories.tsx | 27 +- .../sub_components/summary_title.test.tsx | 237 +++++++++++++----- .../sub_components/summary_title.tsx | 32 ++- 8 files changed, 437 insertions(+), 152 deletions(-) diff --git a/src/platform/plugins/shared/discover/public/context_awareness/inspector/profiles_inspector_view/document_profiles_section/document_profile_table.test.tsx b/src/platform/plugins/shared/discover/public/context_awareness/inspector/profiles_inspector_view/document_profiles_section/document_profile_table.test.tsx index 9516f649881b6..0d36b72ca9210 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/inspector/profiles_inspector_view/document_profiles_section/document_profile_table.test.tsx +++ b/src/platform/plugins/shared/discover/public/context_awareness/inspector/profiles_inspector_view/document_profiles_section/document_profile_table.test.tsx @@ -62,12 +62,12 @@ describe('', () => { getDataTableRecordWithContextMock({ id: 'record2', raw: generateEsHit({ _id: 'some other record' }), - context: getDocumentContextMock({ type: DocumentType.Span }), + context: getDocumentContextMock({ type: DocumentType.Trace }), }), getDataTableRecordWithContextMock({ id: 'record3', raw: generateEsHit({ _id: 'one specific record' }), - context: getDocumentContextMock({ type: DocumentType.Transaction }), + context: getDocumentContextMock({ type: DocumentType.Trace }), }), ]; @@ -87,8 +87,7 @@ describe('', () => { // Then expect(screen.getByText('log')).toBeVisible(); - expect(screen.getByText('span')).toBeVisible(); - expect(screen.getByText('transaction')).toBeVisible(); + expect(screen.getAllByText('trace')).toHaveLength(2); }); describe('when the inspect action is clicked', () => { diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts index 1023d8b6764e5..482dae7d282d4 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts @@ -37,7 +37,7 @@ describe('spanDocumentProfileProvider', () => { const RESOLUTION_MATCH = { isMatch: true, context: { - type: DocumentType.Span, + type: DocumentType.Trace, }, }; const RESOLUTION_MISMATCH = { @@ -59,7 +59,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildMockRecord('index', { + record: buildSpanMockRecord('index', { 'data_stream.type': ['traces'], 'processor.event': ['span'], }), @@ -72,7 +72,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildMockRecord('another-index'), + record: buildSpanMockRecord('another-index'), }) ).toEqual(RESOLUTION_MISMATCH); }); @@ -82,7 +82,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildMockRecord('index', { + record: buildSpanMockRecord('index', { 'data_stream.type': ['traces'], 'processor.event': ['other'], }), @@ -95,7 +95,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildMockRecord('index', { + record: buildSpanMockRecord('index', { 'data_stream.type': ['traces'], kind: 'Internal', }), @@ -108,7 +108,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildMockRecord('index', { + record: buildSpanMockRecord('index', { 'data_stream.type': ['traces'], }), }) @@ -127,7 +127,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId, solutionType }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildMockRecord('index', { + record: buildSpanMockRecord('index', { 'data_stream.type': ['traces'], 'processor.event': ['span'], }), @@ -137,7 +137,99 @@ describe('spanDocumentProfileProvider', () => { }); }); -const buildMockRecord = (index: string, fields: Record = {}) => +const buildSpanMockRecord = (index: string, fields: Record = {}) => + buildDataTableRecord({ + _id: '', + _index: index, + fields: { + _index: index, + ...fields, + }, + }); + +describe('transactionDocumentProfileProvider', () => { + const getRootContext = ({ + profileId, + solutionType, + }: { + profileId: string; + solutionType?: SolutionType; + }): ContextWithProfileId => { + return { + profileId, + solutionType: solutionType ?? SolutionType.Observability, + }; + }; + + const DATA_SOURCE_CONTEXT: ContextWithProfileId = { + profileId: 'traces-transaction-document-profile', + category: DataSourceCategory.Traces, + }; + const RESOLUTION_MATCH = { + isMatch: true, + context: { + type: DocumentType.Trace, + }, + }; + const RESOLUTION_MISMATCH = { + isMatch: false, + }; + + const mockServices: ProfileProviderServices = { + ...createContextAwarenessMocks().profileProviderServices, + }; + + describe('when root profile is observability', () => { + const profileId = OBSERVABILITY_ROOT_PROFILE_ID; + const transactionDocumentProfileProvider = + createObservabilityTracesDocumentProfileProvider(mockServices); + + it('matches records with the correct data stream type and the correct processor event', () => { + expect( + transactionDocumentProfileProvider.resolve({ + rootContext: getRootContext({ profileId }), + dataSourceContext: DATA_SOURCE_CONTEXT, + record: buildSpanMockRecord('index', { + 'data_stream.type': ['traces'], + 'processor.event': ['transaction'], + }), + }) + ).toEqual(RESOLUTION_MATCH); + }); + + it('does not match records with neither characteristic', () => { + expect( + transactionDocumentProfileProvider.resolve({ + rootContext: getRootContext({ profileId }), + dataSourceContext: DATA_SOURCE_CONTEXT, + record: buildSpanMockRecord('another-index'), + }) + ).toEqual(RESOLUTION_MISMATCH); + }); + }); + + describe('when solutionType is NOT observability', () => { + const profileId = OBSERVABILITY_ROOT_PROFILE_ID; + const solutionType = SolutionType.Default; + const transactionDocumentProfileProvider = + createObservabilityTracesDocumentProfileProvider(mockServices); + + it('does not match records with the correct data stream type and the correct processor event', () => { + expect( + transactionDocumentProfileProvider.resolve({ + rootContext: getRootContext({ profileId, solutionType }), + dataSourceContext: DATA_SOURCE_CONTEXT, + record: buildTransactionMockRecord('index', { + 'data_stream.type': ['traces'], + 'processor.event': ['transaction'], + }), + }) + ).toEqual(RESOLUTION_MISMATCH); + }); + }); +}); + +const buildTransactionMockRecord = (index: string, fields: Record = {}) => buildDataTableRecord({ _id: '', _index: index, diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts index 9a7cd01b22376..c7b05cc8e3641 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts @@ -40,28 +40,28 @@ export const createObservabilityTracesDocumentProfileProvider = ({ return { isMatch: false }; } - const isSpanRecord = getIsSpanRecord({ + const isTraceRecord = resolveTraceRecord({ record, }); - if (!isSpanRecord) { + if (!isTraceRecord) { return { isMatch: false }; } return { isMatch: true, context: { - type: DocumentType.Span, + type: DocumentType.Trace, }, }; }, }); -const getIsSpanRecord = ({ record }: { record: DataTableRecord }) => { - return isSpanDocument(record); +const resolveTraceRecord = ({ record }: { record: DataTableRecord }) => { + return isTraceDocument(record); }; -const isSpanDocument = (record: DataTableRecord) => { +const isTraceDocument = (record: DataTableRecord) => { const dataStreamType = getFieldValue(record, DATASTREAM_TYPE_FIELD); const processorEvent = getFieldValue(record, PROCESSOR_EVENT_FIELD); diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profiles/document_profile.ts b/src/platform/plugins/shared/discover/public/context_awareness/profiles/document_profile.ts index 8f7350d863716..488b917930bac 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profiles/document_profile.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profiles/document_profile.ts @@ -19,8 +19,7 @@ import type { DataSourceContext } from './data_source_profile'; */ export enum DocumentType { Log = 'log', - Span = 'span', - Transaction = 'transaction', + Trace = 'trace', Default = 'default', } diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.test.tsx index 15d61af07ce1e..0853238381943 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.test.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/hooks/use_latency_chart.test.tsx @@ -65,66 +65,135 @@ beforeEach(() => { jest.clearAllMocks(); }); -describe('useSpanLatencyChart', () => { - const params = { - spanName: 'test-span', - serviceName: 'test-service', - }; +describe('useLatencyChart', () => { + describe('Spans', () => { + const params = { + spanName: 'test-span', + serviceName: 'test-service', + }; + + describe('when parameters are NOT missing', () => { + it('should fetch and set data successfully', async () => { + mockHttpPost.mockResolvedValue({ + overallHistogram: [{ x: 1, y: 2 }], + percentileThresholdValue: 456, + }); + + const { result } = renderHook(() => useLatencyChart(params)); + + await waitFor(() => !result.current.loading); + + expect(result.current.loading).toBe(false); + expect(result.current.hasError).toBe(false); + expect(result.current.data).toBeDefined(); + expect(result.current.data?.distributionChartData).toHaveLength(1); + expect(result.current.data?.percentileThresholdValue).toBe(456); + }); + }); - describe('when parameters are NOT missing', () => { - it('should fetch and set data successfully', async () => { - mockHttpPost.mockResolvedValue({ - overallHistogram: [{ x: 1, y: 2 }], - percentileThresholdValue: 456, + describe('when parameters are missing', () => { + it('should return null data and stop loading', async () => { + const { result } = renderHook(() => + useLatencyChart({ + spanName: '', + serviceName: '', + }) + ); + + await waitFor(() => !result.current.loading); + + expect(result.current.loading).toBe(false); + expect(result.current.hasError).toBe(false); + expect(result.current.data).toBeUndefined(); + expect(mockHttpPost).not.toHaveBeenCalled(); }); + }); + + describe('when there is an error', () => { + it('should handle error and show toast', async () => { + mockHttpPost.mockRejectedValue(new Error('Fetch error')); - const { result } = renderHook(() => useLatencyChart(params)); + const { result } = renderHook(() => useLatencyChart(params)); - await waitFor(() => !result.current.loading); + await waitFor(() => !result.current.loading); - expect(result.current.loading).toBe(false); - expect(result.current.hasError).toBe(false); - expect(result.current.data).toBeDefined(); - expect(result.current.data?.distributionChartData).toHaveLength(1); - expect(result.current.data?.percentileThresholdValue).toBe(456); + expect(result.current.loading).toBe(false); + expect(result.current.hasError).toBe(true); + expect(result.current.data).toBeUndefined(); + expect(mockAddDanger).toHaveBeenCalledWith( + expect.objectContaining({ + title: 'An error occurred while fetching the latency histogram', + text: 'Fetch error', + }) + ); + }); }); }); - describe('when parameters are missing', () => { - it('should return null data and stop loading', async () => { - const { result } = renderHook(() => - useLatencyChart({ - spanName: '', - serviceName: '', - }) - ); - - await waitFor(() => !result.current.loading); - - expect(result.current.loading).toBe(false); - expect(result.current.hasError).toBe(false); - expect(result.current.data).toBeNull(); - expect(mockHttpPost).not.toHaveBeenCalled(); + describe('Transactions', () => { + const params = { + transactionName: 'test-name', + transactionType: 'test-type', + serviceName: 'test-service', + }; + + describe('when parameters are NOT missing', () => { + it('should fetch and set data successfully', async () => { + mockHttpPost.mockResolvedValue({ + overallHistogram: [{ x: 1, y: 2 }], + percentileThresholdValue: 123, + }); + + const { result } = renderHook(() => useLatencyChart(params)); + + await waitFor(() => !result.current.loading); + await waitFor(() => !!result.current.data); + + expect(result.current.loading).toBe(false); + expect(result.current.hasError).toBe(false); + expect(result.current.data).toBeDefined(); + expect(result.current.data?.distributionChartData).toHaveLength(1); + expect(result.current.data?.percentileThresholdValue).toBe(123); + }); + }); + + describe('when parameters are missing', () => { + it('should return null data and stop loading ', async () => { + const { result } = renderHook(() => + useLatencyChart({ + transactionName: '', + transactionType: '', + serviceName: '', + }) + ); + + await waitFor(() => !result.current.loading); + + expect(result.current.loading).toBe(false); + expect(result.current.hasError).toBe(false); + expect(result.current.data).toBeUndefined(); + expect(mockHttpPost).not.toHaveBeenCalled(); + }); }); - }); - describe('when there is an error', () => { - it('should handle error and show toast', async () => { - mockHttpPost.mockRejectedValue(new Error('Fetch error')); + describe('when an error occurs', () => { + it('should handle error and show toast', async () => { + mockHttpPost.mockRejectedValue(new Error('Fetch error')); - const { result } = renderHook(() => useLatencyChart(params)); + const { result } = renderHook(() => useLatencyChart(params)); - await waitFor(() => !result.current.loading); + await waitFor(() => !result.current.loading); - expect(result.current.loading).toBe(false); - expect(result.current.hasError).toBe(true); - expect(result.current.data).toBeUndefined(); - expect(mockAddDanger).toHaveBeenCalledWith( - expect.objectContaining({ - title: 'An error occurred while fetching the latency histogram', - text: 'Fetch error', - }) - ); + expect(result.current.loading).toBe(false); + expect(result.current.hasError).toBe(true); + expect(result.current.data).toBeUndefined(); + expect(mockAddDanger).toHaveBeenCalledWith( + expect.objectContaining({ + title: 'An error occurred while fetching the latency histogram', + text: 'Fetch error', + }) + ); + }); }); }); }); diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.stories.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.stories.tsx index cfde0fa338e90..8a4635b56a38b 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.stories.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.stories.tsx @@ -11,12 +11,14 @@ import type { Meta, StoryObj } from '@storybook/react'; import type { UnifiedDocViewerStorybookArgs } from '../../../../../.storybook/preview'; import minimalAPMFixture from '../../../../__fixtures__/span_apm_minimal.json'; import minimalOtelFixture from '../../../../__fixtures__/span_otel_minimal.json'; +import httpServerApmFixture from '../../../../__fixtures__/transaction_http_server_apm.json'; +import httpServerOtelFixture from '../../../../__fixtures__/transaction_http_server_otel.json'; import redisClientOtelFixture from '../../../../__fixtures__/span_otel_redis_client.json'; import { Overview, type OverviewProps } from './overview'; type Args = UnifiedDocViewerStorybookArgs; const meta = { - title: 'Span overview', + title: 'Trace Overview', component: Overview, } satisfies Meta; @@ -55,3 +57,26 @@ export const RedisClientOtel: Story = { }, tags: ['otel', 'db', 'redis', 'client', 'span'], }; + +/** + * APM HTTP transaction + */ +export const ApmHttpServer: Story = { + name: 'APM HTTP server transaction', + args: { + hit: httpServerApmFixture, + }, + tags: ['transaction', 'span', 'http', 'server', 'apm'], +}; + +/** + * OpenTelemetry HTTP server span. + * Processed by the elasticapmprocessor to add APM transaction and span attributes. + */ +export const OtelHttpServer: Story = { + name: 'OpenTelemetry HTTP server transaction', + args: { + hit: httpServerOtelFixture, + }, + tags: ['otel', 'transaction', 'span', 'http', 'server', 'apm'], +}; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.test.tsx index 524f86064795c..f42dcce5ee721 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.test.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.test.tsx @@ -33,75 +33,178 @@ jest.mock('../../components/highlight_field', () => ({ }, })); -describe('SpanSummaryTitle', () => { - afterAll(() => { - jest.clearAllMocks(); - }); - it('renders spanName with formattedSpanName and id with formattedId', () => { - const { getByText, container } = render( - - ); - - expect(container.querySelector('strong')).toHaveTextContent('Test Span'); - expect(container.querySelector('strong')?.innerHTML).toBe('Test Span'); - - expect(getByText('123')).toBeInTheDocument(); - expect(container.querySelector('span')?.innerHTML).toBe('123'); - }); - - it('renders only id with formattedId when spanName is not provided', () => { - const { getByText, container } = render( - - ); - - expect(getByText('123')).toBeInTheDocument(); - expect(container.querySelector('h2')?.innerHTML).toBe('123'); - }); - - it('renders FieldHoverActionPopover for spanName and id', () => { - const { getByText } = render( - - ); - - expect(getByText('Test Span')).toBeInTheDocument(); - expect(getByText('123')).toBeInTheDocument(); - }); - - it('renders FieldHoverActionPopover if showActions is undefined', () => { - const { container } = render(); - - expect( - container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) - ).not.toBeNull(); - }); - - it('renders FieldHoverActionPopover if showActions is true', () => { - const { container } = render( - - ); +jest.mock('../../components/transaction_name_link', () => ({ + TransactionNameLink: ({ renderContent }: { renderContent: () => React.ReactNode }) => ( +
{renderContent()}
+ ), +})); - expect( - container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) - ).not.toBeNull(); +describe('SummaryTitle', () => { + describe('Spans', () => { + afterAll(() => { + jest.clearAllMocks(); + }); + it('renders spanName with formattedName and id with formattedId', () => { + const { getByText, container } = render( + + ); + + expect(container.querySelector('strong')).toHaveTextContent('Test Span'); + expect(container.querySelector('strong')?.innerHTML).toBe('Test Span'); + + expect(getByText('123')).toBeInTheDocument(); + expect(container.querySelector('span')?.innerHTML).toBe('123'); + }); + + it('renders only id with formattedId when spanName is not provided', () => { + const { getByText, container } = render( + + ); + + expect(getByText('123')).toBeInTheDocument(); + expect(container.querySelector('h2')?.innerHTML).toBe('123'); + }); + + it('renders FieldHoverActionPopover for spanName and id', () => { + const { getByText } = render( + + ); + + expect(getByText('Test Span')).toBeInTheDocument(); + expect(getByText('123')).toBeInTheDocument(); + }); + + it('renders FieldHoverActionPopover if showActions is undefined', () => { + const { container } = render(); + + expect( + container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) + ).not.toBeNull(); + }); + + it('renders FieldHoverActionPopover if showActions is true', () => { + const { container } = render( + + ); + + expect( + container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) + ).not.toBeNull(); + }); + + it('does not render FieldHoverActionPopover if showActions is false', () => { + const { container } = render( + + ); + + expect( + container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) + ).toBeNull(); + }); }); - it('does not render FieldHoverActionPopover if showActions is false', () => { - const { container } = render( - - ); - - expect( - container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) - ).toBeNull(); + describe('Transactions', () => { + afterAll(() => { + jest.clearAllMocks(); + }); + + it('renders transactionName with formattedName and id with formattedId', () => { + const { getByText, container } = render( + + ); + + expect(container.querySelector('strong')).toHaveTextContent('Test Transaction'); + expect(container.querySelector('strong')?.innerHTML).toBe('Test Transaction'); + + expect(getByText('123')).toBeInTheDocument(); + expect(container.querySelector('span')?.innerHTML).toBe('123'); + }); + + it('renders only serviceName when transactionName is not provided', () => { + const { getByText } = render( + + ); + + expect(getByText('Test Service')).toBeInTheDocument(); + + expect(getByText('123')).toBeInTheDocument(); + }); + + it('renders FieldHoverActionPopover for transactionName and id', () => { + const { getByText } = render( + + ); + + expect(getByText('Test Transaction')).toBeInTheDocument(); + expect(getByText('123')).toBeInTheDocument(); + }); + + it('renders TransactionNameLink with transactionName and formattedName', () => { + const { getByText, container } = render( + + ); + + expect(getByText('Test Transaction')).toBeInTheDocument(); + expect(container.querySelector('strong')?.innerHTML).toBe('Test Transaction'); + }); + + it('renders id with formattedId when provided', () => { + const { getByText, container } = render( + + ); + + expect(getByText('123')).toBeInTheDocument(); + expect(container.querySelector('span')?.innerHTML).toBe('123'); + }); + + it('renders FieldHoverActionPopover if showActions is undefined', () => { + const { container } = render(); + + expect( + container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) + ).not.toBeNull(); + }); + + it('renders FieldHoverActionPopover if showActions is true', () => { + const { container } = render(); + + expect( + container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) + ).not.toBeNull(); + }); + + it('does not render FieldHoverActionPopover if showActions is false', () => { + const { container } = render(); + + expect( + container.querySelector(`[data-test-subj="${fieldHoverActionPopoverDataTestSubj}"]`) + ).toBeNull(); + }); }); }); diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx index fb03b1f3d0f46..3ea62ee109f10 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx @@ -81,23 +81,21 @@ export const SummaryTitle = ({ if (name) { nameContent = ( - <> - - - <HighlightField textSize="m" value={name} formattedValue={formattedName} as="strong"> - {transactionName && serviceName - ? ({ content }) => ( - <TransactionNameLink - serviceName={serviceName} - transactionName={transactionName} - renderContent={() => content} - /> - ) - : undefined} - </HighlightField> - - - + + + <HighlightField textSize="m" value={name} formattedValue={formattedName} as="strong"> + {transactionName && serviceName + ? ({ content }) => ( + <TransactionNameLink + serviceName={serviceName} + transactionName={transactionName} + renderContent={() => content} + /> + ) + : undefined} + </HighlightField> + + ); } else if (serviceName) { nameContent = ( From 3f730bc56d3aa0fe3ea5bee79dbc48054b8379d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Tue, 2 Sep 2025 16:44:00 +0200 Subject: [PATCH 06/14] Cleanup unused types, redundant code, fix typing errors --- .../shared/kbn-discover-utils/index.ts | 2 - .../shared/kbn-discover-utils/src/types.ts | 22 +------- .../src/utils/get_span_document_overview.ts | 55 ------------------- .../src/utils/get_trace_document_overview.ts | 4 ++ .../get_transaction_document_overview.ts | 45 --------------- .../kbn-discover-utils/src/utils/index.ts | 2 - .../document_profile/profile.ts | 10 +--- .../traces/components/service_name_link.tsx | 2 +- .../similar_spans/similar_spans.stories.tsx | 4 +- .../resources/get_field_configuration.tsx | 10 +--- 10 files changed, 13 insertions(+), 143 deletions(-) delete mode 100644 src/platform/packages/shared/kbn-discover-utils/src/utils/get_span_document_overview.ts delete mode 100644 src/platform/packages/shared/kbn-discover-utils/src/utils/get_transaction_document_overview.ts diff --git a/src/platform/packages/shared/kbn-discover-utils/index.ts b/src/platform/packages/shared/kbn-discover-utils/index.ts index 103ac8e2bcf23..742cc1b2c06db 100644 --- a/src/platform/packages/shared/kbn-discover-utils/index.ts +++ b/src/platform/packages/shared/kbn-discover-utils/index.ts @@ -41,8 +41,6 @@ export { formatHit, getDocId, getLogDocumentOverview, - getTransactionDocumentOverview, - getSpanDocumentOverview, getTraceDocumentOverview, getFlattenedTraceDocumentOverview, getIgnoredReason, diff --git a/src/platform/packages/shared/kbn-discover-utils/src/types.ts b/src/platform/packages/shared/kbn-discover-utils/src/types.ts index 4a4eec8bb5a65..da11d49e7fbb8 100644 --- a/src/platform/packages/shared/kbn-discover-utils/src/types.ts +++ b/src/platform/packages/shared/kbn-discover-utils/src/types.ts @@ -113,26 +113,6 @@ export interface LogCloudFields { 'cloud.instance.id'?: string; } -export interface TransactionDocumentOverview - extends TraceFields, - ServiceFields, - TransactionFields, - UserAgentFields {} - -export interface SpanDocumentOverview - extends TraceFields, - ServiceFields, - SpanFields, - UserAgentFields { - 'transaction.id'?: string; - 'transaction.name'?: string; - duration?: number; - kind?: string; - 'resource.attributes.telemetry.sdk.language'?: string; - 'links.trace_id'?: string; - 'links.span_id'?: string; -} - export interface TraceDocumentOverview extends TraceFields, Partial, @@ -142,6 +122,8 @@ export interface TraceDocumentOverview duration?: number; kind?: string; 'resource.attributes.telemetry.sdk.language'?: string; + 'links.trace_id'?: string; + 'links.span_id'?: string; } export interface TraceFields { diff --git a/src/platform/packages/shared/kbn-discover-utils/src/utils/get_span_document_overview.ts b/src/platform/packages/shared/kbn-discover-utils/src/utils/get_span_document_overview.ts deleted file mode 100644 index d4788a847958c..0000000000000 --- a/src/platform/packages/shared/kbn-discover-utils/src/utils/get_span_document_overview.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { DataView } from '@kbn/data-views-plugin/common'; -import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import type { DataTableRecord, SpanDocumentOverview } from '../types'; -import { fieldConstants } from '..'; -import { getFormattedFields } from './get_formatted_fields'; -import { getFlattenedFields } from './get_flattened_fields'; - -const fields: Array = [ - fieldConstants.TIMESTAMP_FIELD, - fieldConstants.PARENT_ID_FIELD, - fieldConstants.HTTP_RESPONSE_STATUS_CODE_FIELD, - fieldConstants.TRACE_ID_FIELD, - fieldConstants.SERVICE_NAME_FIELD, - fieldConstants.SERVICE_ENVIRONMENT_FIELD, - fieldConstants.AGENT_NAME_FIELD, - fieldConstants.TRANSACTION_ID_FIELD, - fieldConstants.TRANSACTION_NAME_FIELD, - fieldConstants.SPAN_NAME_FIELD, - fieldConstants.SPAN_ID_FIELD, - fieldConstants.SPAN_ACTION_FIELD, - fieldConstants.SPAN_DURATION_FIELD, - fieldConstants.SPAN_TYPE_FIELD, - fieldConstants.SPAN_SUBTYPE_FIELD, - fieldConstants.SPAN_DESTINATION_SERVICE_RESOURCE_FIELD, - fieldConstants.USER_AGENT_NAME_FIELD, - fieldConstants.USER_AGENT_VERSION_FIELD, - fieldConstants.PROCESSOR_EVENT_FIELD, - fieldConstants.OTEL_DURATION, - fieldConstants.OTEL_SPAN_KIND, - fieldConstants.OTEL_RESOURCE_ATTRIBUTES_TELEMETRY_SDK_LANGUAGE, - fieldConstants.SPAN_LINKS_SPAN_ID, - fieldConstants.SPAN_LINKS_TRACE_ID, - fieldConstants.OTEL_LINKS_SPAN_ID, - fieldConstants.OTEL_LINKS_TRACE_ID, -]; - -export function getSpanDocumentOverview( - doc: DataTableRecord, - { dataView, fieldFormats }: { dataView: DataView; fieldFormats: FieldFormatsStart } -): SpanDocumentOverview { - return getFormattedFields(doc, fields, { dataView, fieldFormats }); -} - -export function getFlattenedSpanDocumentOverview(doc: DataTableRecord): SpanDocumentOverview { - return getFlattenedFields(doc, fields); -} diff --git a/src/platform/packages/shared/kbn-discover-utils/src/utils/get_trace_document_overview.ts b/src/platform/packages/shared/kbn-discover-utils/src/utils/get_trace_document_overview.ts index 3b0eafcbc053c..58bdf6dd2884e 100644 --- a/src/platform/packages/shared/kbn-discover-utils/src/utils/get_trace_document_overview.ts +++ b/src/platform/packages/shared/kbn-discover-utils/src/utils/get_trace_document_overview.ts @@ -41,6 +41,10 @@ const fields: Array = [ fieldConstants.OTEL_DURATION, fieldConstants.OTEL_SPAN_KIND, fieldConstants.OTEL_RESOURCE_ATTRIBUTES_TELEMETRY_SDK_LANGUAGE, + fieldConstants.SPAN_LINKS_SPAN_ID, + fieldConstants.SPAN_LINKS_TRACE_ID, + fieldConstants.OTEL_LINKS_SPAN_ID, + fieldConstants.OTEL_LINKS_TRACE_ID, ]; export function getTraceDocumentOverview( diff --git a/src/platform/packages/shared/kbn-discover-utils/src/utils/get_transaction_document_overview.ts b/src/platform/packages/shared/kbn-discover-utils/src/utils/get_transaction_document_overview.ts deleted file mode 100644 index a9ab144f2ae7d..0000000000000 --- a/src/platform/packages/shared/kbn-discover-utils/src/utils/get_transaction_document_overview.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { DataView } from '@kbn/data-views-plugin/common'; -import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import { fieldConstants } from '..'; -import type { DataTableRecord, TransactionDocumentOverview } from '../types'; -import { getFormattedFields } from './get_formatted_fields'; -import { getFlattenedFields } from './get_flattened_fields'; - -const fields: Array = [ - fieldConstants.TIMESTAMP_FIELD, - fieldConstants.PARENT_ID_FIELD, - fieldConstants.HTTP_RESPONSE_STATUS_CODE_FIELD, - fieldConstants.TRACE_ID_FIELD, - fieldConstants.SERVICE_NAME_FIELD, - fieldConstants.SERVICE_ENVIRONMENT_FIELD, - fieldConstants.AGENT_NAME_FIELD, - fieldConstants.TRANSACTION_ID_FIELD, - fieldConstants.TRANSACTION_TYPE_FIELD, - fieldConstants.TRANSACTION_NAME_FIELD, - fieldConstants.TRANSACTION_DURATION_FIELD, - fieldConstants.USER_AGENT_NAME_FIELD, - fieldConstants.USER_AGENT_VERSION_FIELD, - fieldConstants.PROCESSOR_EVENT_FIELD, -]; - -export function getTransactionDocumentOverview( - doc: DataTableRecord, - { dataView, fieldFormats }: { dataView: DataView; fieldFormats: FieldFormatsStart } -): TransactionDocumentOverview { - return getFormattedFields(doc, fields, { dataView, fieldFormats }); -} - -export function getFlattenedTransactionDocumentOverview( - doc: DataTableRecord -): TransactionDocumentOverview { - return getFlattenedFields(doc, fields); -} diff --git a/src/platform/packages/shared/kbn-discover-utils/src/utils/index.ts b/src/platform/packages/shared/kbn-discover-utils/src/utils/index.ts index 903873a0f54a3..45b33d262ac33 100644 --- a/src/platform/packages/shared/kbn-discover-utils/src/utils/index.ts +++ b/src/platform/packages/shared/kbn-discover-utils/src/utils/index.ts @@ -16,8 +16,6 @@ export * from './get_doc_id'; export * from './get_ignored_reason'; export * from './get_log_document_overview'; export * from './get_trace_document_overview'; -export * from './get_transaction_document_overview'; -export * from './get_span_document_overview'; export * from './get_message_field_with_fallbacks'; export * from './get_should_show_field_handler'; export * from './get_stack_trace_fields'; diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts index c7b05cc8e3641..4ae5f12708ca6 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts @@ -40,11 +40,7 @@ export const createObservabilityTracesDocumentProfileProvider = ({ return { isMatch: false }; } - const isTraceRecord = resolveTraceRecord({ - record, - }); - - if (!isTraceRecord) { + if (!isTraceDocument(record)) { return { isMatch: false }; } @@ -57,10 +53,6 @@ export const createObservabilityTracesDocumentProfileProvider = ({ }, }); -const resolveTraceRecord = ({ record }: { record: DataTableRecord }) => { - return isTraceDocument(record); -}; - const isTraceDocument = (record: DataTableRecord) => { const dataStreamType = getFieldValue(record, DATASTREAM_TYPE_FIELD); const processorEvent = getFieldValue(record, PROCESSOR_EVENT_FIELD); diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx index 54a56025c391e..a1cc0fce7321d 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx @@ -16,7 +16,7 @@ import { ServiceNameWithIcon } from './service_name_with_icon'; const SERVICE_OVERVIEW_LOCATOR_ID = 'serviceOverviewLocator'; interface ServiceNameLinkProps { - serviceName?: string; + serviceName: string; agentName?: string; formattedServiceName: React.ReactNode; } diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/similar_spans.stories.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/similar_spans.stories.tsx index 6092907a4a0fd..91dfd5a85e77e 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/similar_spans.stories.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/similar_spans.stories.tsx @@ -42,7 +42,7 @@ export const Basic: Story = { spanDuration: 1200, latencyChart: { data: { - spanDistributionChartData: mockSpanDistributionChartData, + distributionChartData: mockSpanDistributionChartData, percentileThresholdValue: 1000, }, loading: false, @@ -73,7 +73,7 @@ export const Error: Story = { spanDuration: 1200, latencyChart: { data: { - spanDistributionChartData: [], + distributionChartData: [], }, loading: false, hasError: true, diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/resources/get_field_configuration.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/resources/get_field_configuration.tsx index a0aad190b9d5c..8616aa6d7c2f1 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/resources/get_field_configuration.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/resources/get_field_configuration.tsx @@ -7,11 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { - SpanDocumentOverview, - TransactionDocumentOverview, - TraceDocumentOverview, -} from '@kbn/discover-utils'; +import type { TraceDocumentOverview } from '@kbn/discover-utils'; import { SERVICE_NAME_FIELD, TRACE_ID_FIELD, @@ -43,8 +39,8 @@ export const getCommonFieldConfiguration = ({ attributes, flattenedDoc, }: { - attributes: TransactionDocumentOverview | SpanDocumentOverview | TraceDocumentOverview; - flattenedDoc: TransactionDocumentOverview | SpanDocumentOverview | TraceDocumentOverview; + attributes: TraceDocumentOverview; + flattenedDoc: TraceDocumentOverview; }): Record => { return { [TRANSACTION_NAME_FIELD]: { From ce2d16f5d4e496f7fde00e1ee9af92d7fe6ca930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Wed, 3 Sep 2025 11:39:25 +0200 Subject: [PATCH 07/14] Simplify trace document resolution --- .../document_profile/profile.test.ts | 21 +++-------- .../document_profile/profile.ts | 36 ++++++------------- 2 files changed, 14 insertions(+), 43 deletions(-) diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts index 482dae7d282d4..ef653d932d76b 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts @@ -67,27 +67,14 @@ describe('spanDocumentProfileProvider', () => { ).toEqual(RESOLUTION_MATCH); }); - it('does not match records with neither characteristic', () => { + it('matches records with neither characteristic as long as it comes from the correct data source', () => { expect( spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, record: buildSpanMockRecord('another-index'), }) - ).toEqual(RESOLUTION_MISMATCH); - }); - - it('does not match records with the correct data stream type but the incorrect processor event', () => { - expect( - spanDocumentProfileProvider.resolve({ - rootContext: getRootContext({ profileId }), - dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildSpanMockRecord('index', { - 'data_stream.type': ['traces'], - 'processor.event': ['other'], - }), - }) - ).toEqual(RESOLUTION_MISMATCH); + ).toEqual(RESOLUTION_MATCH); }); it('matches records with the correct data stream type and any OTEL `kind` field (unprocessed spans)', () => { @@ -197,14 +184,14 @@ describe('transactionDocumentProfileProvider', () => { ).toEqual(RESOLUTION_MATCH); }); - it('does not match records with neither characteristic', () => { + it('matches records with neither characteristic as long as it comes from the correct data source', () => { expect( transactionDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, record: buildSpanMockRecord('another-index'), }) - ).toEqual(RESOLUTION_MISMATCH); + ).toEqual(RESOLUTION_MATCH); }); }); diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts index 4ae5f12708ca6..d9861e8183d96 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts @@ -7,11 +7,9 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { DataTableRecord } from '@kbn/discover-utils'; -import { DATASTREAM_TYPE_FIELD, getFieldValue, PROCESSOR_EVENT_FIELD } from '@kbn/discover-utils'; import { TRACES_PRODUCT_FEATURE_ID } from '../../../../../../common/constants'; import type { DocumentProfileProvider } from '../../../../profiles'; -import { DocumentType, SolutionType } from '../../../../profiles'; +import { DataSourceCategory, DocumentType, SolutionType } from '../../../../profiles'; import type { ProfileProviderServices } from '../../../profile_provider_services'; import { createGetDocViewer } from './accessors'; @@ -33,32 +31,18 @@ export const createObservabilityTracesDocumentProfileProvider = ({ logs: logsContextService.getAllLogsIndexPattern(), }), }, - resolve: ({ record, rootContext }) => { + resolve: ({ rootContext, dataSourceContext }) => { const isObservabilitySolutionView = rootContext.solutionType === SolutionType.Observability; - if (!isObservabilitySolutionView) { - return { isMatch: false }; + if (isObservabilitySolutionView && dataSourceContext.category === DataSourceCategory.Traces) { + return { + isMatch: true, + context: { + type: DocumentType.Trace, + }, + }; } - if (!isTraceDocument(record)) { - return { isMatch: false }; - } - - return { - isMatch: true, - context: { - type: DocumentType.Trace, - }, - }; + return { isMatch: false }; }, }); - -const isTraceDocument = (record: DataTableRecord) => { - const dataStreamType = getFieldValue(record, DATASTREAM_TYPE_FIELD); - const processorEvent = getFieldValue(record, PROCESSOR_EVENT_FIELD); - - const isApmSpan = processorEvent === 'span' || processorEvent === 'transaction'; - const isOtelSpan = processorEvent == null; - - return dataStreamType === 'traces' && (isApmSpan || isOtelSpan); -}; From 3467ebebb28a2f1631fb27bb7692f3c5ae9b0340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Wed, 3 Sep 2025 11:50:57 +0200 Subject: [PATCH 08/14] Refine data profile matching to expect at least a trace.id --- .../document_profile/profile.test.ts | 44 ++++++------------- .../document_profile/profile.ts | 17 +++++-- 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts index ef653d932d76b..720fe375a100d 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts @@ -54,53 +54,40 @@ describe('spanDocumentProfileProvider', () => { const spanDocumentProfileProvider = createObservabilityTracesDocumentProfileProvider(mockServices); - it('matches records with the correct data stream type and the correct processor event', () => { + it('matches records with at least the correct source and a trace id', () => { expect( spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, record: buildSpanMockRecord('index', { - 'data_stream.type': ['traces'], - 'processor.event': ['span'], + 'trace.id': ['c0ffee'], }), }) ).toEqual(RESOLUTION_MATCH); }); - it('matches records with neither characteristic as long as it comes from the correct data source', () => { + it('does not match records with no trace id', () => { expect( spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, record: buildSpanMockRecord('another-index'), }) - ).toEqual(RESOLUTION_MATCH); + ).toEqual(RESOLUTION_MISMATCH); }); - it('matches records with the correct data stream type and any OTEL `kind` field (unprocessed spans)', () => { + it('matches records with the correct trace id and any OTEL `kind` field (unprocessed spans)', () => { expect( spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, record: buildSpanMockRecord('index', { - 'data_stream.type': ['traces'], + 'trace.id': ['c0ffee'], kind: 'Internal', }), }) ).toEqual(RESOLUTION_MATCH); }); - - it('defaults to matching records with the correct data stream type but no processor event field (unprocessed spans)', () => { - expect( - spanDocumentProfileProvider.resolve({ - rootContext: getRootContext({ profileId }), - dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildSpanMockRecord('index', { - 'data_stream.type': ['traces'], - }), - }) - ).toEqual(RESOLUTION_MATCH); - }); }); describe('when root profile is NOT observability', () => { @@ -109,14 +96,13 @@ describe('spanDocumentProfileProvider', () => { const spanDocumentProfileProvider = createObservabilityTracesDocumentProfileProvider(mockServices); - it('does not match records with the correct data stream type and the correct processor event', () => { + it('does not match records with the correct data source and a trace id', () => { expect( spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId, solutionType }), dataSourceContext: DATA_SOURCE_CONTEXT, record: buildSpanMockRecord('index', { - 'data_stream.type': ['traces'], - 'processor.event': ['span'], + 'trace.id': ['c0ffee'], }), }) ).toEqual(RESOLUTION_MISMATCH); @@ -171,27 +157,26 @@ describe('transactionDocumentProfileProvider', () => { const transactionDocumentProfileProvider = createObservabilityTracesDocumentProfileProvider(mockServices); - it('matches records with the correct data stream type and the correct processor event', () => { + it('matches records with the correct data source and a trace id', () => { expect( transactionDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, record: buildSpanMockRecord('index', { - 'data_stream.type': ['traces'], - 'processor.event': ['transaction'], + 'trace.id': ['c0ffee'], }), }) ).toEqual(RESOLUTION_MATCH); }); - it('matches records with neither characteristic as long as it comes from the correct data source', () => { + it('does not match records with no trace id', () => { expect( transactionDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, record: buildSpanMockRecord('another-index'), }) - ).toEqual(RESOLUTION_MATCH); + ).toEqual(RESOLUTION_MISMATCH); }); }); @@ -201,14 +186,13 @@ describe('transactionDocumentProfileProvider', () => { const transactionDocumentProfileProvider = createObservabilityTracesDocumentProfileProvider(mockServices); - it('does not match records with the correct data stream type and the correct processor event', () => { + it('does not match records with the correct data source and a trace id', () => { expect( transactionDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId, solutionType }), dataSourceContext: DATA_SOURCE_CONTEXT, record: buildTransactionMockRecord('index', { - 'data_stream.type': ['traces'], - 'processor.event': ['transaction'], + 'trace.id': ['c0ffee'], }), }) ).toEqual(RESOLUTION_MISMATCH); diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts index d9861e8183d96..7ffef28f0396a 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts @@ -7,8 +7,10 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { type DataTableRecord, TRACE_ID_FIELD, getFieldValue } from '@kbn/discover-utils'; +import type { ContextWithProfileId } from '../../../../profile_service'; import { TRACES_PRODUCT_FEATURE_ID } from '../../../../../../common/constants'; -import type { DocumentProfileProvider } from '../../../../profiles'; +import type { DataSourceContext, DocumentProfileProvider } from '../../../../profiles'; import { DataSourceCategory, DocumentType, SolutionType } from '../../../../profiles'; import type { ProfileProviderServices } from '../../../profile_provider_services'; import { createGetDocViewer } from './accessors'; @@ -31,10 +33,10 @@ export const createObservabilityTracesDocumentProfileProvider = ({ logs: logsContextService.getAllLogsIndexPattern(), }), }, - resolve: ({ rootContext, dataSourceContext }) => { + resolve: ({ record, rootContext, dataSourceContext }) => { const isObservabilitySolutionView = rootContext.solutionType === SolutionType.Observability; - if (isObservabilitySolutionView && dataSourceContext.category === DataSourceCategory.Traces) { + if (isObservabilitySolutionView && isTraceDocument(record, dataSourceContext)) { return { isMatch: true, context: { @@ -46,3 +48,12 @@ export const createObservabilityTracesDocumentProfileProvider = ({ return { isMatch: false }; }, }); + +function isTraceDocument( + record: DataTableRecord, + dataSourceContext: ContextWithProfileId +): boolean { + const traceId = getFieldValue(record, TRACE_ID_FIELD); + + return dataSourceContext.category === DataSourceCategory.Traces && !!traceId; +} From 5efe8917485e9444cc6700f8bf450f9547c64483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Wed, 3 Sep 2025 12:04:55 +0200 Subject: [PATCH 09/14] Remove further span references, merge profile tests --- .../document_profile/profile.test.ts | 104 ++---------------- .../document_profile/profile.ts | 2 +- 2 files changed, 8 insertions(+), 98 deletions(-) diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts index 720fe375a100d..139cfd2201b6f 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.test.ts @@ -16,7 +16,7 @@ import type { ContextWithProfileId } from '../../../../profile_service'; import { OBSERVABILITY_ROOT_PROFILE_ID } from '../../consts'; import type { ProfileProviderServices } from '../../../profile_provider_services'; -describe('spanDocumentProfileProvider', () => { +describe('tracesDocumentProfileProvider', () => { const getRootContext = ({ profileId, solutionType, @@ -31,7 +31,7 @@ describe('spanDocumentProfileProvider', () => { }; const DATA_SOURCE_CONTEXT: ContextWithProfileId = { - profileId: 'traces-span-document-profile', + profileId: 'traces-document-profile', category: DataSourceCategory.Traces, }; const RESOLUTION_MATCH = { @@ -59,7 +59,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildSpanMockRecord('index', { + record: buildTraceMockRecord('index', { 'trace.id': ['c0ffee'], }), }) @@ -71,7 +71,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildSpanMockRecord('another-index'), + record: buildTraceMockRecord('another-index'), }) ).toEqual(RESOLUTION_MISMATCH); }); @@ -81,7 +81,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildSpanMockRecord('index', { + record: buildTraceMockRecord('index', { 'trace.id': ['c0ffee'], kind: 'Internal', }), @@ -101,7 +101,7 @@ describe('spanDocumentProfileProvider', () => { spanDocumentProfileProvider.resolve({ rootContext: getRootContext({ profileId, solutionType }), dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildSpanMockRecord('index', { + record: buildTraceMockRecord('index', { 'trace.id': ['c0ffee'], }), }) @@ -110,97 +110,7 @@ describe('spanDocumentProfileProvider', () => { }); }); -const buildSpanMockRecord = (index: string, fields: Record = {}) => - buildDataTableRecord({ - _id: '', - _index: index, - fields: { - _index: index, - ...fields, - }, - }); - -describe('transactionDocumentProfileProvider', () => { - const getRootContext = ({ - profileId, - solutionType, - }: { - profileId: string; - solutionType?: SolutionType; - }): ContextWithProfileId => { - return { - profileId, - solutionType: solutionType ?? SolutionType.Observability, - }; - }; - - const DATA_SOURCE_CONTEXT: ContextWithProfileId = { - profileId: 'traces-transaction-document-profile', - category: DataSourceCategory.Traces, - }; - const RESOLUTION_MATCH = { - isMatch: true, - context: { - type: DocumentType.Trace, - }, - }; - const RESOLUTION_MISMATCH = { - isMatch: false, - }; - - const mockServices: ProfileProviderServices = { - ...createContextAwarenessMocks().profileProviderServices, - }; - - describe('when root profile is observability', () => { - const profileId = OBSERVABILITY_ROOT_PROFILE_ID; - const transactionDocumentProfileProvider = - createObservabilityTracesDocumentProfileProvider(mockServices); - - it('matches records with the correct data source and a trace id', () => { - expect( - transactionDocumentProfileProvider.resolve({ - rootContext: getRootContext({ profileId }), - dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildSpanMockRecord('index', { - 'trace.id': ['c0ffee'], - }), - }) - ).toEqual(RESOLUTION_MATCH); - }); - - it('does not match records with no trace id', () => { - expect( - transactionDocumentProfileProvider.resolve({ - rootContext: getRootContext({ profileId }), - dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildSpanMockRecord('another-index'), - }) - ).toEqual(RESOLUTION_MISMATCH); - }); - }); - - describe('when solutionType is NOT observability', () => { - const profileId = OBSERVABILITY_ROOT_PROFILE_ID; - const solutionType = SolutionType.Default; - const transactionDocumentProfileProvider = - createObservabilityTracesDocumentProfileProvider(mockServices); - - it('does not match records with the correct data source and a trace id', () => { - expect( - transactionDocumentProfileProvider.resolve({ - rootContext: getRootContext({ profileId, solutionType }), - dataSourceContext: DATA_SOURCE_CONTEXT, - record: buildTransactionMockRecord('index', { - 'trace.id': ['c0ffee'], - }), - }) - ).toEqual(RESOLUTION_MISMATCH); - }); - }); -}); - -const buildTransactionMockRecord = (index: string, fields: Record = {}) => +const buildTraceMockRecord = (index: string, fields: Record = {}) => buildDataTableRecord({ _id: '', _index: index, diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts index 7ffef28f0396a..dbba2fa7eea67 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts @@ -15,7 +15,7 @@ import { DataSourceCategory, DocumentType, SolutionType } from '../../../../prof import type { ProfileProviderServices } from '../../../profile_provider_services'; import { createGetDocViewer } from './accessors'; -const OBSERVABILITY_TRACES_SPAN_DOCUMENT_PROFILE_ID = 'observability-traces-span-document-profile'; +const OBSERVABILITY_TRACES_SPAN_DOCUMENT_PROFILE_ID = 'observability-traces-document-profile'; export const createObservabilityTracesDocumentProfileProvider = ({ tracesContextService, From 379ca59cf250c2d04275b4e90351a099d2523783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Wed, 3 Sep 2025 17:19:50 +0200 Subject: [PATCH 10/14] Tidy-up overly defensive coding and some better naming --- .../traces/components/service_name_link.tsx | 12 ++++---- .../observability/traces/components/trace.tsx | 4 +-- .../components/transaction_name_link.tsx | 19 ++++++------- .../traces/doc_viewer_overview/overview.tsx | 6 ++-- .../doc_viewer_overview/resources/fields.ts | 25 +++++++++++++---- .../sub_components/dependency_name_link.tsx | 28 +++++++++---------- .../sub_components/summary_title.tsx | 17 +++++------ 7 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx index a1cc0fce7321d..6880555b69677 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/service_name_link.tsx @@ -42,13 +42,11 @@ export function ServiceNameLink({ rangeTo: string; }>(SERVICE_OVERVIEW_LOCATOR_ID); - const href = - serviceName && - apmLinkToServiceEntityLocator?.getRedirectUrl({ - serviceName, - rangeFrom: timeRangeFrom, - rangeTo: timeRangeTo, - }); + const href = apmLinkToServiceEntityLocator?.getRedirectUrl({ + serviceName, + rangeFrom: timeRangeFrom, + rangeTo: timeRangeTo, + }); const routeLinkProps = href ? getRouterLinkProps({ diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/trace.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/trace.tsx index 657e266372cc8..491b430443491 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/trace.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/trace.tsx @@ -14,7 +14,7 @@ import { i18n } from '@kbn/i18n'; import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; import type { DataViewField } from '@kbn/data-views-plugin/common'; import { SERVICE_NAME_FIELD } from '@kbn/discover-utils'; -import { transactionTraceFields, traceFields } from '../doc_viewer_overview/resources/fields'; +import { transactionTraceFields, spanTraceFields } from '../doc_viewer_overview/resources/fields'; import { SummaryField } from '../doc_viewer_overview/sub_components/summary_field'; import { getUnifiedDocViewerServices } from '../../../../plugin'; import type { FieldConfiguration } from '../resources/get_field_configuration'; @@ -70,7 +70,7 @@ export const Trace = ({ const fieldRows = displayType === 'span' - ? traceFields.map((fieldId: string) => ( + ? spanTraceFields.map((fieldId: string) => ( React.ReactNode; + transactionName: string; + renderContent?: (name: string) => React.ReactNode; } export function TransactionNameLink({ @@ -34,16 +37,12 @@ export function TransactionNameLink({ const { from: timeRangeFrom, to: timeRangeTo } = dataService.query.timefilter.timefilter.getTime(); - const apmLinkToTransactionByNameLocator = urlService.locators.get<{ - serviceName: string; - transactionName: string; - rangeFrom: string; - rangeTo: string; - }>(TRANSACTION_DETAILS_BY_NAME_LOCATOR); + const apmLinkToTransactionByNameLocator = urlService.locators.get( + TRANSACTION_DETAILS_BY_NAME_LOCATOR + ); const href = serviceName && - transactionName && apmLinkToTransactionByNameLocator?.getRedirectUrl({ serviceName, transactionName, diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx index 77d92e398723a..d7f4566468d96 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx @@ -32,7 +32,7 @@ import { getUnifiedDocViewerServices } from '../../../../plugin'; import { SpanLinks } from '../components/span_links'; import { Trace } from '../components/trace'; import { RootSpanProvider } from './hooks/use_root_span'; -import { fields, allFields } from './resources/fields'; +import { spanAndTransactionFields, traceFields } from './resources/fields'; import { getFieldConfiguration } from './resources/get_field_configuration'; import { DurationSummary } from './sub_components/duration_summary'; import { SummaryField } from './sub_components/summary_field'; @@ -73,7 +73,7 @@ export function Overview({ }), [dataView, fieldFormats, hit] ); - const { dataViewFields } = useDataViewFields({ fields: allFields, dataView, columnsMeta }); + const { dataViewFields } = useDataViewFields({ fields: traceFields, dataView, columnsMeta }); const fieldConfigurations = useMemo( () => getFieldConfiguration({ attributes: formattedDoc, flattenedDoc }), [formattedDoc, flattenedDoc] @@ -136,7 +136,7 @@ export function Overview({ />
- {fields.map((fieldId) => ( + {spanAndTransactionFields.map((fieldId) => ( (DEPENDENCY_OVERVIEW_LOCATOR_ID); + const apmLinkToDependencyOverviewLocator = urlService.locators.get( + DEPENDENCY_OVERVIEW_LOCATOR_ID + ); - const href = - environment && - apmLinkToDependencyOverviewLocator?.getRedirectUrl({ - dependencyName, - environment, - rangeFrom: timeRangeFrom, - rangeTo: timeRangeTo, - }); + const href = apmLinkToDependencyOverviewLocator?.getRedirectUrl({ + dependencyName, + environment, + rangeFrom: timeRangeFrom, + rangeTo: timeRangeTo, + }); const routeLinkProps = href ? getRouterLinkProps({ diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx index 3ea62ee109f10..b7e465376b43e 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/sub_components/summary_title.tsx @@ -77,7 +77,6 @@ export const SummaryTitle = ({ const nameField = transactionName ? TRANSACTION_NAME_FIELD : SPAN_NAME_FIELD; let nameContent; - let idContent; if (name) { nameContent = ( @@ -110,15 +109,13 @@ export const SummaryTitle = ({ ); } - if (id) { - idContent = ( - - - <HighlightField value={id} formattedValue={formattedId} /> - - - ); - } + const idContent = id ? ( + + + <HighlightField value={id} formattedValue={formattedId} /> + + + ) : undefined; return ( <> From 907dcb6e825cfdedd018aacee24bfac0142ff589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Wed, 3 Sep 2025 17:42:50 +0200 Subject: [PATCH 11/14] use ProcessorEvent --- .../observability/traces/doc_viewer_overview/overview.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx index d7f4566468d96..59133578191ce 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/doc_viewer_overview/overview.tsx @@ -24,6 +24,7 @@ import { } from '@kbn/discover-utils'; import type { TraceIndexes } from '@kbn/discover-utils/src'; import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; +import { ProcessorEvent } from '@kbn/apm-types-shared'; import React, { useMemo, useState } from 'react'; import { css } from '@emotion/react'; import { useDataViewFields } from '../../../../hooks/use_data_view_fields'; @@ -83,7 +84,9 @@ export function Overview({ const formattedId = formattedDoc[TRANSACTION_ID_FIELD] || formattedDoc[SPAN_ID_FIELD]; const formattedName = formattedDoc[TRANSACTION_NAME_FIELD] || formattedDoc[SPAN_NAME_FIELD]; - const displayType = formattedDoc[TRANSACTION_NAME_FIELD] ? 'transaction' : 'span'; + const displayType = formattedDoc[TRANSACTION_NAME_FIELD] + ? ProcessorEvent.transaction + : ProcessorEvent.span; const apmDurationField = flattenedDoc[TRANSACTION_DURATION_FIELD] ?? flattenedDoc[SPAN_DURATION_FIELD]; From f5a0cdb1c9b048126b161c9f89fa1868bc3fa856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Wed, 3 Sep 2025 17:58:10 +0200 Subject: [PATCH 12/14] Align on Overview tab title --- .../document_profile/accessors/doc_viewer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/accessors/doc_viewer.tsx b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/accessors/doc_viewer.tsx index 6da6534a5e67f..5ba0330520d96 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/accessors/doc_viewer.tsx +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/accessors/doc_viewer.tsx @@ -20,8 +20,8 @@ export const createGetDocViewer = (prev: (params: DocViewerExtensionParams) => DocViewerExtension) => (params: DocViewerExtensionParams) => { const prevDocViewer = prev(params); - const tabTitle = i18n.translate('discover.docViews.observability.traces.traceOverview.title', { - defaultMessage: 'Trace Overview', + const tabTitle = i18n.translate('discover.docViews.observability.traces.overview.title', { + defaultMessage: 'Overview', }); return { ...prevDocViewer, From 662fd7c63ccffbb8d0241df2422deac5261b157d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Thu, 4 Sep 2025 10:35:48 +0200 Subject: [PATCH 13/14] Remove TODO --- .../observability/traces/components/similar_spans/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/index.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/index.tsx index 310ee5c6b6bfb..0960250b8dd35 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/index.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/similar_spans/index.tsx @@ -18,7 +18,7 @@ import type { LatencyChartData } from '../../doc_viewer_overview/hooks/use_laten export interface SimilarSpansProps { spanDuration: number; latencyChart: { - data: LatencyChartData | null; // TODO move this interface + data: LatencyChartData | null; loading: boolean; hasError: boolean; }; From 9d13460fd92cfaaca7d5d84ce940e59c4ec0cd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Rica?= Date: Thu, 4 Sep 2025 18:20:45 +0200 Subject: [PATCH 14/14] Add TODO --- .../traces_document_profile/document_profile/profile.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts index dbba2fa7eea67..c6d2623bd6433 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/traces_document_profile/document_profile/profile.ts @@ -55,5 +55,7 @@ function isTraceDocument( ): boolean { const traceId = getFieldValue(record, TRACE_ID_FIELD); + // TODO: Relying on this data source check is a hack, this should be refactored to use + // _index field once ES|QL queries return default metadata. return dataSourceContext.category === DataSourceCategory.Traces && !!traceId; }