Skip to content

Commit 970b9fc

Browse files
committed
Propagate Lambda ResourceId or FaasId for the child spans
1 parent 2907367 commit 970b9fc

File tree

4 files changed

+96
-0
lines changed

4 files changed

+96
-0
lines changed

aws-distro-opentelemetry-node-autoinstrumentation/src/attribute-propagating-span-processor.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Span as APISpan, AttributeValue, Context, SpanKind, trace } from '@open
55
import { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';
66
import { AWS_ATTRIBUTE_KEYS } from './aws-attribute-keys';
77
import { AwsSpanProcessingUtil } from './aws-span-processing-util';
8+
import { SEMRESATTRS_FAAS_ID } from '@opentelemetry/semantic-conventions';
89

910
/**
1011
* AttributePropagatingSpanProcessor handles the propagation of attributes from parent spans to
@@ -85,6 +86,18 @@ export class AttributePropagatingSpanProcessor implements SpanProcessor {
8586
if (this.isConsumerKind(span) && this.isConsumerKind(parentReadableSpan)) {
8687
span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_CONSUMER_PARENT_SPAN_KIND, SpanKind[parentReadableSpan.kind]);
8788
}
89+
90+
// If parent span contains "cloud.resource_id" or "faas.id" but not in child span, child span will be
91+
// propagated with one of these attribute from parent. "cloud.resource_id" takes priority if it exists
92+
const parentResourceId = AwsSpanProcessingUtil.getResourceId(parentSpan);
93+
const resourceId = AwsSpanProcessingUtil.getResourceId(span);
94+
if (!resourceId && parentResourceId) {
95+
if (AwsSpanProcessingUtil.isKeyPresent(parentSpan, AwsSpanProcessingUtil.CLOUD_RESOURCE_ID)) {
96+
span.setAttribute(AwsSpanProcessingUtil.CLOUD_RESOURCE_ID, parentResourceId);
97+
} else {
98+
span.setAttribute(SEMRESATTRS_FAAS_ID, parentResourceId);
99+
}
100+
}
88101
}
89102

90103
let propagationData: AttributeValue | undefined = undefined;

aws-distro-opentelemetry-node-autoinstrumentation/src/aws-span-processing-util.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
SEMATTRS_HTTP_URL,
1616
SEMATTRS_MESSAGING_OPERATION,
1717
SEMATTRS_RPC_SYSTEM,
18+
SEMRESATTRS_FAAS_ID,
1819
} from '@opentelemetry/semantic-conventions';
1920
import { AWS_ATTRIBUTE_KEYS } from './aws-attribute-keys';
2021
import { AWS_LAMBDA_FUNCTION_NAME_CONFIG, isLambdaEnvironment } from './aws-opentelemetry-configurator';
@@ -33,6 +34,9 @@ export class AwsSpanProcessingUtil {
3334
static LOCAL_ROOT: string = 'LOCAL_ROOT';
3435
static SQS_RECEIVE_MESSAGE_SPAN_NAME: string = 'Sqs.ReceiveMessage';
3536
static AWS_SDK_INSTRUMENTATION_SCOPE_PREFIX: string = '@opentelemetry/instrumentation-aws-sdk';
37+
// "cloud.resource_id" is defined in semconv which has not yet picked up by OTel JS
38+
// https://opentelemetry.io/docs/specs/semconv/attributes-registry/cloud/
39+
static CLOUD_RESOURCE_ID: string = 'cloud.resource_id';
3640

3741
// Max keyword length supported by parsing into remote_operation from DB_STATEMENT.
3842
// The current longest command word is DATETIME_INTERVAL_PRECISION at 27 characters.
@@ -273,4 +277,15 @@ export class AwsSpanProcessingUtil {
273277
const isLocalRoot: boolean = span.parentSpanId === undefined || !isParentSpanContextValid || isParentSpanRemote;
274278
span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_IS_LOCAL_ROOT, isLocalRoot);
275279
}
280+
281+
static getResourceId(span: ReadableSpan): string | undefined {
282+
let resourceId: AttributeValue | undefined = undefined;
283+
if (AwsSpanProcessingUtil.isKeyPresent(span, AwsSpanProcessingUtil.CLOUD_RESOURCE_ID)) {
284+
resourceId = span.attributes[AwsSpanProcessingUtil.CLOUD_RESOURCE_ID];
285+
} else if (AwsSpanProcessingUtil.isKeyPresent(span, SEMRESATTRS_FAAS_ID)) {
286+
resourceId = span.attributes[SEMRESATTRS_FAAS_ID];
287+
}
288+
289+
return typeof resourceId === 'string' ? resourceId : undefined;
290+
}
276291
}

aws-distro-opentelemetry-node-autoinstrumentation/test/attribute-propagating-span-processor.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,44 @@ describe('AttributePropagatingSpanProcessorTest', () => {
240240
);
241241
});
242242

243+
it('testLambdaResourceIdAttributeExist', () => {
244+
const parentSpan: APISpan = tracer.startSpan('parent', { kind: SpanKind.SERVER });
245+
246+
parentSpan.setAttribute(AwsSpanProcessingUtil.CLOUD_RESOURCE_ID, 'resource-123');
247+
248+
const childSpan: APISpan = createNestedSpan(parentSpan, 1);
249+
expect((childSpan as any).attributes[AwsSpanProcessingUtil.CLOUD_RESOURCE_ID]).not.toBeUndefined();
250+
expect((childSpan as any).attributes[AwsSpanProcessingUtil.CLOUD_RESOURCE_ID]).toEqual('resource-123');
251+
});
252+
253+
it('testLambdaFaasIdAttributeExist', () => {
254+
const parentSpan: APISpan = tracer.startSpan('parent', { kind: SpanKind.SERVER });
255+
256+
parentSpan.setAttribute('faas.id', 'faas-123');
257+
258+
const childSpan: APISpan = createNestedSpan(parentSpan, 1);
259+
expect((childSpan as any).attributes['faas.id']).not.toBeUndefined();
260+
expect((childSpan as any).attributes['faas.id']).toEqual('faas-123');
261+
});
262+
263+
it('testBothLambdaFaasIdAndResourceIdAttributesExist', () => {
264+
const parentSpan: APISpan = tracer.startSpan('parent', { kind: SpanKind.SERVER });
265+
266+
parentSpan.setAttribute('faas.id', 'faas-123');
267+
parentSpan.setAttribute(AwsSpanProcessingUtil.CLOUD_RESOURCE_ID, 'resource-123');
268+
269+
const childSpan: APISpan = createNestedSpan(parentSpan, 1);
270+
expect((childSpan as any).attributes[AwsSpanProcessingUtil.CLOUD_RESOURCE_ID]).not.toBeUndefined();
271+
expect((childSpan as any).attributes[AwsSpanProcessingUtil.CLOUD_RESOURCE_ID]).toEqual('resource-123');
272+
});
273+
274+
it('testLambdaNoneResourceAttributesExist', () => {
275+
const parentSpan: APISpan = tracer.startSpan('parent', { kind: SpanKind.SERVER });
276+
277+
const childSpan: APISpan = createNestedSpan(parentSpan, 1);
278+
expect((childSpan as any).attributes[AwsSpanProcessingUtil.CLOUD_RESOURCE_ID]).toBeUndefined();
279+
});
280+
243281
function createNestedSpan(parentSpan: APISpan, depth: number): APISpan {
244282
if (depth === 0) {
245283
return parentSpan;

aws-distro-opentelemetry-node-autoinstrumentation/test/aws-span-processing-util.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,36 @@ describe('AwsSpanProcessingUtilTest', () => {
377377
const actualOperation: string = AwsSpanProcessingUtil.getIngressOperation(spanDataMock);
378378
expect(actualOperation).toEqual('TestFunction/Handler');
379379
});
380+
381+
it('should return cloud.resource_id when present', () => {
382+
spanDataMock.attributes[AwsSpanProcessingUtil.CLOUD_RESOURCE_ID] = 'cloud-123';
383+
const result = AwsSpanProcessingUtil.getResourceId(spanDataMock);
384+
expect(result).toBe('cloud-123');
385+
});
386+
387+
it('should return faas.id when cloud.resource_id is not present', () => {
388+
spanDataMock.attributes['faas.id'] = 'faas-123';
389+
const result = AwsSpanProcessingUtil.getResourceId(spanDataMock);
390+
expect(result).toBe('faas-123');
391+
});
392+
393+
it('should return cloud.resource_id when both cloud.resource_id and faas.id are present', () => {
394+
spanDataMock.attributes[AwsSpanProcessingUtil.CLOUD_RESOURCE_ID] = 'cloud-123';
395+
spanDataMock.attributes['faas.id'] = 'faas-123';
396+
const result = AwsSpanProcessingUtil.getResourceId(spanDataMock);
397+
expect(result).toBe('cloud-123');
398+
});
399+
400+
it('should return undefined when neither cloud.resource_id nor faas.id are present', () => {
401+
const result = AwsSpanProcessingUtil.getResourceId(spanDataMock);
402+
expect(result).toBeUndefined();
403+
});
404+
405+
it('should return undefined if cloud.resource_id is not a string', () => {
406+
spanDataMock.attributes[AwsSpanProcessingUtil.CLOUD_RESOURCE_ID] = 123; // Incorrect type
407+
const result = AwsSpanProcessingUtil.getResourceId(spanDataMock);
408+
expect(result).toBeUndefined();
409+
});
380410
});
381411

382412
function createMockSpanContext(): SpanContext {

0 commit comments

Comments
 (0)