Skip to content

Commit

Permalink
APM UI changes for serverless services / AWS lambda (#122775)
Browse files Browse the repository at this point in the history
* Adapted service UI for AWS lambda / serverless services

* Add unit tests for isServerlessAgent function

* Add story for cold start badge

* Add unit tests for service icons and icon details

* Add aws_lambda checks to isMetricsTabHidden and isJVMsTabHidden unit tests

* Add API test for coldstart_rate chart

* Change service icon API tests to use synthrace and test for serverless property

* Change service details API tests to use synthrace and add test for serverless

* Add e2e tests for cold start rate chart

* Add cold start badge to transaction flyout

* Add beta badge to cloud details in a lambda context

* Add support for multiple lambda functions in a single service

Co-authored-by: Alexander Wert <alexander.wert@elastic.co>
Co-authored-by: Casper Hübertz <casper@formgeist.com>
  • Loading branch information
3 people authored Jan 31, 2022
1 parent ecba478 commit 78643f4
Show file tree
Hide file tree
Showing 48 changed files with 2,240 additions and 247 deletions.
16 changes: 16 additions & 0 deletions packages/elastic-apm-synthtrace/src/lib/apm/apm_fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ export type ApmFields = Fields &
};
'transaction.sampled': true;
'service.name': string;
'service.version': string;
'service.environment': string;
'service.node.name': string;
'service.runtime.name': string;
'service.runtime.version': string;
'service.framework.name': string;
'span.id': string;
'span.name': string;
'span.type': string;
Expand All @@ -77,5 +81,17 @@ export type ApmFields = Fields &
'span.destination.service.response_time.count': number;
'span.self_time.count': number;
'span.self_time.sum.us': number;
'cloud.provider': string;
'cloud.project.name': string;
'cloud.service.name': string;
'cloud.availability_zone': string;
'cloud.machine.type': string;
'cloud.region': string;
'host.os.platform': string;
'faas.id': string;
'faas.coldstart': boolean;
'faas.execution': string;
'faas.trigger.type': string;
'faas.trigger.request_id': string;
}> &
ApmApplicationMetricFields;
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ export function getTransactionMetrics(events: ApmFields[]) {
'host.name',
'container.id',
'kubernetes.pod.name',
'cloud.account.id',
'cloud.account.name',
'cloud.machine.type',
'cloud.project.id',
'cloud.project.name',
'cloud.service.name',
'service.language.name',
'service.language.version',
'service.runtime.name',
'service.runtime.version',
'host.os.platform',
'faas.id',
'faas.coldstart',
'faas.trigger.type',
]);

