[Discover][APM] Span/Transaction name titles and id subtitles for doc overview tab (#218311)

## Summary

This PR changes the Span/Transaction overview tab title to being the
Name/ID title/subtitle pair. The span/transaction name is the title,
with the id is being a subdued text section. The title components for
Span/Transaction will fallback if the name field is not present to
showing just the id, or for the case of Transactions, the service name
if neither name nor id is available.

|   |  Screenshot example  |
| - | - |
| Transaction | ![Span title 4 Screenshot 2025-04-15
182149](https://github.com/user-attachments/assets/1435d5d6-b543-48ca-be9b-9fb7102d0c8a)
|
| Span | ![Span title 3 Screenshot 2025-04-15
182149](https://github.com/user-attachments/assets/be970bbf-e679-41e4-a0d8-7429e65a2559)
|




Closes #216861

## How to test

* Enable traces for discover by adding the following to
`kibana.dev.yaml`:
```yaml
discover.experimental.enabledProfiles:
  - observability-traces-data-source-profile
  - observability-traces-transaction-document-profile
  - observability-traces-span-document-profile
 ```
* Ensure you are on an Observability root profile space
* Go to Discover, use or create a Data View profiles targetting traces-* (such as remote_cluster:traces-*).
* Click on a span/transaction to expand the doc viewer
* The title should be the span title/id or the transaction title/id with the transaction title being a link.
This commit is contained in:
Gonçalo Rica Pais da Silva 2025-04-17 15:13:32 +02:00 committed by GitHub
parent e21bec3f31
commit 821f74ea5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 128 additions and 42 deletions

View file

@ -24,6 +24,7 @@ export const TRANSACTION_ID_FIELD = 'transaction.id';
export const TRANSACTION_NAME_FIELD = 'transaction.name';
export const TRANSACTION_DURATION_FIELD = 'transaction.duration.us';
export const SPAN_NAME_FIELD = 'span.name';
export const SPAN_ID_FIELD = 'span.id';
export const SPAN_ACTION_FIELD = 'span.action';
export const SPAN_DURATION_FIELD = 'span.duration.us';
export const SPAN_TYPE_FIELD = 'span.type';

View file

@ -138,6 +138,7 @@ export interface TransactionTraceFields {
}
export interface SpanTraceFields {
'span.id': string;
'span.name'?: string;
'span.action'?: string;
'span.duration.us'?: number;

View file

@ -26,6 +26,7 @@ export function getTraceDocumentOverview(doc: DataTableRecord): TraceDocumentOve
fieldConstants.TRANSACTION_NAME_FIELD,
fieldConstants.TRANSACTION_DURATION_FIELD,
fieldConstants.SPAN_NAME_FIELD,
fieldConstants.SPAN_ID_FIELD,
fieldConstants.SPAN_ACTION_FIELD,
fieldConstants.SPAN_DURATION_FIELD,
fieldConstants.SPAN_TYPE_FIELD,

View file

@ -11,7 +11,8 @@ import React, { useCallback } from 'react';
import { ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { traceFields } from '../doc_viewer_span_overview/resources/fields';
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 { getUnifiedDocViewerServices } from '../../../../plugin';
@ -58,6 +59,19 @@ export const Trace = ({
[rangeFrom, rangeTo, displayLimit, serviceName, traceId, transactionId]
);
const fieldRows =
displayType === 'span'
? spanTraceFields.map((fieldId: string) => (
<SpanSummaryField key={fieldId} fieldId={fieldId} fieldConfiguration={fields[fieldId]} />
))
: transactionTraceFields.map((fieldId: string) => (
<TransactionSummaryField
key={fieldId}
fieldId={fieldId}
fieldConfiguration={fields[fieldId]}
/>
));
return (
<>
<EuiTitle size="s">
@ -69,21 +83,7 @@ export const Trace = ({
</EuiTitle>
<EuiSpacer size="m" />
<EuiFlexGroup direction="column">
<EuiFlexItem>
{traceFields.map((fieldId: string) => {
const props = {
key: fieldId,
fieldId,
fieldConfiguration: fields[fieldId],
};
return displayType === 'span' ? (
<SpanSummaryField {...props} />
) : (
<TransactionSummaryField {...props} />
);
})}
</EuiFlexItem>
<EuiFlexItem>{fieldRows}</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<ReactEmbeddableRenderer

View file

@ -16,9 +16,14 @@ import { getUnifiedDocViewerServices } from '../../../../plugin';
interface TransactionNameLinkProps {
serviceName: string;
transactionName: string;
renderContent?: (name: string) => React.ReactNode;
}
export function TransactionNameLink({ transactionName, serviceName }: TransactionNameLinkProps) {
export function TransactionNameLink({
transactionName,
serviceName,
renderContent,
}: TransactionNameLinkProps) {
const {
share: { url: urlService },
core,
@ -57,7 +62,9 @@ export function TransactionNameLink({ transactionName, serviceName }: Transactio
})
: undefined;
const content = <EuiText size="xs">{transactionName}</EuiText>;
const content = renderContent?.(transactionName) ?? (
<EuiText size="xs">{transactionName}</EuiText>
);
return (
<>

View file

@ -11,7 +11,6 @@ import {
HTTP_RESPONSE_STATUS_CODE_FIELD,
SERVICE_NAME_FIELD,
SPAN_DESTINATION_SERVICE_RESOURCE_FIELD,
SPAN_NAME_FIELD,
SPAN_SUBTYPE_FIELD,
SPAN_TYPE_FIELD,
TIMESTAMP_FIELD,
@ -20,7 +19,6 @@ import {
} from '@kbn/discover-utils';
export const spanFields = [
SPAN_NAME_FIELD,
SERVICE_NAME_FIELD,
SPAN_DESTINATION_SERVICE_RESOURCE_FIELD,
TIMESTAMP_FIELD,
@ -29,4 +27,4 @@ export const spanFields = [
SPAN_SUBTYPE_FIELD,
];
export const traceFields = [TRACE_ID_FIELD, TRANSACTION_NAME_FIELD];
export const spanTraceFields = [TRACE_ID_FIELD, TRANSACTION_NAME_FIELD];

View file

@ -9,12 +9,13 @@
import React, { useMemo } from 'react';
import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types';
import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiPanel, EuiSpacer } from '@elastic/eui';
import {
SERVICE_NAME_FIELD,
SPAN_DURATION_FIELD,
TRACE_ID_FIELD,
SPAN_ID_FIELD,
SPAN_NAME_FIELD,
TRANSACTION_ID_FIELD,
getTraceDocumentOverview,
} from '@kbn/discover-utils';
@ -25,6 +26,7 @@ import { getSpanFieldConfiguration } from './resources/get_span_field_configurat
import { SpanSummaryField } from './sub_components/span_summary_field';
import { SpanDurationSummary } from './sub_components/span_duration_summary';
import { Trace } from '../components/trace';
import { SpanSummaryTitle } from './sub_components/span_summary_title';
export type SpanOverviewProps = DocViewRenderProps & {
transactionIndexPattern: string;
@ -53,13 +55,7 @@ export function SpanOverview({
>
<EuiPanel color="transparent" hasShadow={false} paddingSize="none">
<EuiSpacer size="m" />
<EuiTitle size="s">
<h2>
{i18n.translate('unifiedDocViewer.observability.traces.spanOverview.title', {
defaultMessage: 'Span detail',
})}
</h2>
</EuiTitle>
<SpanSummaryTitle name={parsedDoc[SPAN_NAME_FIELD]} id={parsedDoc[SPAN_ID_FIELD]} />
<EuiSpacer size="m" />
{spanFields.map((fieldId) => (
<SpanSummaryField

View file

@ -0,0 +1,33 @@
/*
* 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 { EuiText, EuiTitle } from '@elastic/eui';
import React from 'react';
export interface SpanSummaryTitleProps {
name?: string;
id: string;
}
export const SpanSummaryTitle = ({ name, id }: SpanSummaryTitleProps) => {
return name ? (
<>
<EuiTitle size="xs">
<h2>{name}</h2>
</EuiTitle>
<EuiText size="xs" color="subdued">
{id}
</EuiText>
</>
) : (
<EuiTitle size="xs">
<h2>{id}</h2>
</EuiTitle>
);
};

View file

@ -11,6 +11,7 @@ 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';
@ -22,3 +23,5 @@ export const transactionFields = [
USER_AGENT_NAME_FIELD,
USER_AGENT_VERSION_FIELD,
];
export const transactionTraceFields = [TRACE_ID_FIELD];

View file

@ -0,0 +1,50 @@
/*
* 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 { EuiText, EuiTitle } from '@elastic/eui';
import React from 'react';
import { TransactionNameLink } from '../../components/transaction_name_link';
export interface TransactionSummaryTitleProps {
serviceName: string;
name?: string;
id?: string;
}
const renderTransactionTitle = (transactionName: string) => <strong>{transactionName}</strong>;
export const TransactionSummaryTitle = ({
serviceName,
name,
id,
}: TransactionSummaryTitleProps) => {
return (
<>
<EuiTitle size="xs">
<h2>
{name ? (
<TransactionNameLink
serviceName={serviceName}
transactionName={name}
renderContent={renderTransactionTitle}
/>
) : (
serviceName
)}
</h2>
</EuiTitle>
{id && (
<EuiText size="xs" color="subdued">
{id}
</EuiText>
)}
</>
);
};

View file

@ -9,13 +9,13 @@
import React, { useMemo } from 'react';
import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types';
import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiPanel, EuiSpacer } from '@elastic/eui';
import {
SERVICE_NAME_FIELD,
TRACE_ID_FIELD,
TRANSACTION_DURATION_FIELD,
TRANSACTION_ID_FIELD,
TRANSACTION_NAME_FIELD,
getTraceDocumentOverview,
} from '@kbn/discover-utils';
import { FieldActionsProvider } from '../../../../hooks/use_field_actions';
@ -26,6 +26,7 @@ import { TransactionDurationSummary } from './sub_components/transaction_duratio
import { RootTransactionProvider } from './hooks/use_root_transaction';
import { Trace } from '../components/trace';
import { TransactionSummaryTitle } from './sub_components/transaction_summary_title';
export type TransactionOverviewProps = DocViewRenderProps & {
tracesIndexPattern: string;
};
@ -46,13 +47,6 @@ export function TransactionOverview({
[parsedDoc]
);
const detailTitle = i18n.translate(
'unifiedDocViewer.observability.traces.transactionOverview.title',
{
defaultMessage: 'Transaction detail',
}
);
return (
<RootTransactionProvider traceId={traceId} indexPattern={tracesIndexPattern}>
<FieldActionsProvider
@ -63,9 +57,11 @@ export function TransactionOverview({
>
<EuiPanel color="transparent" hasShadow={false} paddingSize="none">
<EuiSpacer size="m" />
<EuiTitle size="s">
<h2>{detailTitle}</h2>
</EuiTitle>
<TransactionSummaryTitle
serviceName={parsedDoc[SERVICE_NAME_FIELD]}
id={parsedDoc[TRANSACTION_ID_FIELD]!}
name={parsedDoc[TRANSACTION_NAME_FIELD]!}
/>
<EuiSpacer size="m" />
{transactionFields.map((fieldId) => (
<TransactionSummaryField