Skip to content

Commit 798bdbf

Browse files
Favor parameterized routes for resource name. (#513)
* Favor parameterized routes for resource name. * Move resourcePath construction to separate method. * Update src/trace/span-inferrer.ts Co-authored-by: AJ Stuyvenberg <[email protected]> --------- Co-authored-by: AJ Stuyvenberg <[email protected]>
1 parent b20c40e commit 798bdbf

File tree

4 files changed

+219
-1
lines changed

4 files changed

+219
-1
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
{
2+
"resource": "/user/{id}",
3+
"path": "/user/42",
4+
"httpMethod": "GET",
5+
"headers": {
6+
"Accept": "*/*",
7+
"CloudFront-Forwarded-Proto": "https",
8+
"CloudFront-Is-Desktop-Viewer": "true",
9+
"CloudFront-Is-Mobile-Viewer": "false",
10+
"CloudFront-Is-SmartTV-Viewer": "false",
11+
"CloudFront-Is-Tablet-Viewer": "false",
12+
"CloudFront-Viewer-ASN": "7922",
13+
"CloudFront-Viewer-Country": "US",
14+
"Host": "mcwkra0ya4.execute-api.sa-east-1.amazonaws.com",
15+
"User-Agent": "curl/8.1.2",
16+
"Via": "2.0 xxx.cloudfront.net (CloudFront)",
17+
"X-Amz-Cf-Id": "Tz3yUVcJkwOhQGqZgKTzrEHqAoOd8ZprYAHpg2S6BNxdd-Ym79pb6g==",
18+
"X-Amzn-Trace-Id": "Root=1-65f49d20-7ba106216238dd0078a5db31",
19+
"X-Forwarded-For": "76.115.124.192, 15.158.54.119",
20+
"X-Forwarded-Port": "443",
21+
"X-Forwarded-Proto": "https"
22+
},
23+
"multiValueHeaders": {
24+
"Accept": [
25+
"*/*"
26+
],
27+
"CloudFront-Forwarded-Proto": [
28+
"https"
29+
],
30+
"CloudFront-Is-Desktop-Viewer": [
31+
"true"
32+
],
33+
"CloudFront-Is-Mobile-Viewer": [
34+
"false"
35+
],
36+
"CloudFront-Is-SmartTV-Viewer": [
37+
"false"
38+
],
39+
"CloudFront-Is-Tablet-Viewer": [
40+
"false"
41+
],
42+
"CloudFront-Viewer-ASN": [
43+
"7922"
44+
],
45+
"CloudFront-Viewer-Country": [
46+
"US"
47+
],
48+
"Host": [
49+
"mcwkra0ya4.execute-api.sa-east-1.amazonaws.com"
50+
],
51+
"User-Agent": [
52+
"curl/8.1.2"
53+
],
54+
"Via": [
55+
"2.0 xxx.cloudfront.net (CloudFront)"
56+
],
57+
"X-Amz-Cf-Id": [
58+
"Tz3yUVcJkwOhQGqZgKTzrEHqAoOd8ZprYAHpg2S6BNxdd-Ym79pb6g=="
59+
],
60+
"X-Amzn-Trace-Id": [
61+
"Root=1-65f49d20-7ba106216238dd0078a5db31"
62+
],
63+
"X-Forwarded-For": [
64+
"76.115.124.192, 15.158.54.119"
65+
],
66+
"X-Forwarded-Port": [
67+
"443"
68+
],
69+
"X-Forwarded-Proto": [
70+
"https"
71+
]
72+
},
73+
"queryStringParameters": null,
74+
"multiValueQueryStringParameters": null,
75+
"pathParameters": {
76+
"id": "42"
77+
},
78+
"stageVariables": null,
79+
"requestContext": {
80+
"resourceId": "ojg3nk",
81+
"resourcePath": "/user/{id}",
82+
"httpMethod": "GET",
83+
"extendedRequestId": "Ur19IHYDmjQEU5A=",
84+
"requestTime": "15/Mar/2024:19:10:24 +0000",
85+
"path": "/dev/user/42",
86+
"accountId": "425362996713",
87+
"protocol": "HTTP/1.1",
88+
"stage": "dev",
89+
"domainPrefix": "mcwkra0ya4",
90+
"requestTimeEpoch": 1710529824520,
91+
"requestId": "e16399f7-e984-463a-9931-745ba021a27f",
92+
"identity": {
93+
"cognitoIdentityPoolId": null,
94+
"accountId": null,
95+
"cognitoIdentityId": null,
96+
"caller": null,
97+
"sourceIp": "76.115.124.192",
98+
"principalOrgId": null,
99+
"accessKey": null,
100+
"cognitoAuthenticationType": null,
101+
"cognitoAuthenticationProvider": null,
102+
"userArn": null,
103+
"userAgent": "curl/8.1.2",
104+
"user": null
105+
},
106+
"domainName": "mcwkra0ya4.execute-api.sa-east-1.amazonaws.com",
107+
"apiId": "mcwkra0ya4"
108+
},
109+
"body": null,
110+
"isBase64Encoded": false
111+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"version": "2.0",
3+
"routeKey": "GET /user/{id}",
4+
"rawPath": "/user/42",
5+
"rawQueryString": "",
6+
"headers": {
7+
"accept": "*/*",
8+
"content-length": "0",
9+
"host": "9vj54we5ih.execute-api.sa-east-1.amazonaws.com",
10+
"user-agent": "curl/8.1.2",
11+
"x-amzn-trace-id": "Root=1-65f49d71-505edb3b69b8abd513cfa08b",
12+
"x-forwarded-for": "76.115.124.192",
13+
"x-forwarded-port": "443",
14+
"x-forwarded-proto": "https"
15+
},
16+
"requestContext": {
17+
"accountId": "425362996713",
18+
"apiId": "9vj54we5ih",
19+
"domainName": "9vj54we5ih.execute-api.sa-east-1.amazonaws.com",
20+
"domainPrefix": "9vj54we5ih",
21+
"http": {
22+
"method": "GET",
23+
"path": "/user/42",
24+
"protocol": "HTTP/1.1",
25+
"sourceIp": "76.115.124.192",
26+
"userAgent": "curl/8.1.2"
27+
},
28+
"requestId": "Ur2JtjEfGjQEPOg=",
29+
"routeKey": "GET /user/{id}",
30+
"stage": "$default",
31+
"time": "15/Mar/2024:19:11:45 +0000",
32+
"timeEpoch": 1710529905066
33+
},
34+
"pathParameters": {
35+
"id": "42"
36+
},
37+
"isBase64Encoded": false
38+
}

src/trace/span-inferrer.spec.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const eventBridgeSQSEvent = require("../../event_samples/eventbridge-sqs.json");
1212
const webSocketEvent = require("../../event_samples/api-gateway-wss.json");
1313
const apiGatewayV1 = require("../../event_samples/api-gateway-v1.json");
1414
const apiGatewayV2 = require("../../event_samples/api-gateway-v2.json");
15+
const apiGatewayV1Parametrized = require("../../event_samples/api-gateway-v1-parametrized.json");
16+
const apiGatewayV2Parametrized = require("../../event_samples/api-gateway-v2-parametrized.json");
1517
const apiGatewayV1RequestAuthorizer = require("../../event_samples/api-gateway-traced-authorizer-request-v1.json");
1618
const apiGatewayV1RequestAuthorizerCached = require("../../event_samples/api-gateway-traced-authorizer-request-v1-cached.json");
1719
const apiGatewayV1TokenAuthorizer = require("../../event_samples/api-gateway-traced-authorizer-token-v1.json");
@@ -713,6 +715,60 @@ describe("SpanInferrer", () => {
713715
});
714716
});
715717

