Skip to content

Commit 3a3275f

Browse files
iblancofkibanamachine
authored andcommitted
[Discover][Traces] Add APM operation IDs to traces log fetching (#268452)
## Summary Relates to elastic/observability-dev#5348 This PR instruments two log-fetching code paths in the trace document viewer with APM execution context and explicit error capture, enabling RED metrics (rate, errors, duration) visibility in APM for those operations. Without operation IDs, all Kibana search requests appear identically in APM's search/ese spans with no way to attribute latency or throughput to a specific feature. ### fetchLogDocumentById `fetchLogDocumentById` is called when a user opens a log document from the expanded waterfall flyout. Because this function owns the `data.search.search` call directly, we can instrument it fully: - An `executionContext` with `operation_id: fetch-log-by-id` is passed to the search call. The Kibana HTTP layer forwards this via the `x-kbn-context` header, and the server-side APM agent attaches it as a `kibana_meta_operation_id` label on the resulting span. - The search call is wrapped in a try-catch that calls `apm.captureError` with the same label, so failures appear in APM with the correct operation context. - `toasts.addDanger` is replaced with `toasts.add({ color: 'danger', ... })` to avoid double error capture: `addDanger` internally calls `apm.captureError`, which would produce a duplicate error event alongside ours. ### Logs embeddable (TraceContextLogEvents) The correlated logs section in the trace document overview renders via `LazySavedSearchComponent`, which delegates all fetching to `SearchEmbeddable` internally. Because we do not own the fetch call, explicit error capture is not feasible: `SearchEmbeddable` handles errors internally and surfaces them as inline UI state, so failures never propagate as rejections we can catch or as failed HTTP spans in APM. For latency and throughput, we thread `executionContext` with `operation_id: fetch-trace-context-logs` from `TraceContextLogEvents` down through `LogEventsComponent` and `LazySavedSearchComponent` to `SearchEmbeddable`'s `parentApi`. The embeddable reads execution context from `parentApi` and includes it in every search request, tagging those spans with `kibana_meta_operation_id: fetch-trace-context-logs` on the server. This required adding `executionContext` as a typed prop to `LogEventsProps` and `SavedSearchComponentProps`, and declaring `@kbn/core-execution-context-common` in `kbn_references` for both affected packages. ### How to test Verify the logs embeddable span label: - Start Kibana with a synthtrace scenario that includes log documents correlated to a trace (e.g. `node scripts/synthtrace logs_traces_hosts`). - Open a trace document in the doc viewer and expand the Logs section. - In APM, filter search/ese spans by `labels.kibana_meta_operation_id: fetch-trace-context-logs` and confirm requests from the logs section appear with that label. Verify the fetch log span label and error capture: - From the expanded waterfall, click a log item to open the log document flyout. - In APM, filter by `labels.kibana_meta_operation_id: fetch-log-by-id` and confirm the fetch span appears. - To test error capture: temporarily break the fetch (e.g. pass an invalid index) and confirm a single APM error appears with `labels.kibana_meta_operation_id: fetch-log-by-id`, not two. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
1 parent 1f14d4e commit 3a3275f

16 files changed

Lines changed: 129 additions & 28 deletions

File tree

src/platform/packages/shared/kbn-saved-search-component/moon.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ project:
1717
owner: '@elastic/obs-exploration-team'
1818
sourceRoot: src/platform/packages/shared/kbn-saved-search-component
1919
dependsOn:
20+
- '@kbn/core-execution-context-common'
2021
- '@kbn/embeddable-plugin'
2122
- '@kbn/shared-ux-utility'
2223
- '@kbn/discover-utils'

src/platform/packages/shared/kbn-saved-search-component/src/components/saved_search.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,15 @@ const SavedSearchComponentTable: React.FC<
171171
const embeddableApi = useRef<SearchEmbeddableApi | undefined>(undefined);
172172
const [isEmbeddableApiAvailable, setIsEmbeddableApiAvailable] = useState(false);
173173

174+
const { executionContext } = props;
174175
const parentApi = useMemo(() => {
175176
return {
177+
...(executionContext ? { executionContext } : {}),
176178
getSerializedStateForChild: () => {
177179
return initialSerializedState;
178180
},
179181
};
180-
}, [initialSerializedState]);
182+
}, [initialSerializedState, executionContext]);
181183

