[APM][Transactions] Test trace summary (#207115)

Closes #206947

## Summary

This PR adds tests for trace summary (Otel / APM cases) and changes the
`styled-components` to `css`.
This commit is contained in:
jennypavlova 2025-01-21 09:49:03 +01:00 committed by GitHub
parent 4d539f0f5c
commit 8b97ad0b13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 116 additions and 21 deletions

View file

@ -213,6 +213,7 @@ export type ApmFields = Fields<{
}>;
'url.original': string;
'url.domain': string;
'url.full': string;
}> &
ApmApplicationMetricFields &
ExperimentalFields;

View file

@ -25,6 +25,9 @@ export class Transaction extends BaseSpan {
'processor.event': 'transaction',
'transaction.id': generateShortId(),
'transaction.sampled': true,
'http.request.method': 'GET',
'http.response.status_code': 200,
'url.full': 'elastic.co',
});
}

View file

@ -22,7 +22,6 @@ interface OtelSharedResourceAttributes {
'telemetry.sdk.language'?: string;
'telemetry.sdk.name'?: string;
'telemetry.sdk.version'?: string;
'some.resource.attribute'?: string;
}
export interface OtelDocument extends Fields {
@ -165,6 +164,9 @@ class Otel extends Serializable<OtelDocument> {
'transaction.root': true,
'transaction.sampled': true,
'transaction.type': 'unknown',
'http.request.method': 'POST',
'http.response.status_code': 200,
'url.full': 'elastic.co',
},
data_stream: {
dataset: 'generic.otel',

View file

@ -24,6 +24,9 @@ export interface OtelTransactionDocument extends OtelDocument {
'transaction.root'?: boolean;
'transaction.sampled'?: boolean;
'transaction.type'?: string;
'http.response.status_code'?: number;
'http.request.method'?: string;
'url.full'?: string;
};
status?: {
code?: string;

View file

@ -95,6 +95,8 @@ describe('simple trace', () => {
'container.id': 'instance-1',
'event.outcome': 'success',
'host.name': 'instance-1',
'http.request.method': 'GET',
'http.response.status_code': 200,
'processor.event': 'transaction',
'processor.name': 'transaction',
'service.environment': 'production',
@ -107,6 +109,7 @@ describe('simple trace', () => {
'transaction.name': 'GET /api/product/list',
'transaction.type': 'request',
'transaction.sampled': true,
'url.full': 'elastic.co',
});
});

View file

@ -8,6 +8,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -20,6 +22,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459200050,
@ -89,6 +92,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -101,6 +106,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459260050,
@ -170,6 +176,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -182,6 +190,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459320050,
@ -251,6 +260,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -263,6 +274,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459380050,
@ -332,6 +344,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -344,6 +358,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459440050,
@ -413,6 +428,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -425,6 +442,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459500050,
@ -494,6 +512,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -506,6 +526,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459560050,
@ -575,6 +596,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -587,6 +610,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459620050,
@ -656,6 +680,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -668,6 +694,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459680050,
@ -737,6 +764,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -749,6 +778,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459740050,
@ -818,6 +848,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -830,6 +862,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459800050,
@ -899,6 +932,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -911,6 +946,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459860050,
@ -980,6 +1016,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -992,6 +1030,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459920050,
@ -1061,6 +1100,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -1073,6 +1114,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609459980050,
@ -1142,6 +1184,8 @@ Array [
"container.id": "instance-1",
"event.outcome": "success",
"host.name": "instance-1",
"http.request.method": "GET",
"http.response.status_code": 200,
"processor.event": "transaction",
"processor.name": "transaction",
"service.environment": "production",
@ -1154,6 +1198,7 @@ Array [
"transaction.name": "GET /api/product/list",
"transaction.sampled": true,
"transaction.type": "request",
"url.full": "elastic.co",
},
Object {
"@timestamp": 1609460040050,

View file

@ -20,6 +20,12 @@ const baseUrl = url.format({
query: { rangeFrom: start, rangeTo: end },
});
const transactionTabPath = '/app/apm/services/sendotlp-synth/transactions/view';
const transactionUrl = url.format({
pathname: transactionTabPath,
query: { rangeFrom: start, rangeTo: end, transactionName: 'parent-synth' },
});
describe('Service Overview', () => {
before(() => {
synthtraceOtel.index(
@ -117,6 +123,13 @@ describe('Service Overview', () => {
cy.contains('a', 'parent-synth').click();
cy.contains('h5', 'parent-synth');
});
it('shows transaction summary', () => {
cy.visitKibana(transactionUrl);
cy.getByTestSubj('apmHttpInfoRequestMethod').should('exist');
cy.getByTestSubj('apmHttpInfoUrl').should('exist');
cy.getByTestSubj('apmHttpStatusBadge').should('exist');
});
});
describe('errors', () => {

View file

@ -123,6 +123,21 @@ describe('Transaction details', () => {
cy.url().should('include', 'opbeans-java/errors');
});
describe('Trace sample summary', () => {
it('shows transaction summary', () => {
cy.visitKibana(
`/app/apm/services/opbeans-node/transactions/view?${new URLSearchParams({
...timeRange,
transactionName: 'GET /api/product/:id',
})}`
);
cy.getByTestSubj('apmHttpInfoRequestMethod').should('exist');
cy.getByTestSubj('apmHttpInfoUrl').should('exist');
cy.getByTestSubj('apmHttpStatusBadge').should('exist');
});
});
describe('when navigating to a trace sample', () => {
it('keeps the same trace sample after reloading the page', () => {
cy.visitKibana(

View file

@ -5,21 +5,20 @@
* 2.0.
*/
import { EuiBadge, EuiToolTip } from '@elastic/eui';
import { EuiBadge, EuiToolTip, useEuiTheme } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import styled from '@emotion/styled';
import { truncate, unit } from '../../../../utils/style';
import { css } from '@emotion/react';
import { unit } from '../../../../utils/style';
import { HttpStatusBadge } from '../http_status_badge';
const HttpInfoBadge = styled(EuiBadge)`
margin-right: ${({ theme }) => theme.euiTheme.size.xs};
`;
const Url = styled('span')`
const urlStyles = css`
display: inline-block;
vertical-align: bottom;
${truncate(unit * 24)};
max-width: ${unit * 24}px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
interface HttpInfoProps {
method?: string;
@ -27,11 +26,9 @@ interface HttpInfoProps {
url: string;
}
const Span = styled('span')`
white-space: nowrap;
`;
export function HttpInfoSummaryItem({ status, method, url }: HttpInfoProps) {
const { euiTheme } = useEuiTheme();
if (!url) {
return null;
}
@ -41,18 +38,29 @@ export function HttpInfoSummaryItem({ status, method, url }: HttpInfoProps) {
});
return (
<Span>
<HttpInfoBadge title={undefined}>
<span
css={css`
whitespace: nowrap;
`}
>
<EuiBadge
title={undefined}
css={{
marginRight: `${euiTheme.size.xs}`,
}}
>
{method && (
<EuiToolTip content={methodLabel}>
<>{method.toUpperCase()}</>
<span data-test-subj="apmHttpInfoRequestMethod">{method.toUpperCase()}</span>
</EuiToolTip>
)}{' '}
<EuiToolTip content={url}>
<Url>{url}</Url>
<span data-test-subj="apmHttpInfoUrl" css={urlStyles}>
{url}
</span>
</EuiToolTip>
</HttpInfoBadge>
</EuiBadge>
{status && <HttpStatusBadge status={status} />}
</Span>
</span>
);
}

View file

@ -21,7 +21,9 @@ export function HttpStatusBadge({ status }: HttpStatusBadgeProps) {
return (
<EuiToolTip content={label}>
<EuiBadge color={useGetStatusColor(status) || 'default'}>
{status} {statusCodes[status.toString()]}
<span data-test-subj="apmHttpStatusBadge">
{status} {statusCodes[status.toString()]}
</span>
</EuiBadge>
</EuiToolTip>
);