Skip to content

Commit

Permalink
Add support for multiple lambda functions in a single service
Browse files Browse the repository at this point in the history
  • Loading branch information
gbamparop committed Jan 29, 2022
1 parent 9e9cc76 commit 22fdd59
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 36 deletions.
1 change: 1 addition & 0 deletions packages/elastic-apm-synthtrace/src/lib/apm/apm_fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export type ApmFields = Fields &
'cloud.machine.type': string;
'cloud.region': string;
'host.os.platform': string;
'faas.id': string;
'faas.coldstart': boolean;
'faas.execution': string;
'faas.trigger.type': string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export function getTransactionMetrics(events: ApmFields[]) {
'service.runtime.name',
'service.runtime.version',
'host.os.platform',
'faas.id',
'faas.coldstart',
'faas.trigger.type',
]);
Expand Down

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

1 change: 1 addition & 0 deletions x-pack/plugins/apm/common/elasticsearch_fieldnames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,6 @@ 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
Expand Up @@ -289,7 +289,7 @@ describe('ServiceIcons', () => {
data: {
serverless: {
type: '',
functionName: 'lambda-java-dev',
functionNames: ['lambda-java-dev'],
faasTriggerTypes: ['datasource', 'http'],
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,26 @@ export function ServerlessDetails({ serverless }: Props) {
}

const listItems: EuiDescriptionListProps['listItems'] = [];
if (serverless.functionName) {

if (!!serverless.functionNames?.length) {
listItems.push({
title: i18n.translate(
'xpack.apm.serviceIcons.serviceDetails.cloud.functionNameLabel',
{
defaultMessage: 'Function name',
defaultMessage:
'{functionNames, plural, =0 {Function name} one {Function name} other {Function names}} ',
values: { functionNames: serverless.functionNames.length },
}
),
description: serverless.functionName,
description: (
<ul>
{serverless.functionNames.map((type, index) => (
<li key={index}>
<EuiBadge color="hollow">{type}</EuiBadge>
</li>
))}
</ul>
),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
SERVICE_NAME,
SERVICE_NODE_NAME,
SERVICE_VERSION,
FAAS_ID,
FAAS_TRIGGER_TYPE,
} from '../../../common/elasticsearch_fieldnames';
import { ContainerType } from '../../../common/service_metadata';
Expand Down Expand Up @@ -55,7 +56,7 @@ export interface ServiceMetadataDetails {
};
serverless?: {
type?: string;
functionName?: string;
functionNames?: string[];
faasTriggerTypes?: string[];
};
cloud?: {
Expand Down Expand Up @@ -138,6 +139,12 @@ export async function getServiceMetadataDetails({
size: 10,
},
},
faasFunctionNames: {
terms: {
field: FAAS_ID,
size: 10,
},
},
totalNumberInstances: { cardinality: { field: SERVICE_NODE_NAME } },
},
},
Expand Down Expand Up @@ -185,7 +192,9 @@ export async function getServiceMetadataDetails({
!!response.aggregations?.faasTriggerTypes?.buckets.length && cloud
? {
type: cloud.service?.name,
functionName: service.name,
functionNames: response.aggregations?.faasFunctionNames.buckets
.map((bucket) => getLambdaFunctionNameFromARN(bucket.key as string))
.filter((name) => name),
faasTriggerTypes: response.aggregations?.faasTriggerTypes.buckets.map(
(bucket) => bucket.key as string
),
Expand Down Expand Up @@ -216,3 +225,8 @@ export async function getServiceMetadataDetails({
cloud: cloudDetails,
};
}

function getLambdaFunctionNameFromARN(arn: string) {
// Lambda function ARN example: arn:aws:lambda:us-west-2:123456789012:function:my-function
return arn.split(':')[6] || '';
}
1 change: 1 addition & 0 deletions x-pack/plugins/apm/typings/es_schemas/raw/fields/faas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

export interface Faas {
id: string;
coldstart?: boolean;
execution?: string;
trigger?: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export const dataConfig = {
},
containerOs: 'linux',
serverless: {
firstFunctionName: 'my-function-1',
secondFunctionName: 'my-function-2',
faasTriggerType: 'other',
},
cloud: {
Expand Down Expand Up @@ -58,40 +60,71 @@ export async function generateData({
projectName,
serviceName: cloudServiceName,
} = cloud;
const { faasTriggerType } = serverless;
const { faasTriggerType, firstFunctionName, secondFunctionName } = serverless;
const { version, runtime, framework, agent, name: serviceName } = service;
const { name: serviceRunTimeName, version: serviceRunTimeVersion } = runtime;
const { name: agentName, version: agentVersion } = agent;

const instance = apm.service(serviceName, 'production', agentName).instance('instance-a');

const traceEvents = timerange(start, end)
.interval('30s')
.rate(rate)
.flatMap((timestamp) =>
instance
.transaction(transaction.name)
.timestamp(timestamp)
.defaults({
'cloud.provider': provider,
'cloud.project.name': projectName,
'cloud.service.name': cloudServiceName,
'cloud.availability_zone': availabilityZone,
'cloud.machine.type': machineType,
'cloud.region': region,
'faas.trigger.type': faasTriggerType,
'host.os.platform': containerOs,
'kubernetes.pod.uid': '48f4c5a5-0625-4bea-9d94-77ee94a17e70',
'service.version': version,
'service.runtime.name': serviceRunTimeName,
'service.runtime.version': serviceRunTimeVersion,
'service.framework.name': framework,
'agent.version': agentVersion,
})
.duration(transaction.duration)
.success()
.serialize()
);
const traceEvents = [
...timerange(start, end)
.interval('30s')
.rate(rate)
.flatMap((timestamp) =>
instance
.transaction(transaction.name)
.timestamp(timestamp)
.defaults({
'cloud.provider': provider,
'cloud.project.name': projectName,
'cloud.service.name': cloudServiceName,
'cloud.availability_zone': availabilityZone,
'cloud.machine.type': machineType,
'cloud.region': region,
'faas.id': `arn:aws:lambda:us-west-2:123456789012:function:${firstFunctionName}`,
'faas.trigger.type': faasTriggerType,
'host.os.platform': containerOs,
'kubernetes.pod.uid': '48f4c5a5-0625-4bea-9d94-77ee94a17e70',
'service.version': version,
'service.runtime.name': serviceRunTimeName,
'service.runtime.version': serviceRunTimeVersion,
'service.framework.name': framework,
'agent.version': agentVersion,
})
.duration(transaction.duration)
.success()
.serialize()
),
...timerange(start, end)
.interval('30s')
.rate(rate)
.flatMap((timestamp) =>
instance
.transaction(transaction.name)
.timestamp(timestamp)
.defaults({
'cloud.provider': provider,
'cloud.project.name': projectName,
'cloud.service.name': cloudServiceName,
'cloud.availability_zone': availabilityZone,
'cloud.machine.type': machineType,
'cloud.region': region,
'faas.id': `arn:aws:lambda:us-west-2:123456789012:function:${secondFunctionName}`,
'faas.trigger.type': faasTriggerType,
'host.os.platform': containerOs,
'kubernetes.pod.uid': '48f4c5a5-0625-4bea-9d94-77ee94a17e70',
'service.version': version,
'service.runtime.name': serviceRunTimeName,
'service.runtime.version': serviceRunTimeVersion,
'service.framework.name': framework,
'agent.version': agentVersion,
})
.duration(transaction.duration)
.success()
.serialize()
),
];

await synthtraceEsClient.index(traceEvents);
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,12 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns correct serverless details', () => {
const { cloud, serverless } = dataConfig;
const { serviceName: cloudServiceName } = cloud;
const { faasTriggerType } = serverless;
const { faasTriggerType, firstFunctionName, secondFunctionName } = serverless;

expect(body?.serverless?.type).to.be(cloudServiceName);
expect(body?.serverless?.functionName).to.be(serviceName);
expect(body?.serverless?.functionNames).to.have.length(2);
expect(body?.serverless?.functionNames).to.contain(firstFunctionName);
expect(body?.serverless?.functionNames).to.contain(secondFunctionName);
expect(first(body?.serverless?.faasTriggerTypes)).to.be(faasTriggerType);
});

Expand Down

0 comments on commit 22fdd59

Please sign in to comment.