return metricsets.map((metricset) => {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 32 additions & 1 deletion x-pack/plugins/apm/common/agent_name.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
* 2.0.
*/

import { isJavaAgentName, isRumAgentName, isIosAgentName } from './agent_name';
import {
isJavaAgentName,
isRumAgentName,
isIosAgentName,
isServerlessAgent,
} from './agent_name';

describe('agent name helpers', () => {
describe('isJavaAgentName', () => {
Expand Down Expand Up @@ -79,4 +84,30 @@ describe('agent name helpers', () => {
});
});
});

describe('isServerlessAgent', () => {
describe('when the runtime name is AWS_LAMBDA', () => {
it('returns true', () => {
expect(isServerlessAgent('AWS_LAMBDA')).toEqual(true);
});
});

describe('when the runtime name is aws_lambda', () => {
it('returns true', () => {
expect(isServerlessAgent('aws_lambda')).toEqual(true);
});
});

describe('when the runtime name is aws_lambda_test', () => {
it('returns true', () => {
expect(isServerlessAgent('aws_lambda_test')).toEqual(true);
});
});

describe('when the runtime name is something else', () => {
it('returns false', () => {
expect(isServerlessAgent('not_aws_lambda')).toEqual(false);
});
});
});
});
4 changes: 4 additions & 0 deletions x-pack/plugins/apm/common/agent_name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,7 @@ export function isIosAgentName(agentName?: string) {
export function isJRubyAgent(agentName?: string, runtimeName?: string) {
return agentName === 'ruby' && runtimeName?.toLowerCase() === 'jruby';
}

export function isServerlessAgent(runtimeName?: string) {
return runtimeName?.toLowerCase().startsWith('aws_lambda');
}
5 changes: 5 additions & 0 deletions x-pack/plugins/apm/common/elasticsearch_fieldnames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const CLOUD_MACHINE_TYPE = 'cloud.machine.type';
export const CLOUD_ACCOUNT_ID = 'cloud.account.id';
export const CLOUD_INSTANCE_ID = 'cloud.instance.id';
export const CLOUD_INSTANCE_NAME = 'cloud.instance.name';
export const CLOUD_SERVICE_NAME = 'cloud.service.name';

export const SERVICE = 'service';
export const SERVICE_NAME = 'service.name';
Expand Down Expand Up @@ -152,3 +153,7 @@ export const PROFILE_ALLOC_OBJECTS = 'profile.alloc_objects.count';
export const PROFILE_ALLOC_SPACE = 'profile.alloc_space.bytes';
export const PROFILE_INUSE_OBJECTS = 'profile.inuse_objects.count';
export const PROFILE_INUSE_SPACE = 'profile.inuse_space.bytes';

export const FAAS_ID = 'faas.id';
export const FAAS_COLDSTART = 'faas.coldstart';
export const FAAS_TRIGGER_TYPE = 'faas.trigger.type';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import url from 'url';
import { synthtrace } from '../../../../../synthtrace';
import { generateData } from './generate_data';

const start = '2021-10-10T00:00:00.000Z';
const end = '2021-10-10T00:15:00.000Z';

const serviceOverviewHref = url.format({
pathname: '/app/apm/services/synth-python/overview',
query: { rangeFrom: start, rangeTo: end },
});

const apiToIntercept = {
endpoint:
'/internal/apm/services/synth-python/transactions/charts/coldstart_rate?*',
name: 'coldStartRequest',
};

describe('Service overview - aws lambda', () => {
before(async () => {
await synthtrace.index(
generateData({
start: new Date(start).getTime(),
end: new Date(end).getTime(),
})
);
});

after(async () => {
await synthtrace.clean();
});

beforeEach(() => {
cy.loginAsReadOnlyUser();
});

it('displays a cold start rate chart and not a transaction breakdown chart', () => {
const { endpoint, name } = apiToIntercept;

cy.intercept('GET', endpoint).as(name);
cy.visit(serviceOverviewHref);
cy.wait(`@${name}`);

cy.contains('Cold start rate');
cy.contains('Time spent by span type').should('not.exist');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { apm, timerange } from '@elastic/apm-synthtrace';

const dataConfig = {
serviceName: 'synth-python',
rate: 10,
transaction: {
name: 'GET /apple 🍎',
duration: 1000,
},
};

export function generateData({ start, end }: { start: number; end: number }) {
const { rate, transaction, serviceName } = dataConfig;
const instance = apm
.service(serviceName, 'production', 'python')
.instance('instance-a');

const traceEvents = timerange(start, end)
.interval('1m')
.rate(rate)
.flatMap((timestamp) => [
...instance
.transaction(transaction.name)
.defaults({
'service.runtime.name': 'AWS_Lambda_python3.8',
'faas.coldstart': true,
})
.timestamp(timestamp)
.duration(transaction.duration)
.success()
.serialize(),
]);

return traceEvents;
}
44 changes: 32 additions & 12 deletions x-pack/plugins/apm/public/components/app/service_overview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { useHistory } from 'react-router-dom';
import { isRumAgentName, isIosAgentName } from '../../../../common/agent_name';
import {
isRumAgentName,
isIosAgentName,
isServerlessAgent,
} from '../../../../common/agent_name';
import { AnnotationsContextProvider } from '../../../context/annotations/annotations_context';
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context';
import { useBreakpoints } from '../../../hooks/use_breakpoints';
import { LatencyChart } from '../../shared/charts/latency_chart';
import { TransactionBreakdownChart } from '../../shared/charts/transaction_breakdown_chart';
import { TransactionColdstartRateChart } from '../../shared/charts/transaction_coldstart_rate_chart';
import { FailedTransactionRateChart } from '../../shared/charts/failed_transaction_rate_chart';
import { ServiceOverviewDependenciesTable } from './service_overview_dependencies_table';
import { ServiceOverviewErrorsTable } from './service_overview_errors_table';
Expand All @@ -35,8 +40,13 @@ import { replace } from '../../shared/links/url_helpers';
export const chartHeight = 288;

export function ServiceOverview() {
const { agentName, serviceName, transactionType, fallbackToTransactions } =
useApmServiceContext();
const {
agentName,
serviceName,
transactionType,
fallbackToTransactions,
runtimeName,
} = useApmServiceContext();
const {
query,
query: {
Expand Down Expand Up @@ -69,7 +79,7 @@ export function ServiceOverview() {
const rowDirection = isSingleColumn ? 'column' : 'row';
const isRumAgent = isRumAgentName(agentName);
const isIosAgent = isIosAgentName(agentName);

const isServerless = isServerlessAgent(runtimeName);
const router = useApmRouter();
const dependenciesLink = router.link('/services/{serviceName}/dependencies', {
path: {
Expand Down Expand Up @@ -152,13 +162,23 @@ export function ServiceOverview() {
gutterSize="s"
responsive={false}
>
<EuiFlexItem grow={3}>
<TransactionBreakdownChart
showAnnotations={false}
environment={environment}
kuery={kuery}
/>
</EuiFlexItem>
{isServerless ? (
<EuiFlexItem grow={3}>
<TransactionColdstartRateChart
showAnnotations={false}
environment={environment}
kuery={kuery}
/>
</EuiFlexItem>
) : (
<EuiFlexItem grow={3}>
<TransactionBreakdownChart
showAnnotations={false}
environment={environment}
kuery={kuery}
/>
</EuiFlexItem>
)}
{!isRumAgent && (
<EuiFlexItem grow={7}>
<EuiPanel hasBorder={true}>
Expand All @@ -180,7 +200,7 @@ export function ServiceOverview() {
)}
</EuiFlexGroup>
</EuiFlexItem>
{!isRumAgent && !isIosAgent && (
{!isRumAgent && !isIosAgent && !isServerless && (
<EuiFlexItem>
<EuiFlexGroup
direction="column"
Expand Down
Loading

0 comments on commit 78643f4

Please sign in to comment.