182184
useEffect(
183185
function syncIndex() {

src/platform/packages/shared/kbn-saved-search-component/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type { CSSProperties } from 'react';
1616
import type { SortOrder } from '@kbn/saved-search-plugin/public';
1717
import type { DiscoverGridSettings } from '@kbn/saved-search-plugin/common';
1818
import type { DataGridDensity } from '@kbn/unified-data-table';
19+
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
1920

2021
export interface SavedSearchComponentDependencies {
2122
embeddable: EmbeddableStart;
@@ -56,4 +57,5 @@ export interface SavedSearchComponentProps extends SavedSearchTableConfig {
5657
height?: CSSProperties['height'];
5758
displayOptions?: NonPersistedDisplayOptions;
5859
onTableConfigChange?: (config: SavedSearchTableConfig) => void;
60+
executionContext?: KibanaExecutionContext;
5961
}

src/platform/packages/shared/kbn-saved-search-component/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"target/**/*"
1818
],
1919
"kbn_references": [
20+
"@kbn/core-execution-context-common",
2021
"@kbn/embeddable-plugin",
2122
"@kbn/shared-ux-utility",
2223
"@kbn/discover-utils",

src/platform/plugins/shared/discover_shared/moon.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ dependsOn:
2626
- '@kbn/data-views-plugin'
2727
- '@kbn/slo-schema'
2828
- '@kbn/unified-doc-viewer'
29+
- '@kbn/core-execution-context-common'
2930
tags:
3031
- plugin
3132
- prod

src/platform/plugins/shared/discover_shared/public/services/discover_features/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { FunctionComponent } from 'react';
1313
import type React from 'react';
1414
import type { DataGridCellValueElementProps } from '@kbn/unified-data-table';
1515
import type { Query, TimeRange } from '@kbn/es-query';
16+
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
1617
import type {
1718
ErrorsByTraceId,
1819
FocusedTraceWaterfallProps,
@@ -112,6 +113,7 @@ export interface ObservabilityLogEventsFeature {
112113
enableDocumentViewer: false;
113114
enableFilters: false;
114115
};
116+
executionContext?: KibanaExecutionContext;
115117
}) => JSX.Element;
116118
}
117119

src/platform/plugins/shared/discover_shared/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
"@kbn/data-views-plugin",
1616
"@kbn/slo-schema",
1717
"@kbn/unified-doc-viewer",
18+
"@kbn/core-execution-context-common",
1819
]
1920
}

src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/hooks/use_fetch_log/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ export function useFetchLog({ id, index }: UseFetchLogParams) {
4545
useEffect(() => {
4646
if (error) {
4747
const errorMessage = error instanceof Error ? error.message : String(error);
48-
core.notifications.toasts.addDanger({
48+
core.notifications.toasts.add({
49+
color: 'danger',
50+
iconType: 'error',
4951
title: i18n.translate('unifiedDocViewer.fullScreenWaterfall.logDocument.error', {
5052
defaultMessage: 'An error occurred while fetching the log document',
5153
}),

src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/hooks/use_fetch_log/use_fetch_log.test.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const mockFetchLogDocumentById = jest.fn<
2525
>,
2626
any
2727
>();
28-
const mockAddDanger = jest.fn();
28+
const mockAdd = jest.fn();
2929

3030
const mockGetById: jest.Mock<
3131
| {
@@ -55,7 +55,7 @@ const mockGetById: jest.Mock<
5555
core: {
5656
notifications: {
5757
toasts: {
58-
addDanger: mockAddDanger,
58+
add: mockAdd,
5959
},
6060
},
6161
},
@@ -70,7 +70,6 @@ describe('useFetchLog', () => {
7070
mockGetById.mockReturnValue({
7171
fetchLogDocumentById: mockFetchLogDocumentById,
7272
});
73-
mockAddDanger.mockClear();
7473
});
7574

7675
it('should return undefined when feature is not registered', async () => {
@@ -209,7 +208,9 @@ describe('useFetchLog', () => {
209208
await waitFor(() => !result.current.loading);
210209

211210
await waitFor(() => {
212-
expect(mockAddDanger).toHaveBeenCalledWith({
211+
expect(mockAdd).toHaveBeenCalledWith({
212+
color: 'danger',
213+
iconType: 'error',
213214
title: 'An error occurred while fetching the log document',
214215
text: errorMessage,
215216
});
@@ -225,7 +226,9 @@ describe('useFetchLog', () => {
225226
await waitFor(() => !result.current.loading);
226227

227228
await waitFor(() => {
228-
expect(mockAddDanger).toHaveBeenCalledWith({
229+
expect(mockAdd).toHaveBeenCalledWith({
230+
color: 'danger',
231+
iconType: 'error',
229232
title: 'An error occurred while fetching the log document',
230233
text: errorMessage,
231234
});

src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/trace_context_log_events/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import { useDiscoverLinkAndEsqlQuery } from '../../../../../hooks/use_discover_l
1818
import { useOpenInDiscoverSectionAction } from '../../../../../hooks/use_open_in_discover_section_action';
1919
import { TRACES_DOC_VIEWER_EBT_ELEMENTS, TRACES_DOC_VIEWER_EBT_DETAILS } from '../../ebt_constants';
2020

21+
const FETCH_TRACE_CONTEXT_LOGS_OPERATION_ID = 'fetch-trace-context-logs';
22+
2123
const logsTitle = i18n.translate('unifiedDocViewer.observability.traces.section.logs.title', {
2224
defaultMessage: 'Logs',
2325
});
@@ -97,6 +99,7 @@ export function TraceContextLogEvents({
9799
nonHighlightingQuery={query}
98100
timeRange={savedSearchTimeRange}
99101
index={indexes.logs}
102+
executionContext={{ meta: { operation_id: FETCH_TRACE_CONTEXT_LOGS_OPERATION_ID } }}
100103
/>
101104
</div>
102105
</ContentFrameworkSection>

0 commit comments

Comments
 (0)