718+
it("creates an inferred span for API Gateway V1 events with parameters", () => {
719+
const inferrer = new SpanInferrer(mockWrapper as unknown as TracerWrapper);
720+
inferrer.createInferredSpan(apiGatewayV1Parametrized, {} as any, {} as SpanContext);
721+
722+
expect(mockWrapper.startSpan).toBeCalledWith("aws.apigateway", {
723+
childOf: {},
724+
startTime: 1710529824520,
725+
tags: {
726+
_inferred_span: { synchronicity: "sync", tag_source: "self" },
727+
apiid: "mcwkra0ya4",
728+
endpoint: "/dev/user/42",
729+
"http.url": "mcwkra0ya4.execute-api.sa-east-1.amazonaws.com/dev/user/42",
730+
domain_name: "mcwkra0ya4.execute-api.sa-east-1.amazonaws.com",
731+
operation_name: "aws.apigateway",
732+
"peer.service": "mock-lambda-service",
733+
request_id: undefined,
734+
"http.method": "GET",
735+
"resource.name": "GET /user/{id}",
736+
resource_names: "GET /user/{id}",
737+
service: "mcwkra0ya4.execute-api.sa-east-1.amazonaws.com",
738+
"service.name": "mcwkra0ya4.execute-api.sa-east-1.amazonaws.com",
739+
"span.type": "http",
740+
stage: "dev",
741+
},
742+
});
743+
});
744+
745+
it("creates an inferred span for API Gateway V2 events with parameters", () => {
746+
const inferrer = new SpanInferrer(mockWrapper as unknown as TracerWrapper);
747+
inferrer.createInferredSpan(apiGatewayV2Parametrized, {} as any, {} as SpanContext);
748+
749+
expect(mockWrapper.startSpan).toBeCalledWith("aws.apigateway", {
750+
childOf: {},
751+
startTime: 1710529905066,
752+
tags: {
753+
_inferred_span: { synchronicity: "sync", tag_source: "self" },
754+
apiid: "9vj54we5ih",
755+
endpoint: "/user/42",
756+
"http.url": "9vj54we5ih.execute-api.sa-east-1.amazonaws.com/user/42",
757+
domain_name: "9vj54we5ih.execute-api.sa-east-1.amazonaws.com",
758+
operation_name: "aws.apigateway",
759+
"peer.service": "mock-lambda-service",
760+
request_id: undefined,
761+
"http.method": "GET",
762+
"resource.name": "GET /user/{id}",
763+
resource_names: "GET /user/{id}",
764+
service: "9vj54we5ih.execute-api.sa-east-1.amazonaws.com",
765+
"service.name": "9vj54we5ih.execute-api.sa-east-1.amazonaws.com",
766+
"span.type": "http",
767+
stage: "$default",
768+
},
769+
});
770+
});
771+
716772
it("creates an inferred span for Lambda Function URL Events", () => {
717773
const inferrer = new SpanInferrer(mockWrapper as unknown as TracerWrapper);
718774
inferrer.createInferredSpan(functionUrlEvent, {} as any, {} as SpanContext);

src/trace/span-inferrer.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export class SpanInferrer {
9494
const options: SpanOptions = {};
9595
const domain = event.requestContext.domainName || "";
9696
const path = event.rawPath || event.requestContext.path || event.requestContext.routeKey;
97-
const resourcePath = event.rawPath || event.requestContext.resourcePath || event.requestContext.routeKey;
97+
const resourcePath = this.getResourcePath(event);
9898

9999
let method;
100100
if (event.requestContext.httpMethod) {
@@ -523,4 +523,17 @@ export class SpanInferrer {
523523
};
524524
return new SpanWrapper(this.traceWrapper.startSpan("aws.eventbridge", options), spanWrapperOptions);
525525
}
526+
527+
getResourcePath(event: any): string {
528+
const routeKey = event?.requestContext?.routeKey;
529+
if (routeKey && routeKey.includes("{")) {
530+
// this is a parameterized route
531+
try {
532+
return event.requestContext.routeKey.split(" ")[1];
533+
} catch (e) {
534+
logDebug("Error parsing routeKey", e as Error);
535+
}
536+
}
537+
return event.rawPath || event.requestContext.resourcePath || routeKey;
538+
}
526539
}

0 commit comments

Comments
 (0)