Skip to content

Commit 39cf372

Browse files
committed
In AWS Lambda instrumentation, extend invocation span start time back to Lambda environment startup time on coldstart
1 parent 5eb61d8 commit 39cf372

File tree

2 files changed

+40
-6
lines changed

2 files changed

+40
-6
lines changed

plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ export class AwsLambdaInstrumentation extends InstrumentationBase<AwsLambdaInstr
232232
const span = plugin.tracer.startSpan(
233233
name,
234234
{
235+
// For coldstarts,
236+
// use Lambda start time as invocation span start time to take initialization time into account.
237+
startTime: requestIsColdStart ? lambdaStartTime : Date.now(),
235238
kind: SpanKind.SERVER,
236239
attributes: {
237240
[SEMATTRS_FAAS_EXECUTION]: context.awsRequestId,

plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.test.ts

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ import {
5151
defaultTextMapGetter,
5252
} from '@opentelemetry/api';
5353
import { AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray';
54-
import { W3CTraceContextPropagator } from '@opentelemetry/core';
54+
import {
55+
hrTimeToMilliseconds,
56+
W3CTraceContextPropagator,
57+
} from '@opentelemetry/core';
5558
import { AWSXRayLambdaPropagator } from '@opentelemetry/propagator-aws-xray-lambda';
5659

5760
const memoryExporter = new InMemorySpanExporter();
@@ -266,10 +269,15 @@ describe('lambda handler', () => {
266269
});
267270

268271
it('should record coldstart', async () => {
269-
initializeHandler('lambda-test/sync.handler');
272+
const time0 = Date.now();
273+
initializeHandler('lambda-test/sync.handler', { lambdaStartTime: time0 });
274+
275+
// Simulate initialization time
276+
await new Promise(resolve => setTimeout(resolve, 10));
270277

271278
const handlerModule = lambdaRequire('lambda-test/sync');
272279

280+
const time1 = Date.now();
273281
const result1 = await new Promise((resolve, reject) => {
274282
handlerModule.handler('arg', ctx, (err: Error, res: any) => {
275283
if (err) {
@@ -280,6 +288,7 @@ describe('lambda handler', () => {
280288
});
281289
});
282290

291+
const time2 = Date.now();
283292
const result2 = await new Promise((resolve, reject) => {
284293
handlerModule.handler('arg', ctx, (err: Error, res: any) => {
285294
if (err) {
@@ -298,18 +307,28 @@ describe('lambda handler', () => {
298307
assertSpanSuccess(span1);
299308
assert.strictEqual(span1.parentSpanId, undefined);
300309
assert.strictEqual(span1.attributes[SEMATTRS_FAAS_COLDSTART], true);
310+
// Since it is coldstart, invocation span start time should be equal to lambda start time
311+
assert.strictEqual(hrTimeToMilliseconds(span1.startTime), time0);
312+
// Since it is coldstart, invocation span start time should be before actual handler call time
313+
assert.ok(hrTimeToMilliseconds(span1.startTime) < time1);
301314

302315
assert.strictEqual(result2, 'ok');
303316
assertSpanSuccess(span2);
304317
assert.strictEqual(span2.parentSpanId, undefined);
305318
assert.strictEqual(span2.attributes[SEMATTRS_FAAS_COLDSTART], false);
319+
// Since it is warm invocation, invocation span start time should be after than lambda start time
320+
assert.ok(hrTimeToMilliseconds(span2.startTime) > time0);
321+
// Since it is warm invocation, invocation span start time should be equal or after than handler call time
322+
assert.ok(hrTimeToMilliseconds(span2.startTime) >= time2);
306323
});
307324

308325
it('should record coldstart with provisioned concurrency', async () => {
309326
process.env.AWS_LAMBDA_INITIALIZATION_TYPE = 'provisioned-concurrency';
310327

311-
initializeHandler('lambda-test/sync.handler');
328+
const time0 = Date.now() - 1;
329+
initializeHandler('lambda-test/sync.handler', { lambdaStartTime: time0 });
312330

331+
const time1 = Date.now();
313332
const result = await new Promise((resolve, reject) => {
314333
lambdaRequire('lambda-test/sync').handler(
315334
'arg',
@@ -323,20 +342,26 @@ describe('lambda handler', () => {
323342
}
324343
);
325344
});
345+
326346
assert.strictEqual(result, 'ok');
327347
const spans = memoryExporter.getFinishedSpans();
328348
const [span] = spans;
349+
329350
assert.strictEqual(spans.length, 1);
330351
assertSpanSuccess(span);
331352
assert.strictEqual(span.parentSpanId, undefined);
332353
assert.strictEqual(span.attributes[SEMATTRS_FAAS_COLDSTART], false);
354+
// Since it is warm invocation, invocation span start time should be after than lambda start time
355+
assert.ok(hrTimeToMilliseconds(span.startTime) > time0);
356+
// Since it is warm invocation, invocation span start time should be equal or after than handler call time
357+
assert.ok(hrTimeToMilliseconds(span.startTime) >= time1);
333358
});
334359

335360
it('should record coldstart with proactive initialization', async () => {
336-
initializeHandler('lambda-test/sync.handler', {
337-
lambdaStartTime: Date.now() - 2 * lambdaMaxInitInMilliseconds,
338-
});
361+
const time0 = Date.now() - 2 * lambdaMaxInitInMilliseconds;
362+
initializeHandler('lambda-test/sync.handler', { lambdaStartTime: time0 });
339363

364+
const time1 = Date.now();
340365
const result = await new Promise((resolve, reject) => {
341366
lambdaRequire('lambda-test/sync').handler(
342367
'arg',
@@ -350,13 +375,19 @@ describe('lambda handler', () => {
350375
}
351376
);
352377
});
378+
353379
assert.strictEqual(result, 'ok');
354380
const spans = memoryExporter.getFinishedSpans();
355381
const [span] = spans;
382+
356383
assert.strictEqual(spans.length, 1);
357384
assertSpanSuccess(span);
358385
assert.strictEqual(span.parentSpanId, undefined);
359386
assert.strictEqual(span.attributes[SEMATTRS_FAAS_COLDSTART], false);
387+
// Since it is warm invocation, invocation span start time should be after than lambda start time
388+
assert.ok(hrTimeToMilliseconds(span.startTime) > time0);
389+
// Since it is warm invocation, invocation span start time should be equal or after than handler call time
390+
assert.ok(hrTimeToMilliseconds(span.startTime) >= time1);
360391
});
361392

362393
it('should record error', async () => {

0 commit comments

Comments
 (0)