[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:
Søren Louv-Jansen 2018-12-21 14:21:07 +01:00 committed by GitHub
parent c72dd3f2d9
commit c7bd296ac4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 100 additions and 89 deletions

View file

@ -34,7 +34,7 @@ describe('StickyProperties', () => {
{
label: 'Handled',
fieldName: 'error.exception.handled',
val: 'true'
val: true
},
{
label: 'User ID',

View file

@ -101,7 +101,7 @@ function getPropertyValue({
);
}
return <PropertyValue>{val}</PropertyValue>;
return <PropertyValue>{String(val)}</PropertyValue>;
}
export function StickyProperties({

View file

@ -40,6 +40,7 @@ export async function loadTransactionDistribution({
transactionName,
transactionType = 'request',
transactionId,
traceId,
kuery
}: Required<IUrlParams>) {
return callApi<ITransactionDistributionAPIResponse>({
@ -50,6 +51,7 @@ export async function loadTransactionDistribution({
start,
end,
transactionId,
traceId,
esFilterQuery: await getEncodedEsQuery(kuery)
}
});

View file

@ -4,6 +4,7 @@
* 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 { SpanListAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/spans/get_spans';
import { Span } from 'x-pack/plugins/apm/typings/es_schemas/Span';
@ -43,20 +44,31 @@ export async function loadTransaction({
traceId,
kuery
}: IUrlParams) {
const result = await callApi<TransactionAPIResponse>(
{
pathname: `/api/apm/services/${serviceName}/transactions/${transactionId}`,
query: {
traceId,
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
try {
const result = await callApi<TransactionAPIResponse>(
{
pathname: `/api/apm/services/${serviceName}/transactions/${transactionId}`,
query: {
traceId,
start,
end,
esFilterQuery: await getEncodedEsQuery(kuery)
}
},
{
camelcase: false
}
},
{
camelcase: false
}
);
);
return addVersion(result);
} 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;
}
}

View file

@ -47,6 +47,7 @@ export function TransactionDistributionRequest({
serviceName,
transactionType,
transactionId,
traceId,
start,
end,
transactionName,
@ -66,6 +67,7 @@ export function TransactionDistributionRequest({
serviceName,
transactionType,
transactionId,
traceId,
start,
end,
transactionName,

View file

@ -45,6 +45,7 @@ export function bucketFetcher(
transactionName: string,
transactionType: string,
transactionId: string,
traceId: string,
bucketSize: number,
setup: Setup
): Promise<ESResponse> {
@ -77,6 +78,7 @@ export function bucketFetcher(
bool: {
filter,
should: [
{ term: { [TRACE_ID]: traceId } },
{ term: { [TRANSACTION_ID]: transactionId } },
{ term: { [TRANSACTION_SAMPLED]: true } }
]

View file

@ -13,6 +13,7 @@ export async function getBuckets(
transactionName: string,
transactionType: string,
transactionId: string,
traceId: string,
bucketSize: number,
setup: Setup
) {
@ -21,6 +22,7 @@ export async function getBuckets(
transactionName,
transactionType,
transactionId,
traceId,
bucketSize,
setup
);

View file

@ -21,6 +21,7 @@ export async function getDistribution(
transactionName: string,
transactionType: string,
transactionId: string,
traceId: string,
setup: Setup
): Promise<ITransactionDistributionAPIResponse> {
const bucketSize = await calculateBucketSize(
@ -35,6 +36,7 @@ export async function getDistribution(
transactionName,
transactionType,
transactionId,
traceId,
bucketSize,
setup
);

View file

@ -4,7 +4,7 @@
* 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 { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import {
@ -23,38 +23,40 @@ export async function getTransaction(
): Promise<TransactionAPIResponse> {
const { start, end, esFilterQuery, client, config } = setup;
const params: SearchParams = {
index: config.get('apm_oss.transactionIndices'),
const filter: ESFilter[] = [
{ 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: {
size: 1,
query: {
bool: {
filter: [
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
{ term: { [TRANSACTION_ID]: transactionId } },
{
range: {
'@timestamp': {
gte: start,
lte: end,
format: 'epoch_millis'
}
}
}
]
filter
}
}
}
};
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);
return oc(resp).hits.hits[0]._source();
}

View file

@ -14,6 +14,7 @@ import { Setup } from '../../helpers/setup_request';
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(
transactionId: string,
setup: Setup

View file

@ -12,7 +12,6 @@ import { initServicesApi } from '../services';
// @ts-ignore
import { initStatusApi } from '../status_check';
import { initTracesApi } from '../traces';
import { initTransactionsApi } from '../transactions';
describe('route handlers should fail with a Boom error', () => {
let consoleErrorSpy: any;
@ -76,8 +75,4 @@ describe('route handlers should fail with a Boom error', () => {
describe('trace routes', async () => {
await testRouteFailures(initTracesApi);
});
describe('transaction routes', async () => {
await testRouteFailures(initTransactionsApi);
});
});

View file

@ -109,19 +109,24 @@ export function initTransactionGroupsApi(server: Server) {
options: {
validate: {
query: withDefaultValidators({
transactionId: Joi.string().default('')
transactionId: Joi.string().default(''),
traceId: Joi.string().default('')
})
}
},
handler: req => {
const setup = setupRequest(req);
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(
serviceName,
transactionName,
transactionType,
transactionId,
traceId,
setup
).catch(defaultErrorHandler);
}

View file

@ -12,12 +12,6 @@ import { setupRequest } from '../lib/helpers/setup_request';
import { getTransaction } from '../lib/transactions/get_transaction';
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) {
server.route({
method: 'GET',
@ -29,13 +23,16 @@ export function initTransactionsApi(server: Server) {
})
}
},
handler: req => {
handler: async req => {
const { transactionId } = req.params;
const { traceId } = req.query as { traceId: string };
const setup = setupRequest(req);
return getTransaction(transactionId, traceId, setup).catch(
defaultErrorHandler
);
const transaction = await getTransaction(transactionId, traceId, setup);
if (transaction) {
return transaction;
} else {
throw Boom.notFound('Cannot find the requested page');
}
}
});
@ -51,7 +48,7 @@ export function initTransactionsApi(server: Server) {
handler: req => {
const { transactionId } = req.params;
const setup = setupRequest(req);
return getSpans(transactionId, setup).catch(defaultErrorHandler);
return getSpans(transactionId, setup);
}
});
}

View file

@ -82,35 +82,27 @@ uiModule.directive('monitoringMain', (breadcrumbs, license, kbnUrl, config) => {
link(scope, _element, attributes, controller) {
config.watch('k7design', (val) => scope.showPluginBreadcrumbs = !val);
function getSetupObj() {
return {
licenseService: license,
breadcrumbsService: breadcrumbs,
kbnUrlService: kbnUrl,
attributes: {
name: attributes.name,
product: attributes.product,
instance: attributes.instance,
resolver: attributes.resolver,
page: attributes.page,
tabIconClass: attributes.tabIconClass,
tabIconLabel: attributes.tabIconLabel,
pipelineId: attributes.pipelineId,
pipelineHash: attributes.pipelineHash,
pipelineVersions: get(scope, 'pageData.versions')
},
clusterName: get(scope, 'cluster.cluster_name')
};
}
controller.setup({
licenseService: license,
breadcrumbsService: breadcrumbs,
kbnUrlService: kbnUrl,
attributes: {
name: attributes.name,
product: attributes.product,
instance: attributes.instance,
resolver: attributes.resolver,
page: attributes.page,
tabIconClass: attributes.tabIconClass,
tabIconLabel: attributes.tabIconLabel,
pipelineId: attributes.pipelineId,
pipelineHash: attributes.pipelineHash,
pipelineVersions: get(scope, 'pageData.versions')
},
clusterName: get(scope, 'cluster.cluster_name')
});
const setupObj = getSetupObj();
controller.setup(setupObj);
Object.keys(setupObj.attributes).forEach(key => {
attributes.$observe(key, () => controller.setup(getSetupObj()));
});
scope.$watch('pageData.versions', versions => {
controller.pipelineVersions = versions;
});
attributes.$observe('instance', instance => controller.instance = instance);
attributes.$observe('resolver', resolver => controller.resolver = resolver);
}
};
});

View file

@ -98,9 +98,6 @@ uiRoutes.when('/logstash/pipelines/:id/:hash?', {
getPageData,
reactNodeId: 'monitoringLogstashPipelineApp',
$scope,
options: {
enableTimeFilter: false,
},
$injector
});