[9.0] [data.search] Add APM instrumentation to search route (#214280) (#215214)

# Backport

This will backport the following commits from `main` to `9.0`:
- [[data.search] Add APM instrumentation to search route
(#214280)](https://github.com/elastic/kibana/pull/214280)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Lukas
Olson","email":"lukas@elastic.co"},"sourceCommit":{"committedDate":"2025-03-19T16:37:54Z","message":"[data.search]
Add APM instrumentation to search route (#214280)\n\n##
Summary\n\nResolves
https://github.com/elastic/kibana/issues/208219.\n\nAdds APM
instrumentation to the search route called by `data.search`\nservices.
This was part of `bsearch` before it was removed but for some\nreason
was never added to the search routes directly.\n\n### Checklist\n\n- [x]
[Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [ ] [Flaky
Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\nused on any tests changed\n\n---------\n\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"423d331b3b8b333d71b7cbcf41e09158c83a9108","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Feature:Search","release_note:skip","Team:DataDiscovery","backport:prev-minor","apm:instrumentation","v9.1.0"],"title":"[data.search]
Add APM instrumentation to search
route","number":214280,"url":"https://github.com/elastic/kibana/pull/214280","mergeCommit":{"message":"[data.search]
Add APM instrumentation to search route (#214280)\n\n##
Summary\n\nResolves
https://github.com/elastic/kibana/issues/208219.\n\nAdds APM
instrumentation to the search route called by `data.search`\nservices.
This was part of `bsearch` before it was removed but for some\nreason
was never added to the search routes directly.\n\n### Checklist\n\n- [x]
[Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [ ] [Flaky
Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\nused on any tests changed\n\n---------\n\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"423d331b3b8b333d71b7cbcf41e09158c83a9108"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/214280","number":214280,"mergeCommit":{"message":"[data.search]
Add APM instrumentation to search route (#214280)\n\n##
Summary\n\nResolves
https://github.com/elastic/kibana/issues/208219.\n\nAdds APM
instrumentation to the search route called by `data.search`\nservices.
This was part of `bsearch` before it was removed but for some\nreason
was never added to the search routes directly.\n\n### Checklist\n\n- [x]
[Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [ ] [Flaky
Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\nused on any tests changed\n\n---------\n\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"423d331b3b8b333d71b7cbcf41e09158c83a9108"}}]}]
BACKPORT-->

Co-authored-by: Lukas Olson <lukas@elastic.co>
This commit is contained in:
Kibana Machine 2025-03-19 19:21:54 +01:00 committed by GitHub
parent 483f5ecffa
commit d10a4a0112
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 71 additions and 35 deletions

View file

@ -443,7 +443,7 @@ export class SearchInterceptor {
{
version: '1',
signal: abortSignal,
context: executionContext,
context: this.deps.executionContext.withGlobalContext(executionContext),
body: JSON.stringify({
...request,
...searchOptions,

View file

@ -9,7 +9,7 @@
import type { MockedKeys } from '@kbn/utility-types-jest';
import { from } from 'rxjs';
import { CoreSetup, RequestHandlerContext } from '@kbn/core/server';
import { CoreSetup, type Logger, RequestHandlerContext } from '@kbn/core/server';
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
import { registerSearchRoute } from './search';
import { DataPluginStart } from '../../plugin';
@ -19,13 +19,18 @@ import { KbnSearchError } from '../report_search_error';
describe('Search service', () => {
let mockCoreSetup: MockedKeys<CoreSetup<{}, DataPluginStart>>;
let mockLogger: Logger;
function mockEsError(message: string, statusCode: number, errBody?: Record<string, any>) {
return new KbnSearchError(message, statusCode, errBody);
}
async function runMockSearch(mockContext: any, mockRequest: any, mockResponse: any) {
registerSearchRoute(mockCoreSetup.http.createRouter());
registerSearchRoute(
mockCoreSetup.http.createRouter(),
mockLogger,
mockCoreSetup.executionContext
);
const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value;
const handler = mockRouter.versioned.post.mock.results[0].value.addVersion.mock.calls[0][1];
@ -33,7 +38,11 @@ describe('Search service', () => {
}
async function runMockDelete(mockContext: any, mockRequest: any, mockResponse: any) {
registerSearchRoute(mockCoreSetup.http.createRouter());
registerSearchRoute(
mockCoreSetup.http.createRouter(),
mockLogger,
mockCoreSetup.executionContext
);
const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value;
const handler = mockRouter.versioned.delete.mock.results[0].value.addVersion.mock.calls[0][1];
@ -43,6 +52,7 @@ describe('Search service', () => {
beforeEach(() => {
jest.clearAllMocks();
mockCoreSetup = coreMock.createSetup();
mockLogger = coreMock.createPluginInitializerContext().logger.get();
});
it('handler calls context.search.search with the given request and strategy', async () => {

View file

@ -11,13 +11,21 @@ import { first } from 'rxjs';
import { schema } from '@kbn/config-schema';
import { reportServerError } from '@kbn/kibana-utils-plugin/server';
import { IncomingMessage } from 'http';
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
import { Logger } from '@kbn/logging';
import type { ExecutionContextSetup } from '@kbn/core-execution-context-server';
import apm from 'elastic-apm-node';
import { reportSearchError } from '../report_search_error';
import { getRequestAbortedSignal } from '../../lib';
import type { DataPluginRouter } from '../types';
export const SEARCH_API_BASE_URL = '/internal/search';
export function registerSearchRoute(router: DataPluginRouter): void {
export function registerSearchRoute(
router: DataPluginRouter,
logger: Logger,
executionContextSetup: ExecutionContextSetup
): void {
router.versioned
.post({
path: `${SEARCH_API_BASE_URL}/{strategy}/{id?}`,
@ -65,39 +73,54 @@ export function registerSearchRoute(router: DataPluginRouter): void {
const { strategy, id } = request.params;
const abortSignal = getRequestAbortedSignal(request.events.aborted$);
let executionContext: KibanaExecutionContext | undefined;
const contextHeader = request.headers['x-kbn-context'];
try {
const search = await context.search;
const response = await search
.search(
{ ...searchRequest, id },
{
abortSignal,
strategy,
legacyHitsTotal,
sessionId,
isStored,
isRestore,
retrieveResults,
stream,
}
)
.pipe(first())
.toPromise();
if (response && (response.rawResponse as unknown as IncomingMessage).pipe) {
return res.ok({
body: response.rawResponse,
headers: {
'kbn-search-is-restored': response.isRestored ? '?1' : '?0',
'kbn-search-request-params': JSON.stringify(response.requestParams),
},
});
} else {
return res.ok({ body: response });
if (contextHeader != null) {
executionContext = JSON.parse(
decodeURIComponent(Array.isArray(contextHeader) ? contextHeader[0] : contextHeader)
);
}
} catch (err) {
return reportSearchError(res, err);
logger.error(`Error parsing search execution context: ${contextHeader}`);
}
return executionContextSetup.withContext(executionContext, async () => {
apm.addLabels(executionContextSetup.getAsLabels());
try {
const search = await context.search;
const response = await search
.search(
{ ...searchRequest, id },
{
abortSignal,
strategy,
legacyHitsTotal,
sessionId,
isStored,
isRestore,
retrieveResults,
stream,
}
)
.pipe(first())
.toPromise();
if (response && (response.rawResponse as unknown as IncomingMessage).pipe) {
return res.ok({
body: response.rawResponse,
headers: {
'kbn-search-is-restored': response.isRestored ? '?1' : '?0',
'kbn-search-request-params': JSON.stringify(response.requestParams),
},
});
} else {
return res.ok({ body: response });
}
} catch (err) {
return reportSearchError(res, err);
}
});
}
);

View file

@ -148,7 +148,7 @@ export class SearchService {
const usage = usageCollection ? usageProvider(core) : undefined;
const router = core.http.createRouter<DataRequestHandlerContext>();
registerSearchRoute(router);
registerSearchRoute(router, this.logger, core.executionContext);
registerSessionRoutes(router, this.logger);
this.sessionService.setup(core, {});

View file

@ -55,6 +55,9 @@
"@kbn/esql-utils",
"@kbn/shared-ux-table-persist",
"@kbn/core-deprecations-common",
"@kbn/core-execution-context-common",
"@kbn/logging",
"@kbn/core-execution-context-server",
],
"exclude": [
"target/**/*",