[APM] Add missing trace_id to transaction histogram (#27591)
* [APM] Add missing trace_id to transaction histogram * Handle 404 properly * Fix `unhandled` issue * Fix tests
This commit is contained in:
parent
c72dd3f2d9
commit
c7bd296ac4
15 changed files with 100 additions and 89 deletions
|
@ -34,7 +34,7 @@ describe('StickyProperties', () => {
|
||||||
{
|
{
|
||||||
label: 'Handled',
|
label: 'Handled',
|
||||||
fieldName: 'error.exception.handled',
|
fieldName: 'error.exception.handled',
|
||||||
val: 'true'
|
val: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'User ID',
|
label: 'User ID',
|
||||||
|
|
|
@ -101,7 +101,7 @@ function getPropertyValue({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <PropertyValue>{val}</PropertyValue>;
|
return <PropertyValue>{String(val)}</PropertyValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StickyProperties({
|
export function StickyProperties({
|
||||||
|
|
|
@ -40,6 +40,7 @@ export async function loadTransactionDistribution({
|
||||||
transactionName,
|
transactionName,
|
||||||
transactionType = 'request',
|
transactionType = 'request',
|
||||||
transactionId,
|
transactionId,
|
||||||
|
traceId,
|
||||||
kuery
|
kuery
|
||||||
}: Required<IUrlParams>) {
|
}: Required<IUrlParams>) {
|
||||||
return callApi<ITransactionDistributionAPIResponse>({
|
return callApi<ITransactionDistributionAPIResponse>({
|
||||||
|
@ -50,6 +51,7 @@ export async function loadTransactionDistribution({
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
transactionId,
|
transactionId,
|
||||||
|
traceId,
|
||||||
esFilterQuery: await getEncodedEsQuery(kuery)
|
esFilterQuery: await getEncodedEsQuery(kuery)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { KFetchError } from 'ui/kfetch/kfetch_error';
|
||||||
import { TransactionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/get_transaction';
|
import { TransactionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/get_transaction';
|
||||||
import { SpanListAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/spans/get_spans';
|
import { SpanListAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/spans/get_spans';
|
||||||
import { Span } from 'x-pack/plugins/apm/typings/es_schemas/Span';
|
import { Span } from 'x-pack/plugins/apm/typings/es_schemas/Span';
|
||||||
|
@ -43,20 +44,31 @@ export async function loadTransaction({
|
||||||
traceId,
|
traceId,
|
||||||
kuery
|
kuery
|
||||||
}: IUrlParams) {
|
}: IUrlParams) {
|
||||||
const result = await callApi<TransactionAPIResponse>(
|
try {
|
||||||
{
|
const result = await callApi<TransactionAPIResponse>(
|
||||||
pathname: `/api/apm/services/${serviceName}/transactions/${transactionId}`,
|
{
|
||||||
query: {
|
pathname: `/api/apm/services/${serviceName}/transactions/${transactionId}`,
|
||||||
traceId,
|
query: {
|
||||||
start,
|
traceId,
|
||||||
end,
|
start,
|
||||||
esFilterQuery: await getEncodedEsQuery(kuery)
|
end,
|
||||||
|
esFilterQuery: await getEncodedEsQuery(kuery)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
camelcase: false
|
||||||
}
|
}
|
||||||
},
|
);
|
||||||
{
|
return addVersion(result);
|
||||||
camelcase: false
|
} catch (e) {
|
||||||
}
|
const err: KFetchError = e;
|
||||||
);
|
|
||||||
|
|
||||||
return addVersion(result);
|
// swallow 404 errors
|
||||||
|
if (err.res.status === 404) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-throw all other errors
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ export function TransactionDistributionRequest({
|
||||||
serviceName,
|
serviceName,
|
||||||
transactionType,
|
transactionType,
|
||||||
transactionId,
|
transactionId,
|
||||||
|
traceId,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
transactionName,
|
transactionName,
|
||||||
|
@ -66,6 +67,7 @@ export function TransactionDistributionRequest({
|
||||||
serviceName,
|
serviceName,
|
||||||
transactionType,
|
transactionType,
|
||||||
transactionId,
|
transactionId,
|
||||||
|
traceId,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
transactionName,
|
transactionName,
|
||||||
|
|
|
@ -45,6 +45,7 @@ export function bucketFetcher(
|
||||||
transactionName: string,
|
transactionName: string,
|
||||||
transactionType: string,
|
transactionType: string,
|
||||||
transactionId: string,
|
transactionId: string,
|
||||||
|
traceId: string,
|
||||||
bucketSize: number,
|
bucketSize: number,
|
||||||
setup: Setup
|
setup: Setup
|
||||||
): Promise<ESResponse> {
|
): Promise<ESResponse> {
|
||||||
|
@ -77,6 +78,7 @@ export function bucketFetcher(
|
||||||
bool: {
|
bool: {
|
||||||
filter,
|
filter,
|
||||||
should: [
|
should: [
|
||||||
|
{ term: { [TRACE_ID]: traceId } },
|
||||||
{ term: { [TRANSACTION_ID]: transactionId } },
|
{ term: { [TRANSACTION_ID]: transactionId } },
|
||||||
{ term: { [TRANSACTION_SAMPLED]: true } }
|
{ term: { [TRANSACTION_SAMPLED]: true } }
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,6 +13,7 @@ export async function getBuckets(
|
||||||
transactionName: string,
|
transactionName: string,
|
||||||
transactionType: string,
|
transactionType: string,
|
||||||
transactionId: string,
|
transactionId: string,
|
||||||
|
traceId: string,
|
||||||
bucketSize: number,
|
bucketSize: number,
|
||||||
setup: Setup
|
setup: Setup
|
||||||
) {
|
) {
|
||||||
|
@ -21,6 +22,7 @@ export async function getBuckets(
|
||||||
transactionName,
|
transactionName,
|
||||||
transactionType,
|
transactionType,
|
||||||
transactionId,
|
transactionId,
|
||||||
|
traceId,
|
||||||
bucketSize,
|
bucketSize,
|
||||||
setup
|
setup
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,6 +21,7 @@ export async function getDistribution(
|
||||||
transactionName: string,
|
transactionName: string,
|
||||||
transactionType: string,
|
transactionType: string,
|
||||||
transactionId: string,
|
transactionId: string,
|
||||||
|
traceId: string,
|
||||||
setup: Setup
|
setup: Setup
|
||||||
): Promise<ITransactionDistributionAPIResponse> {
|
): Promise<ITransactionDistributionAPIResponse> {
|
||||||
const bucketSize = await calculateBucketSize(
|
const bucketSize = await calculateBucketSize(
|
||||||
|
@ -35,6 +36,7 @@ export async function getDistribution(
|
||||||
transactionName,
|
transactionName,
|
||||||
transactionType,
|
transactionType,
|
||||||
transactionId,
|
transactionId,
|
||||||
|
traceId,
|
||||||
bucketSize,
|
bucketSize,
|
||||||
setup
|
setup
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { SearchParams } from 'elasticsearch';
|
import { ESFilter } from 'elasticsearch';
|
||||||
import { oc } from 'ts-optchain';
|
import { oc } from 'ts-optchain';
|
||||||
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
|
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
|
||||||
import {
|
import {
|
||||||
|
@ -23,38 +23,40 @@ export async function getTransaction(
|
||||||
): Promise<TransactionAPIResponse> {
|
): Promise<TransactionAPIResponse> {
|
||||||
const { start, end, esFilterQuery, client, config } = setup;
|
const { start, end, esFilterQuery, client, config } = setup;
|
||||||
|
|
||||||
const params: SearchParams = {
|
const filter: ESFilter[] = [
|
||||||
index: config.get('apm_oss.transactionIndices'),
|
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
|
||||||
|
{ term: { [TRANSACTION_ID]: transactionId } },
|
||||||
|
{
|
||||||
|
range: {
|
||||||
|
'@timestamp': {
|
||||||
|
gte: start,
|
||||||
|
lte: end,
|
||||||
|
format: 'epoch_millis'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (esFilterQuery) {
|
||||||
|
filter.push(esFilterQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traceId) {
|
||||||
|
filter.push({ term: { [TRACE_ID]: traceId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
index: config.get<string>('apm_oss.transactionIndices'),
|
||||||
body: {
|
body: {
|
||||||
size: 1,
|
size: 1,
|
||||||
query: {
|
query: {
|
||||||
bool: {
|
bool: {
|
||||||
filter: [
|
filter
|
||||||
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
|
|
||||||
{ term: { [TRANSACTION_ID]: transactionId } },
|
|
||||||
{
|
|
||||||
range: {
|
|
||||||
'@timestamp': {
|
|
||||||
gte: start,
|
|
||||||
lte: end,
|
|
||||||
format: 'epoch_millis'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (esFilterQuery) {
|
|
||||||
params.body.query.bool.filter.push(esFilterQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (traceId) {
|
|
||||||
params.body.query.bool.filter.push({ term: { [TRACE_ID]: traceId } });
|
|
||||||
}
|
|
||||||
|
|
||||||
const resp = await client<Transaction>('search', params);
|
const resp = await client<Transaction>('search', params);
|
||||||
return oc(resp).hits.hits[0]._source();
|
return oc(resp).hits.hits[0]._source();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { Setup } from '../../helpers/setup_request';
|
||||||
|
|
||||||
export type SpanListAPIResponse = Span[];
|
export type SpanListAPIResponse = Span[];
|
||||||
|
|
||||||
|
// Deprecated and will be removed in 7.0. Only needed for backwards compatability pre 6.5 (introducition of v2 API and distributed tracing)
|
||||||
export async function getSpans(
|
export async function getSpans(
|
||||||
transactionId: string,
|
transactionId: string,
|
||||||
setup: Setup
|
setup: Setup
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { initServicesApi } from '../services';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { initStatusApi } from '../status_check';
|
import { initStatusApi } from '../status_check';
|
||||||
import { initTracesApi } from '../traces';
|
import { initTracesApi } from '../traces';
|
||||||
import { initTransactionsApi } from '../transactions';
|
|
||||||
|
|
||||||
describe('route handlers should fail with a Boom error', () => {
|
describe('route handlers should fail with a Boom error', () => {
|
||||||
let consoleErrorSpy: any;
|
let consoleErrorSpy: any;
|
||||||
|
@ -76,8 +75,4 @@ describe('route handlers should fail with a Boom error', () => {
|
||||||
describe('trace routes', async () => {
|
describe('trace routes', async () => {
|
||||||
await testRouteFailures(initTracesApi);
|
await testRouteFailures(initTracesApi);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('transaction routes', async () => {
|
|
||||||
await testRouteFailures(initTransactionsApi);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -109,19 +109,24 @@ export function initTransactionGroupsApi(server: Server) {
|
||||||
options: {
|
options: {
|
||||||
validate: {
|
validate: {
|
||||||
query: withDefaultValidators({
|
query: withDefaultValidators({
|
||||||
transactionId: Joi.string().default('')
|
transactionId: Joi.string().default(''),
|
||||||
|
traceId: Joi.string().default('')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: req => {
|
handler: req => {
|
||||||
const setup = setupRequest(req);
|
const setup = setupRequest(req);
|
||||||
const { serviceName, transactionType, transactionName } = req.params;
|
const { serviceName, transactionType, transactionName } = req.params;
|
||||||
const { transactionId } = req.query as { transactionId: string };
|
const { transactionId, traceId } = req.query as {
|
||||||
|
transactionId: string;
|
||||||
|
traceId: string;
|
||||||
|
};
|
||||||
return getDistribution(
|
return getDistribution(
|
||||||
serviceName,
|
serviceName,
|
||||||
transactionName,
|
transactionName,
|
||||||
transactionType,
|
transactionType,
|
||||||
transactionId,
|
transactionId,
|
||||||
|
traceId,
|
||||||
setup
|
setup
|
||||||
).catch(defaultErrorHandler);
|
).catch(defaultErrorHandler);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,6 @@ import { setupRequest } from '../lib/helpers/setup_request';
|
||||||
import { getTransaction } from '../lib/transactions/get_transaction';
|
import { getTransaction } from '../lib/transactions/get_transaction';
|
||||||
import { getSpans } from '../lib/transactions/spans/get_spans';
|
import { getSpans } from '../lib/transactions/spans/get_spans';
|
||||||
|
|
||||||
const defaultErrorHandler = (err: Error) => {
|
|
||||||
// tslint:disable-next-line
|
|
||||||
console.error(err.stack);
|
|
||||||
throw Boom.boomify(err, { statusCode: 400 });
|
|
||||||
};
|
|
||||||
|
|
||||||
export function initTransactionsApi(server: Server) {
|
export function initTransactionsApi(server: Server) {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -29,13 +23,16 @@ export function initTransactionsApi(server: Server) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: req => {
|
handler: async req => {
|
||||||
const { transactionId } = req.params;
|
const { transactionId } = req.params;
|
||||||
const { traceId } = req.query as { traceId: string };
|
const { traceId } = req.query as { traceId: string };
|
||||||
const setup = setupRequest(req);
|
const setup = setupRequest(req);
|
||||||
return getTransaction(transactionId, traceId, setup).catch(
|
const transaction = await getTransaction(transactionId, traceId, setup);
|
||||||
defaultErrorHandler
|
if (transaction) {
|
||||||
);
|
return transaction;
|
||||||
|
} else {
|
||||||
|
throw Boom.notFound('Cannot find the requested page');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,7 +48,7 @@ export function initTransactionsApi(server: Server) {
|
||||||
handler: req => {
|
handler: req => {
|
||||||
const { transactionId } = req.params;
|
const { transactionId } = req.params;
|
||||||
const setup = setupRequest(req);
|
const setup = setupRequest(req);
|
||||||
return getSpans(transactionId, setup).catch(defaultErrorHandler);
|
return getSpans(transactionId, setup);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,35 +82,27 @@ uiModule.directive('monitoringMain', (breadcrumbs, license, kbnUrl, config) => {
|
||||||
link(scope, _element, attributes, controller) {
|
link(scope, _element, attributes, controller) {
|
||||||
config.watch('k7design', (val) => scope.showPluginBreadcrumbs = !val);
|
config.watch('k7design', (val) => scope.showPluginBreadcrumbs = !val);
|
||||||
|
|
||||||
function getSetupObj() {
|
controller.setup({
|
||||||
return {
|
licenseService: license,
|
||||||
licenseService: license,
|
breadcrumbsService: breadcrumbs,
|
||||||
breadcrumbsService: breadcrumbs,
|
kbnUrlService: kbnUrl,
|
||||||
kbnUrlService: kbnUrl,
|
attributes: {
|
||||||
attributes: {
|
name: attributes.name,
|
||||||
name: attributes.name,
|
product: attributes.product,
|
||||||
product: attributes.product,
|
instance: attributes.instance,
|
||||||
instance: attributes.instance,
|
resolver: attributes.resolver,
|
||||||
resolver: attributes.resolver,
|
page: attributes.page,
|
||||||
page: attributes.page,
|
tabIconClass: attributes.tabIconClass,
|
||||||
tabIconClass: attributes.tabIconClass,
|
tabIconLabel: attributes.tabIconLabel,
|
||||||
tabIconLabel: attributes.tabIconLabel,
|
pipelineId: attributes.pipelineId,
|
||||||
pipelineId: attributes.pipelineId,
|
pipelineHash: attributes.pipelineHash,
|
||||||
pipelineHash: attributes.pipelineHash,
|
pipelineVersions: get(scope, 'pageData.versions')
|
||||||
pipelineVersions: get(scope, 'pageData.versions')
|
},
|
||||||
},
|
clusterName: get(scope, 'cluster.cluster_name')
|
||||||
clusterName: get(scope, 'cluster.cluster_name')
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const setupObj = getSetupObj();
|
attributes.$observe('instance', instance => controller.instance = instance);
|
||||||
controller.setup(setupObj);
|
attributes.$observe('resolver', resolver => controller.resolver = resolver);
|
||||||
Object.keys(setupObj.attributes).forEach(key => {
|
|
||||||
attributes.$observe(key, () => controller.setup(getSetupObj()));
|
|
||||||
});
|
|
||||||
scope.$watch('pageData.versions', versions => {
|
|
||||||
controller.pipelineVersions = versions;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -98,9 +98,6 @@ uiRoutes.when('/logstash/pipelines/:id/:hash?', {
|
||||||
getPageData,
|
getPageData,
|
||||||
reactNodeId: 'monitoringLogstashPipelineApp',
|
reactNodeId: 'monitoringLogstashPipelineApp',
|
||||||
$scope,
|
$scope,
|
||||||
options: {
|
|
||||||
enableTimeFilter: false,
|
|
||||||
},
|
|
||||||
$injector
|
$injector
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue