diff --git a/event_samples/api-gateway-traced-authorizer-request-v1-cached.json b/event_samples/api-gateway-traced-authorizer-request-v1-cached.json new file mode 100644 index 00000000..1c5d17cd --- /dev/null +++ b/event_samples/api-gateway-traced-authorizer-request-v1-cached.json @@ -0,0 +1,89 @@ +{ + "resource": "/hello", + "path": "/hello", + "httpMethod": "POST", + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Authorization": "password", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-ASN": "174", + "CloudFront-Viewer-Country": "US", + "Host": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "Postman-Token": "62ccb3d9-a44f-427c-9952-418c0a2eb1c3", + "User-Agent": "PostmanRuntime/7.29.0", + "Via": "1.1 xxx (CloudFront)", + "X-Amz-Cf-Id": "90JXZEr6stVabQV78Zwn5EADW0evkpWINdmt3jzkuHQh9KtqowKejw==", + "X-Amzn-Trace-Id": "Root=1-62ffee4f-373bdfda15f09a065a39ac73", + "X-Forwarded-For": "38.142.177.195, 64.252.135.71", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "Accept": ["*/*"], + "Accept-Encoding": ["gzip, deflate, br"], + "Authorization": ["password"], + "CloudFront-Forwarded-Proto": ["https"], + "CloudFront-Is-Desktop-Viewer": ["true"], + "CloudFront-Is-Mobile-Viewer": ["false"], + "CloudFront-Is-SmartTV-Viewer": ["false"], + "CloudFront-Is-Tablet-Viewer": ["false"], + "CloudFront-Viewer-ASN": ["174"], + "CloudFront-Viewer-Country": ["US"], + "Host": ["3gsxz7lha4.execute-api.sa-east-1.amazonaws.com"], + "Postman-Token": ["62ccb3d9-a44f-427c-9952-418c0a2eb1c3"], + "User-Agent": ["PostmanRuntime/7.29.0"], + "Via": ["1.1 xxx.cloudfront.net (CloudFront)"], + "X-Amz-Cf-Id": ["90JXZEr6stVabQV78Zwn5EADW0evkpWINdmt3jzkuHQh9KtqowKejw=="], + "X-Amzn-Trace-Id": ["Root=1-62ffee4f-373bdfda15f09a065a39ac73"], + "X-Forwarded-For": ["38.142.177.195, 64.252.135.71"], + "X-Forwarded-Port": ["443"], + "X-Forwarded-Proto": ["https"] + }, + "queryStringParameters": null, + "multiValueQueryStringParameters": null, + "pathParameters": null, + "stageVariables": null, + "requestContext": { + "resourceId": "oozq9u", + "authorizer": { + "_datadog": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiIyMDk1MzE5NzYxMDg0NzEwNzQ3IiwieC1kYXRhZG9nLXBhcmVudC1pZCI6IjIwOTUzMTk3NjEwODQ3MTA3NDciLCJ4LWRhdGFkb2ctc2FtcGxpbmctcHJpb3JpdHkiOiIxIiwieC1kYXRhZG9nLXBhcmVudC1zcGFuLWZpbmlzaC10aW1lIjoxNjYwOTM5ODU3MDUyLCJ4LWRhdGFkb2ctYXV0aG9yaXppbmctcmVxdWVzdGlkIjoiZjFmOGQ0NmQtZWY2Zi00NmFmLWEzZWQtN2EyMGEyNmUyNjUxIn0=", + "principalId": "foo", + "integrationLatency": 0, + "preserve": "this key set by a customer" + }, + "resourcePath": "/hello", + "httpMethod": "POST", + "extendedRequestId": "XIIseElXGjQFvXg=", + "requestTime": "19/Aug/2022:20:10:55 +0000", + "path": "/dev/hello", + "accountId": "601427279990", + "protocol": "HTTP/1.1", + "stage": "dev", + "domainPrefix": "3gsxz7lha4", + "requestTimeEpoch": 1660939855656, + "requestId": "f1f8d46d-ef6f-46af-a3ed-7a20a26e2652", + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "sourceIp": "38.142.177.195", + "principalOrgId": null, + "accessKey": null, + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "PostmanRuntime/7.29.0", + "user": null + }, + "domainName": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "apiId": "3gsxz7lha4" + }, + "body": null, + "isBase64Encoded": false +} diff --git a/event_samples/api-gateway-traced-authorizer-request-v1.json b/event_samples/api-gateway-traced-authorizer-request-v1.json new file mode 100644 index 00000000..2a09e884 --- /dev/null +++ b/event_samples/api-gateway-traced-authorizer-request-v1.json @@ -0,0 +1,89 @@ +{ + "resource": "/hello", + "path": "/hello", + "httpMethod": "POST", + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Authorization": "password", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-ASN": "174", + "CloudFront-Viewer-Country": "US", + "Host": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "Postman-Token": "62ccb3d9-a44f-427c-9952-418c0a2eb1c3", + "User-Agent": "PostmanRuntime/7.29.0", + "Via": "1.1 xxx.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "90JXZEr6stVabQV78Zwn5EADW0evkpWINdmt3jzkuHQh9KtqowKejw==", + "X-Amzn-Trace-Id": "Root=1-62ffee4f-373bdfda15f09a065a39ac73", + "X-Forwarded-For": "38.142.177.195, 64.252.135.71", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "Accept": ["*/*"], + "Accept-Encoding": ["gzip, deflate, br"], + "Authorization": ["password"], + "CloudFront-Forwarded-Proto": ["https"], + "CloudFront-Is-Desktop-Viewer": ["true"], + "CloudFront-Is-Mobile-Viewer": ["false"], + "CloudFront-Is-SmartTV-Viewer": ["false"], + "CloudFront-Is-Tablet-Viewer": ["false"], + "CloudFront-Viewer-ASN": ["174"], + "CloudFront-Viewer-Country": ["US"], + "Host": ["3gsxz7lha4.execute-api.sa-east-1.amazonaws.com"], + "Postman-Token": ["62ccb3d9-a44f-427c-9952-418c0a2eb1c3"], + "User-Agent": ["PostmanRuntime/7.29.0"], + "Via": ["1.1 xxx.cloudfront.net (CloudFront)"], + "X-Amz-Cf-Id": ["90JXZEr6stVabQV78Zwn5EADW0evkpWINdmt3jzkuHQh9KtqowKejw=="], + "X-Amzn-Trace-Id": ["Root=1-62ffee4f-373bdfda15f09a065a39ac73"], + "X-Forwarded-For": ["38.142.177.195, 64.252.135.71"], + "X-Forwarded-Port": ["443"], + "X-Forwarded-Proto": ["https"] + }, + "queryStringParameters": null, + "multiValueQueryStringParameters": null, + "pathParameters": null, + "stageVariables": null, + "requestContext": { + "resourceId": "oozq9u", + "authorizer": { + "_datadog": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiIyMDk1MzE5NzYxMDg0NzEwNzQ3IiwieC1kYXRhZG9nLXBhcmVudC1pZCI6IjIwOTUzMTk3NjEwODQ3MTA3NDciLCJ4LWRhdGFkb2ctc2FtcGxpbmctcHJpb3JpdHkiOiIxIiwieC1kYXRhZG9nLXBhcmVudC1zcGFuLWZpbmlzaC10aW1lIjoxNjYwOTM5ODU3MDUyMDAwMDAwLCJ4LWRhdGFkb2ctYXV0aG9yaXppbmctcmVxdWVzdGlkIjoiZjFmOGQ0NmQtZWY2Zi00NmFmLWEzZWQtN2EyMGEyNmUyNjUxIn0=", + "principalId": "foo", + "integrationLatency": 1419, + "preserve": "this key set by a customer" + }, + "resourcePath": "/hello", + "httpMethod": "POST", + "extendedRequestId": "XIIseElXGjQFvXg=", + "requestTime": "19/Aug/2022:20:10:55 +0000", + "path": "/dev/hello", + "accountId": "601427279990", + "protocol": "HTTP/1.1", + "stage": "dev", + "domainPrefix": "3gsxz7lha4", + "requestTimeEpoch": 1660939855656, + "requestId": "f1f8d46d-ef6f-46af-a3ed-7a20a26e2651", + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "sourceIp": "38.142.177.195", + "principalOrgId": null, + "accessKey": null, + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "PostmanRuntime/7.29.0", + "user": null + }, + "domainName": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "apiId": "3gsxz7lha4" + }, + "body": null, + "isBase64Encoded": false +} diff --git a/event_samples/api-gateway-traced-authorizer-request-v2-cached.json b/event_samples/api-gateway-traced-authorizer-request-v2-cached.json new file mode 100644 index 00000000..1f2dbf53 --- /dev/null +++ b/event_samples/api-gateway-traced-authorizer-request-v2-cached.json @@ -0,0 +1,47 @@ +{ + "version": "2.0", + "routeKey": "GET /hello", + "rawPath": "/hello", + "rawQueryString": "", + "headers": { + "accept": "*/*", + "accept-encoding": "gzip, deflate, br", + "authorization": "secretT0k3n", + "authorizationtoken": "secretT0k3n", + "cache-control": "no-cache", + "content-length": "0", + "host": "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + "postman-token": "e0a783f5-8f72-427f-99bb-81d28ac3b37b", + "user-agent": "PostmanRuntime/7.29.2", + "userid": "27", + "x-amzn-trace-id": "Root=1-6346fdb8-74147ee52ffc4c685787d44c", + "x-forwarded-for": "24.193.182.233", + "x-forwarded-port": "443", + "x-forwarded-proto": "https" + }, + "requestContext": { + "accountId": "601427279990", + "apiId": "l9flvsey83", + "authorizer": { + "lambda": { + "_datadog": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiIzNzI2NzU1MTU4Mjk1OTIxMDQiLCJ4LWRhdGFkb2ctcGFyZW50LWlkIjoiMzcyNjc1NTE1ODI5NTkyMTA0IiwieC1kYXRhZG9nLXNhbXBsaW5nLXByaW9yaXR5IjoiMSIsIngtZGF0YWRvZy1wYXJlbnQtc3Bhbi1maW5pc2gtdGltZSI6MTY2NTU5Njc3MTgxMiwieC1kYXRhZG9nLWF1dGhvcml6aW5nLXJlcXVlc3RpZCI6Ilo1eUhmaDVFR2pRRUpCZz0ifQ==", + "scope": "this is just a string" + } + }, + "domainName": "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + "domainPrefix": "l9flvsey83", + "http": { + "method": "GET", + "path": "/hello", + "protocol": "HTTP/1.1", + "sourceIp": "24.193.182.233", + "userAgent": "PostmanRuntime/7.29.2" + }, + "requestId": "Z5yU6jHVmjQEJ4Q=", + "routeKey": "GET /hello", + "stage": "$default", + "time": "12/Oct/2022:17:47:36 +0000", + "timeEpoch": 1665596856876 + }, + "isBase64Encoded": false +} diff --git a/event_samples/api-gateway-traced-authorizer-request-v2.json b/event_samples/api-gateway-traced-authorizer-request-v2.json new file mode 100644 index 00000000..df6c244b --- /dev/null +++ b/event_samples/api-gateway-traced-authorizer-request-v2.json @@ -0,0 +1,47 @@ +{ + "version": "2.0", + "routeKey": "GET /hello", + "rawPath": "/hello", + "rawQueryString": "", + "headers": { + "accept": "*/*", + "accept-encoding": "gzip, deflate, br", + "authorization": "secretT0k3n", + "authorizationtoken": "secretT0k3n", + "cache-control": "no-cache", + "content-length": "0", + "host": "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + "postman-token": "e7c0d4f6-6af1-46dc-81ad-76dd8b02af8c", + "user-agent": "PostmanRuntime/7.29.2", + "userid": "27", + "x-amzn-trace-id": "Root=1-6346fd62-123a3d6477d3393b1509b50b", + "x-forwarded-for": "24.193.182.233", + "x-forwarded-port": "443", + "x-forwarded-proto": "https" + }, + "requestContext": { + "accountId": "601427279990", + "apiId": "l9flvsey83", + "authorizer": { + "lambda": { + "_datadog": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiIzNzI2NzU1MTU4Mjk1OTIxMDQiLCJ4LWRhdGFkb2ctcGFyZW50LWlkIjoiMzcyNjc1NTE1ODI5NTkyMTA0IiwieC1kYXRhZG9nLXNhbXBsaW5nLXByaW9yaXR5IjoiMSIsIngtZGF0YWRvZy1wYXJlbnQtc3Bhbi1maW5pc2gtdGltZSI6MTY2NTU5Njc3MTgxMjAwMDAwMCwieC1kYXRhZG9nLWF1dGhvcml6aW5nLXJlcXVlc3RpZCI6Ilo1eUhmaDVFR2pRRUpCZz0ifQ==", + "scope": "this is just a string" + } + }, + "domainName": "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + "domainPrefix": "l9flvsey83", + "http": { + "method": "GET", + "path": "/hello", + "protocol": "HTTP/1.1", + "sourceIp": "24.193.182.233", + "userAgent": "PostmanRuntime/7.29.2" + }, + "requestId": "Z5yHfh5EGjQEJBg=", + "routeKey": "GET /hello", + "stage": "$default", + "time": "12/Oct/2022:17:46:10 +0000", + "timeEpoch": 1665596770926 + }, + "isBase64Encoded": false +} diff --git a/event_samples/api-gateway-traced-authorizer-request-websocket-connect.json b/event_samples/api-gateway-traced-authorizer-request-websocket-connect.json new file mode 100644 index 00000000..68d8a2a8 --- /dev/null +++ b/event_samples/api-gateway-traced-authorizer-request-websocket-connect.json @@ -0,0 +1,48 @@ +{ + "headers": { + "Auth": "secretT0k3n", + "Host": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com", + "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits", + "Sec-WebSocket-Key": "4v5yA3WKtAK6EK1KUvSxew==", + "Sec-WebSocket-Version": "13", + "X-Amzn-Trace-Id": "Root=1-6356cf5d-355baf3954d8ebee6af753ef", + "X-Forwarded-For": "24.193.182.233", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "Auth": ["secretT0k3n"], + "Host": ["85fj5nw29d.execute-api.sa-east-1.amazonaws.com"], + "Sec-WebSocket-Extensions": ["permessage-deflate; client_max_window_bits"], + "Sec-WebSocket-Key": ["4v5yA3WKtAK6EK1KUvSxew=="], + "Sec-WebSocket-Version": ["13"], + "X-Amzn-Trace-Id": ["Root=1-6356cf5d-355baf3954d8ebee6af753ef"], + "X-Forwarded-For": ["24.193.182.233"], + "X-Forwarded-Port": ["443"], + "X-Forwarded-Proto": ["https"] + }, + "requestContext": { + "routeKey": "$connect", + "authorizer": { + "_datadog": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiI2NTQ1NDA2NzQ3NDUzNjg0NjAwIiwieC1kYXRhZG9nLXBhcmVudC1pZCI6IjY1NDU0MDY3NDc0NTM2ODQ2MDAiLCJ4LWRhdGFkb2ctc2FtcGxpbmctcHJpb3JpdHkiOiIxIiwieC1kYXRhZG9nLXBhcmVudC1zcGFuLWZpbmlzaC10aW1lIjoxNjY2NjMzNTY2OTMxMDAwMDAwLCJ4LWRhdGFkb2ctYXV0aG9yaXppbmctcmVxdWVzdGlkIjoiYWhWV3NIVkFtalFGcTZnPSJ9", + "scope": "this is just a string", + "principalId": "foo", + "integrationLatency": 1119 + }, + "eventType": "CONNECT", + "extendedRequestId": "ahVWsHVAmjQFq6g=", + "requestTime": "24/Oct/2022:17:46:05 +0000", + "messageDirection": "IN", + "stage": "dev", + "connectedAt": 1666633565827, + "requestTimeEpoch": 1666633565828, + "identity": { + "sourceIp": "24.193.182.233" + }, + "requestId": "ahVWsHVAmjQFq6g=", + "domainName": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com", + "connectionId": "ahVWscZqmjQCI1w=", + "apiId": "85fj5nw29d" + }, + "isBase64Encoded": false +} diff --git a/event_samples/api-gateway-traced-authorizer-request-websocket-message.json b/event_samples/api-gateway-traced-authorizer-request-websocket-message.json new file mode 100644 index 00000000..20de6afc --- /dev/null +++ b/event_samples/api-gateway-traced-authorizer-request-websocket-message.json @@ -0,0 +1,27 @@ +{ + "requestContext": { + "routeKey": "hello", + "authorizer": { + "_datadog": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiI2NTQ1NDA2NzQ3NDUzNjg0NjAwIiwieC1kYXRhZG9nLXBhcmVudC1pZCI6IjY1NDU0MDY3NDc0NTM2ODQ2MDAiLCJ4LWRhdGFkb2ctc2FtcGxpbmctcHJpb3JpdHkiOiIxIiwieC1kYXRhZG9nLXBhcmVudC1zcGFuLWZpbmlzaC10aW1lIjoxNjY2NjMzNTY2OTMxLCJ4LWRhdGFkb2ctYXV0aG9yaXppbmctcmVxdWVzdGlkIjoiYWhWV3NIVkFtalFGcTZnPSJ9", + "scope": "this is just a string", + "principalId": "foo" + }, + "messageId": "ahVmYcavmjQCI1w=", + "eventType": "MESSAGE", + "extendedRequestId": "ahVmYGOMmjQFhyg=", + "requestTime": "24/Oct/2022:17:47:46 +0000", + "messageDirection": "IN", + "stage": "dev", + "connectedAt": 1666633565827, + "requestTimeEpoch": 1666633666203, + "identity": { + "sourceIp": "24.193.182.233" + }, + "requestId": "ahVmYGOMmjQFhyg=", + "domainName": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com", + "connectionId": "ahVWscZqmjQCI1w=", + "apiId": "85fj5nw29d" + }, + "body": "{\"action\": \"hello\", \"message\":\"in\"}", + "isBase64Encoded": false +} diff --git a/event_samples/api-gateway-traced-authorizer-token-v1-cached.json b/event_samples/api-gateway-traced-authorizer-token-v1-cached.json new file mode 100644 index 00000000..868e9002 --- /dev/null +++ b/event_samples/api-gateway-traced-authorizer-token-v1-cached.json @@ -0,0 +1,95 @@ +{ + "resource": "/hi", + "path": "/hi", + "httpMethod": "GET", + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Authorization": "secretT0k3n", + "authorizationToken": "secretT0k3n", + "Cache-Control": "no-cache", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-ASN": "174", + "CloudFront-Viewer-Country": "US", + "Host": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "Postman-Token": "169e3731-8497-4d97-a141-340e2fcc6f16", + "User-Agent": "PostmanRuntime/7.29.2", + "userid": "1236", + "Via": "1.1 x.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "bAx8Dew3PKcGOa1ByLD4vsNkk8hz4ccomS1LTOfnkR3kDqveJIWF8g==", + "X-Amzn-Trace-Id": "Root=1-63596622-39dfb1680d2463a003de23b5", + "X-Forwarded-For": "38.122.226.210, 15.158.41.235", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "Accept": ["*/*"], + "Accept-Encoding": ["gzip, deflate, br"], + "Authorization": ["secretT0k3n"], + "authorizationToken": ["secretT0k3n"], + "Cache-Control": ["no-cache"], + "CloudFront-Forwarded-Proto": ["https"], + "CloudFront-Is-Desktop-Viewer": ["true"], + "CloudFront-Is-Mobile-Viewer": ["false"], + "CloudFront-Is-SmartTV-Viewer": ["false"], + "CloudFront-Is-Tablet-Viewer": ["false"], + "CloudFront-Viewer-ASN": ["174"], + "CloudFront-Viewer-Country": ["US"], + "Host": ["4dyr9xqip7.execute-api.sa-east-1.amazonaws.com"], + "Postman-Token": ["169e3731-8497-4d97-a141-340e2fcc6f16"], + "User-Agent": ["PostmanRuntime/7.29.2"], + "userid": ["1236"], + "Via": ["1.1 x.cloudfront.net (CloudFront)"], + "X-Amz-Cf-Id": ["bAx8Dew3PKcGOa1ByLD4vsNkk8hz4ccomS1LTOfnkR3kDqveJIWF8g=="], + "X-Amzn-Trace-Id": ["Root=1-63596622-39dfb1680d2463a003de23b5"], + "X-Forwarded-For": ["38.122.226.210, 15.158.41.235"], + "X-Forwarded-Port": ["443"], + "X-Forwarded-Proto": ["https"] + }, + "queryStringParameters": null, + "multiValueQueryStringParameters": null, + "pathParameters": null, + "stageVariables": null, + "requestContext": { + "resourceId": "nwlcwu", + "authorizer": { + "_datadog": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiI2OTY4Mjg0MDg3NjQ5NTc3MjAwIiwieC1kYXRhZG9nLXBhcmVudC1pZCI6IjY5NjgyODQwODc2NDk1NzcyMDAiLCJ4LWRhdGFkb2ctc2FtcGxpbmctcHJpb3JpdHkiOiIxIiwieC1kYXRhZG9nLXBhcmVudC1zcGFuLWZpbmlzaC10aW1lIjoxNjY2ODAzMTk2NzgwfQ==", + "scope": "this is just a string", + "principalId": "foo", + "integrationLatency": 0 + }, + "resourcePath": "/hi", + "httpMethod": "GET", + "extendedRequestId": "anzlWFUimjQFcpA=", + "requestTime": "26/Oct/2022:16:53:54 +0000", + "path": "/dev/hi", + "accountId": "425362996713", + "protocol": "HTTP/1.1", + "stage": "dev", + "domainPrefix": "4dyr9xqip7", + "requestTimeEpoch": 1666803234094, + "requestId": "d5686bff-1124-4df1-b15f-119d7ca11fa7", + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "sourceIp": "38.122.226.210", + "principalOrgId": null, + "accessKey": null, + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "PostmanRuntime/7.29.2", + "user": null + }, + "domainName": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "apiId": "4dyr9xqip7" + }, + "body": null, + "isBase64Encoded": false +} diff --git a/event_samples/api-gateway-traced-authorizer-token-v1.json b/event_samples/api-gateway-traced-authorizer-token-v1.json new file mode 100644 index 00000000..29f4e2b3 --- /dev/null +++ b/event_samples/api-gateway-traced-authorizer-token-v1.json @@ -0,0 +1,95 @@ +{ + "resource": "/hi", + "path": "/hi", + "httpMethod": "GET", + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Authorization": "secretT0k3n", + "authorizationToken": "secretT0k3n", + "Cache-Control": "no-cache", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-ASN": "174", + "CloudFront-Viewer-Country": "US", + "Host": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "Postman-Token": "f7fa9aab-d480-4d0f-a761-f5d750479c2e", + "User-Agent": "PostmanRuntime/7.29.2", + "userid": "1236", + "Via": "1.1 x.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "1KztlWy3gTvgpziQnUD-u9iXblM1UqGFJDGclhV6NVW_UBeuelkFyA==", + "X-Amzn-Trace-Id": "Root=1-635965fb-216d74f3611f8cdc6fed0e35", + "X-Forwarded-For": "38.122.226.210, 15.158.41.212", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "Accept": ["*/*"], + "Accept-Encoding": ["gzip, deflate, br"], + "Authorization": ["secretT0k3n"], + "authorizationToken": ["secretT0k3n"], + "Cache-Control": ["no-cache"], + "CloudFront-Forwarded-Proto": ["https"], + "CloudFront-Is-Desktop-Viewer": ["true"], + "CloudFront-Is-Mobile-Viewer": ["false"], + "CloudFront-Is-SmartTV-Viewer": ["false"], + "CloudFront-Is-Tablet-Viewer": ["false"], + "CloudFront-Viewer-ASN": ["174"], + "CloudFront-Viewer-Country": ["US"], + "Host": ["4dyr9xqip7.execute-api.sa-east-1.amazonaws.com"], + "Postman-Token": ["f7fa9aab-d480-4d0f-a761-f5d750479c2e"], + "User-Agent": ["PostmanRuntime/7.29.2"], + "userid": ["1236"], + "Via": ["1.1 x.cloudfront.net (CloudFront)"], + "X-Amz-Cf-Id": ["1KztlWy3gTvgpziQnUD-u9iXblM1UqGFJDGclhV6NVW_UBeuelkFyA=="], + "X-Amzn-Trace-Id": ["Root=1-635965fb-216d74f3611f8cdc6fed0e35"], + "X-Forwarded-For": ["38.122.226.210, 15.158.41.212"], + "X-Forwarded-Port": ["443"], + "X-Forwarded-Proto": ["https"] + }, + "queryStringParameters": null, + "multiValueQueryStringParameters": null, + "pathParameters": null, + "stageVariables": null, + "requestContext": { + "resourceId": "nwlcwu", + "authorizer": { + "_datadog": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiI2OTY4Mjg0MDg3NjQ5NTc3MjAwIiwieC1kYXRhZG9nLXBhcmVudC1pZCI6IjY5NjgyODQwODc2NDk1NzcyMDAiLCJ4LWRhdGFkb2ctc2FtcGxpbmctcHJpb3JpdHkiOiIxIiwieC1kYXRhZG9nLXBhcmVudC1zcGFuLWZpbmlzaC10aW1lIjoxNjY2ODAzMTk2NzgwMDAwMDAwfQ==", + "scope": "this is just a string", + "principalId": "foo", + "integrationLatency": 1144 + }, + "resourcePath": "/hi", + "httpMethod": "GET", + "extendedRequestId": "anzfWF-YmjQFkJg=", + "requestTime": "26/Oct/2022:16:53:15 +0000", + "path": "/dev/hi", + "accountId": "425362996713", + "protocol": "HTTP/1.1", + "stage": "dev", + "domainPrefix": "4dyr9xqip7", + "requestTimeEpoch": 1666803195639, + "requestId": "2bd8ffee-f68e-4848-bdd8-9196f37631ea", + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "sourceIp": "38.122.226.210", + "principalOrgId": null, + "accessKey": null, + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "PostmanRuntime/7.29.2", + "user": null + }, + "domainName": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "apiId": "4dyr9xqip7" + }, + "body": null, + "isBase64Encoded": false +} diff --git a/src/index.ts b/src/index.ts index 09acd70e..3e3a25d6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,6 +34,8 @@ export const lambdaTaskRootEnvVar = "LAMBDA_TASK_ROOT"; export const mergeXrayTracesEnvVar = "DD_MERGE_XRAY_TRACES"; export const traceExtractorEnvVar = "DD_TRACE_EXTRACTOR"; export const defaultSiteURL = "datadoghq.com"; +export const encodeAuthorizerContextEnvVar = "DD_ENCODE_AUTHORIZER_CONTEXT"; +export const decodeAuthorizerContextEnvVar = "DD_DECODE_AUTHORIZER_CONTEXT"; interface GlobalConfig { /** @@ -64,6 +66,8 @@ export const defaultConfig: Config = { captureLambdaPayload: false, createInferredSpan: true, debugLogging: false, + encodeAuthorizerContext: true, + decodeAuthorizerContext: true, enhancedMetrics: true, forceWrap: false, injectLogContext: true, @@ -156,7 +160,7 @@ export function datadog( incrementErrorsMetric(metricsListener, context); } await metricsListener.onCompleteInvocation(); - await traceListener.onCompleteInvocation(error); + await traceListener.onCompleteInvocation(error, result, event); } catch (err) { if (err instanceof Error) { logDebug("Failed to complete listeners", err); @@ -273,6 +277,16 @@ function getConfig(userConfig?: Partial): Config { config.createInferredSpan = result === "true"; } + if (userConfig === undefined || userConfig.encodeAuthorizerContext === undefined) { + const result = getEnvValue(encodeAuthorizerContextEnvVar, "true").toLowerCase(); + config.encodeAuthorizerContext = result === "true"; + } + + if (userConfig === undefined || userConfig.decodeAuthorizerContext === undefined) { + const result = getEnvValue(decodeAuthorizerContextEnvVar, "true").toLowerCase(); + config.decodeAuthorizerContext = result === "true"; + } + return config; } diff --git a/src/trace/constants.ts b/src/trace/constants.ts index ffb9461d..f81c1b59 100644 --- a/src/trace/constants.ts +++ b/src/trace/constants.ts @@ -22,3 +22,5 @@ export const xrayTraceEnvVar = "_X_AMZN_TRACE_ID"; export const awsXrayDaemonAddressEnvVar = "AWS_XRAY_DAEMON_ADDRESS"; export const ddtraceVersion = "X.X.X"; export const apiGatewayEventV2 = "2.0"; +export const parentSpanFinishTimeHeader = "x-datadog-parent-span-finish-time"; +export const authorizingRequestIdHeader = "x-datadog-authorizing-requestid"; diff --git a/src/trace/context.spec.ts b/src/trace/context.spec.ts index e0d04d22..e70388ea 100644 --- a/src/trace/context.spec.ts +++ b/src/trace/context.spec.ts @@ -221,6 +221,31 @@ describe("readTraceFromEvent", () => { }); }); + it("can parse a traced authorizer source", () => { + const result = readTraceFromHTTPEvent({ + requestContext: { + resourceId: "oozq9u", + authorizer: { + _datadog: + "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiIyMzg5NTg5OTU0MDI2MDkwMjk2IiwieC1kYXRhZG9nLXBhcmVudC1pZCI6IjIzODk1ODk5NTQwMjYwOTAyOTYiLCJ4LWRhdGFkb2ctc2FtcGxpbmctcHJpb3JpdHkiOiIxIiwieC1kYXRhZG9nLXBhcmVudC1zcGFuLWZpbmlzaC10aW1lIjoxNjYwOTM5ODk5MjMzLCJ4LWRhdGFkb2ctYXV0aG9yaXppbmctcmVxdWVzdGlkIjoicmFuZG9tLWlkIn0==", + principalId: "foo", + integrationLatency: 71, + preserve: "this key set by a customer", + }, + stage: "dev", + requestId: "random-id", + }, + httpMethod: "GET", + resource: "/hello", + }); + expect(result).toEqual({ + parentID: "2389589954026090296", + sampleMode: 1, + source: "event", + traceID: "2389589954026090296", + }); + }); + it("can parse an SNS message source", () => { const result = readTraceFromEvent({ Records: [ diff --git a/src/trace/context.ts b/src/trace/context.ts index 90d532d8..1396ca0d 100644 --- a/src/trace/context.ts +++ b/src/trace/context.ts @@ -26,6 +26,8 @@ import { xrayTraceEnvVar, } from "./constants"; import { TraceExtractor } from "./listener"; +import { parseEventSourceSubType, eventSubTypes } from "./trigger"; +import { authorizingRequestIdHeader } from "./constants"; export interface XRayTraceHeader { traceID: string; @@ -56,6 +58,7 @@ export function extractTraceContext( event: any, context: Context, extractor?: TraceExtractor, + decodeAuthorizerContext: boolean = true, ): TraceContext | undefined { let trace; @@ -71,7 +74,7 @@ export function extractTraceContext( } if (!trace) { - trace = readTraceFromEvent(event); + trace = readTraceFromEvent(event, decodeAuthorizerContext); } if (!trace) { @@ -199,7 +202,7 @@ export function sendXraySubsegment(segment: string) { export function readTraceFromAppSyncEvent(event: any): TraceContext | undefined { event.headers = event.request.headers; - return readTraceFromHTTPEvent(event); + return readTraceFromHTTPEvent(event, false); } export function readTraceFromSQSEvent(event: SQSEvent): TraceContext | undefined { @@ -207,23 +210,8 @@ export function readTraceFromSQSEvent(event: SQSEvent): TraceContext | undefined const traceHeaders = event.Records[0].messageAttributes._datadog.stringValue; try { - const traceData = JSON.parse(traceHeaders); - const traceID = traceData[traceIDHeader]; - const parentID = traceData[parentIDHeader]; - const sampledHeader = traceData[samplingPriorityHeader]; + const trace = exportTraceData(JSON.parse(traceHeaders)); - if (typeof traceID !== "string" || typeof parentID !== "string" || typeof sampledHeader !== "string") { - return; - } - - const sampleMode = parseInt(sampledHeader, 10); - - const trace = { - parentID, - sampleMode, - source: Source.Event, - traceID, - }; logDebug(`extracted trace context from sqs event`, { trace, event }); return trace; } catch (err) { @@ -253,21 +241,8 @@ export function readTraceFromSNSSQSEvent(event: SQSEvent): TraceContext | undefi const b64Decoded = Buffer.from(parsedBody.MessageAttributes._datadog.Value, "base64").toString("ascii"); traceData = JSON.parse(b64Decoded); } - const traceID = traceData[traceIDHeader]; - const parentID = traceData[parentIDHeader]; - const sampledHeader = traceData[samplingPriorityHeader]; + const trace = exportTraceData(traceData); - if (typeof traceID !== "string" || typeof parentID !== "string" || typeof sampledHeader !== "string") { - return; - } - const sampleMode = parseInt(sampledHeader, 10); - - const trace = { - parentID, - sampleMode, - source: Source.Event, - traceID, - }; logDebug(`extracted trace context from SNS SQS event`, { trace, event }); return trace; } @@ -285,22 +260,7 @@ export function readTraceFromKinesisEvent(event: KinesisStreamEvent): TraceConte try { const parsedBody = JSON.parse(Buffer.from(event.Records[0].kinesis.data, "base64").toString("ascii")) as any; if (parsedBody && parsedBody._datadog) { - const traceData = parsedBody._datadog; - const traceID = traceData[traceIDHeader]; - const parentID = traceData[parentIDHeader]; - const sampledHeader = traceData[samplingPriorityHeader]; - - if (typeof traceID !== "string" || typeof parentID !== "string" || typeof sampledHeader !== "string") { - return; - } - const sampleMode = parseInt(sampledHeader, 10); - - const trace = { - parentID, - sampleMode, - source: Source.Event, - traceID, - }; + const trace = exportTraceData(parsedBody._datadog); logDebug(`extracted trace context from Kinesis event`, { trace }); return trace; } @@ -316,22 +276,7 @@ export function readTraceFromKinesisEvent(event: KinesisStreamEvent): TraceConte export function readTraceFromEventbridgeEvent(event: EventBridgeEvent): TraceContext | undefined { if (event?.detail?._datadog) { try { - const traceData = event.detail._datadog; - const traceID = traceData[traceIDHeader]; - const parentID = traceData[parentIDHeader]; - const sampledHeader = traceData[samplingPriorityHeader]; - - if (typeof traceID !== "string" || typeof parentID !== "string" || typeof sampledHeader !== "string") { - return; - } - const sampleMode = parseInt(sampledHeader, 10); - - const trace = { - parentID, - sampleMode, - source: Source.Event, - traceID, - }; + const trace = exportTraceData(event.detail._datadog); logDebug(`extracted trace context from Eventbridge event`, { trace, event }); return trace; } catch (err) { @@ -355,21 +300,7 @@ export function readTraceFromSNSEvent(event: SNSEvent): TraceContext | undefined ); traceData = JSON.parse(b64Decoded); } - const traceID = traceData[traceIDHeader]; - const parentID = traceData[parentIDHeader]; - const sampledHeader = traceData[samplingPriorityHeader]; - - if (typeof traceID !== "string" || typeof parentID !== "string" || typeof sampledHeader !== "string") { - return; - } - const sampleMode = parseInt(sampledHeader, 10); - - const trace = { - parentID, - sampleMode, - source: Source.Event, - traceID, - }; + const trace = exportTraceData(traceData); logDebug(`extracted trace context from SNS event`, { trace, event }); return trace; } catch (err) { @@ -411,31 +342,43 @@ export function readTraceFromLambdaContext(context: any): TraceContext | undefin return; } - const traceID = traceData[traceIDHeader]; - if (typeof traceID !== "string") { - return; - } - const parentID = traceData[parentIDHeader]; - if (typeof parentID !== "string") { - return; - } - const sampledHeader = traceData[samplingPriorityHeader]; - if (typeof sampledHeader !== "string") { - return; - } - const sampleMode = parseInt(sampledHeader, 10); - - const trace = { - parentID, - sampleMode, - source: Source.Event, - traceID, - }; + const trace = exportTraceData(traceData); logDebug(`extracted trace context from lambda context`, { trace, context }); return trace; } -export function readTraceFromHTTPEvent(event: any): TraceContext | undefined { +export function getInjectedAuthorizerData(event: any, eventSourceSubType: eventSubTypes) { + const authorizerHeaders = event?.requestContext?.authorizer; + if (!authorizerHeaders) return null; + const rawDatadogData = + eventSourceSubType === eventSubTypes.apiGatewayV2 ? authorizerHeaders.lambda._datadog : authorizerHeaders._datadog; + if (!rawDatadogData) return null; + const injectedData = JSON.parse(Buffer.from(rawDatadogData, "base64").toString()); + // use the injected requestId to tell if it's the authorizing invocation (not cached) + if ( + authorizerHeaders.integrationLatency > 0 || + event.requestContext.requestId === injectedData[authorizingRequestIdHeader] + ) { + return injectedData; + } else { + return null; + } +} + +export function readTraceFromHTTPEvent(event: any, decodeAuthorizerContext: boolean = true): TraceContext | undefined { + if (decodeAuthorizerContext) { + // need to set the trace context if using authorizer lambda in authorizing (non-cached) cases + try { + const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event); + const injectedAuthorizerData = getInjectedAuthorizerData(event, eventSourceSubType); + if (injectedAuthorizerData !== null) { + return exportTraceData(injectedAuthorizerData); + } + } catch (error) { + logDebug(`unable to extract trace context from authorizer event.`, { error }); + } + } + const headers = event.headers; const lowerCaseHeaders: { [key: string]: string } = {}; @@ -443,43 +386,25 @@ export function readTraceFromHTTPEvent(event: any): TraceContext | undefined { lowerCaseHeaders[key.toLowerCase()] = headers[key]; } - const traceID = lowerCaseHeaders[traceIDHeader]; - if (typeof traceID !== "string") { - return; - } - const parentID = lowerCaseHeaders[parentIDHeader]; - if (typeof parentID !== "string") { - return; - } - const sampledHeader = lowerCaseHeaders[samplingPriorityHeader]; - if (typeof sampledHeader !== "string") { - return; - } - const sampleMode = parseInt(sampledHeader, 10); - - const trace = { - parentID, - sampleMode, - source: Source.Event, - traceID, - }; + const trace = exportTraceData(lowerCaseHeaders); logDebug(`extracted trace context from http event`, { trace, event }); return trace; } -export function readTraceFromEvent(event: any): TraceContext | undefined { +export function readTraceFromEvent(event: any, decodeAuthorizerContext: boolean = true): TraceContext | undefined { if (!event || typeof event !== "object") { return; } if (event.headers !== null && typeof event.headers === "object") { - return readTraceFromHTTPEvent(event); + return readTraceFromHTTPEvent(event, decodeAuthorizerContext); } if (isSNSEvent(event)) { return readTraceFromSNSEvent(event); } + if (isSNSSQSEvent(event)) { return readTraceFromSNSSQSEvent(event); } @@ -640,3 +565,22 @@ export function convertToAPMParentID(xrayParentID: string): string | undefined { } return hex.toString(10); } + +function exportTraceData(traceData: any): TraceContext | undefined { + const traceID = traceData[traceIDHeader]; + const parentID = traceData[parentIDHeader]; + const sampledHeader = traceData[samplingPriorityHeader]; + + if (typeof traceID !== "string" || typeof parentID !== "string" || typeof sampledHeader !== "string") { + return; + } + + const sampleMode = parseInt(sampledHeader, 10); + + return { + parentID, + sampleMode, + source: Source.Event, + traceID, + }; +} diff --git a/src/trace/listener.spec.ts b/src/trace/listener.spec.ts index 34ebb684..b6f30cc1 100644 --- a/src/trace/listener.spec.ts +++ b/src/trace/listener.spec.ts @@ -1,8 +1,17 @@ import { TraceExtractor, TraceListener } from "./listener"; -import { Source, ddtraceVersion } from "./constants"; +import { + Source, + ddtraceVersion, + parentIDHeader, + traceIDHeader, + samplingPriorityHeader, + parentSpanFinishTimeHeader, +} from "./constants"; import { datadogLambdaVersion } from "../constants"; import { Context } from "aws-lambda"; import { TraceHeaders } from "./trace-context-service"; +import { SpanWrapper } from "./span-wrapper"; +import { eventSubTypes } from "./trigger"; let mockWrap: jest.Mock; let mockExtract: jest.Mock; @@ -26,6 +35,15 @@ jest.mock("./tracer-wrapper", () => { extract(event: any): any { return mockExtract(event); } + + injectSpan(span: any): any { + return { + [parentIDHeader]: span.toSpanId(), + [traceIDHeader]: span.toTraceId(), + [samplingPriorityHeader]: 1, + [parentSpanFinishTimeHeader]: 1661189936981, + }; + } } return { TracerWrapper: MockTraceWrapper, @@ -59,6 +77,8 @@ describe("TraceListener", () => { autoPatchHTTP: true, captureLambdaPayload: false, createInferredSpan: true, + encodeAuthorizerContext: true, + decodeAuthorizerContext: true, mergeDatadogXrayTraces: false, injectLogContext: false, }; @@ -279,4 +299,40 @@ describe("TraceListener", () => { unwrappedFunc, ); }); + + it("injects authorizer context if it exists", async () => { + const listener = new TraceListener(defaultConfig); + mockTraceHeaders = { + "x-datadog-parent-id": "797643193680388251", + "x-datadog-sampling-priority": "2", + "x-datadog-trace-id": "4110911582297405551", + "x-datadog-parent-span-finish-time": "1661189936981", + }; + mockTraceSource = Source.Event; + const inferredSpan = new SpanWrapper( + { + toSpanId: () => { + return "797643193680388251"; + }, + toTraceId: () => { + return "4110911582297405551"; + }, + }, + { isAsync: true }, + ); + (listener as any).wrappedCurrentSpan = { + startTime: () => { + return 1661189936981; + }, + } as any; + + (listener as any).inferredSpan = inferredSpan; + + const result: any = {}; + listener.injectAuthorizerSpan(result, "randomId", 1661189936981); + + expect(result.context._datadog).toBe( + "eyJ4LWRhdGFkb2ctcGFyZW50LWlkIjoiNzk3NjQzMTkzNjgwMzg4MjUxIiwieC1kYXRhZG9nLXRyYWNlLWlkIjoiNDExMDkxMTU4MjI5NzQwNTU1MSIsIngtZGF0YWRvZy1zYW1wbGluZy1wcmlvcml0eSI6MSwieC1kYXRhZG9nLXBhcmVudC1zcGFuLWZpbmlzaC10aW1lIjoxNjYxMTg5OTM2OTgxMDAwMDAwLCJ4LWRhdGFkb2ctYXV0aG9yaXppbmctcmVxdWVzdGlkIjoicmFuZG9tSWQifQ==", + ); + }); }); diff --git a/src/trace/listener.ts b/src/trace/listener.ts index 1c3a4c57..a335e181 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -8,17 +8,16 @@ import { } from "./context"; import { patchHttp, unpatchHttp } from "./patch-http"; import { TraceContextService } from "./trace-context-service"; -import { extractTriggerTags, extractHTTPStatusCodeTag } from "./trigger"; +import { extractTriggerTags, extractHTTPStatusCodeTag, eventSubTypes, parseEventSourceSubType } from "./trigger"; import { logDebug, tagObject } from "../utils"; import { didFunctionColdStart } from "../utils/cold-start"; import { datadogLambdaVersion } from "../constants"; -import { Source, ddtraceVersion } from "./constants"; +import { Source, ddtraceVersion, parentSpanFinishTimeHeader, authorizingRequestIdHeader } from "./constants"; import { patchConsole } from "./patch-console"; import { SpanContext, TraceOptions, TracerWrapper } from "./tracer-wrapper"; import { SpanInferrer } from "./span-inferrer"; import { SpanWrapper } from "./span-wrapper"; -import { incrementErrorsMetric, MetricsListener } from "../metrics"; export type TraceExtractor = (event: any, context: Context) => TraceContext; export interface TraceConfig { @@ -35,6 +34,14 @@ export interface TraceConfig { * Whether to create inferred spans for managed services */ createInferredSpan: boolean; + /** + * Whether to encode trace context in authorizer metadata + */ + encodeAuthorizerContext: boolean; + /** + * Whether to decode trace context in authorizer metadata + */ + decodeAuthorizerContext: boolean; /** * Whether to automatically patch console.log with Datadog's tracing ids. */ @@ -87,7 +94,12 @@ export class TraceListener { } else { logDebug("Not patching HTTP libraries", { autoPatchHTTP: this.config.autoPatchHTTP, tracerInitialized }); } - const rootTraceHeaders = this.contextService.extractHeadersFromContext(event, context, this.config.traceExtractor); + const rootTraceHeaders = this.contextService.extractHeadersFromContext( + event, + context, + this.config.traceExtractor, + this.config.decodeAuthorizerContext, + ); // The aws.lambda span needs to have a parented to the Datadog trace context from the // incoming event if available or the X-Ray trace context if hybrid tracing is enabled let parentSpanContext: SpanContext | undefined; @@ -101,7 +113,12 @@ export class TraceListener { }); } if (this.config.createInferredSpan) { - this.inferredSpan = this.inferrer.createInferredSpan(event, context, parentSpanContext); + this.inferredSpan = this.inferrer.createInferredSpan( + event, + context, + parentSpanContext, + this.config.encodeAuthorizerContext, + ); } this.lambdaSpanParentContext = this.inferredSpan?.span || parentSpanContext; this.context = context; @@ -123,7 +140,6 @@ export class TraceListener { // Guard clause if something has gone horribly wrong // so we won't crash user code. if (!this.tracerWrapper.currentSpan) return false; - this.wrappedCurrentSpan = new SpanWrapper(this.tracerWrapper.currentSpan, {}); if (shouldTagPayload) { tagObject(this.tracerWrapper.currentSpan, "function.request", event); @@ -150,7 +166,24 @@ export class TraceListener { return false; } - public async onCompleteInvocation(error?: any) { + injectAuthorizerSpan(result: any, requestId: string, finishTime: number): any { + if (!result.context) { + result.context = {}; + } + const injectedHeaders = { + ...this.tracerWrapper.injectSpan(this.inferredSpan?.span || this.wrappedCurrentSpan?.span), + [parentSpanFinishTimeHeader]: finishTime * 1e6, + // used as the start time in the authorizer span + // padding 1e6 in case this nodejs authorizer is used for a python main lambda function + }; + if (requestId) { + // undefined in token-type authorizer + injectedHeaders[authorizingRequestIdHeader] = requestId; + } + result.context._datadog = Buffer.from(JSON.stringify(injectedHeaders)).toString("base64"); + } + + public async onCompleteInvocation(error?: any, result?: any, event?: any) { // Create a new dummy Datadog subsegment for function trigger tags so we // can attach them to X-Ray spans when hybrid tracing is used if (this.triggerTags) { @@ -162,6 +195,7 @@ export class TraceListener { logDebug("Unpatching HTTP libraries"); unpatchHttp(); } + let finishTime = this.wrappedCurrentSpan?.endTime(); if (this.inferredSpan) { logDebug("Finishing inferred span"); @@ -169,10 +203,17 @@ export class TraceListener { logDebug("Setting error tag to inferred span"); this.inferredSpan.setTag("error", error); } - - const finishTime = this.inferredSpan.isAsync() ? this.wrappedCurrentSpan?.startTime() : Date.now(); + if (this.inferredSpan.isAsync()) { + finishTime = this.wrappedCurrentSpan?.startTime() || Date.now(); + } else { + finishTime = Date.now(); + } this.inferredSpan.finish(finishTime); } + if (this.config.encodeAuthorizerContext && result?.principalId && result?.policyDocument) { + // We're in an authorizer, pass on the trace context, requestId and finishTime to make the authorizer span + this.injectAuthorizerSpan(result, event?.requestContext?.requestId, finishTime || Date.now()); + } } public onWrap any>(func: T): T { diff --git a/src/trace/span-inferrer.spec.ts b/src/trace/span-inferrer.spec.ts index b932e710..656d52c5 100644 --- a/src/trace/span-inferrer.spec.ts +++ b/src/trace/span-inferrer.spec.ts @@ -9,6 +9,14 @@ const eventBridgeEvent = require("../../event_samples/eventbridge.json"); const webSocketEvent = require("../../event_samples/api-gateway-wss.json"); const apiGatewayV1 = require("../../event_samples/api-gateway-v1.json"); const apiGatewayV2 = require("../../event_samples/api-gateway-v2.json"); +const apiGatewayV1RequestAuthorizer = require("../../event_samples/api-gateway-traced-authorizer-request-v1.json"); +const apiGatewayV1RequestAuthorizerCached = require("../../event_samples/api-gateway-traced-authorizer-request-v1-cached.json"); +const apiGatewayV1TokenAuthorizer = require("../../event_samples/api-gateway-traced-authorizer-token-v1.json"); +const apiGatewayV1TokenAuthorizerCached = require("../../event_samples/api-gateway-traced-authorizer-token-v1-cached.json"); +const apiGatewayV2RequestAuthorizer = require("../../event_samples/api-gateway-traced-authorizer-request-v2.json"); +const apiGatewayV2TokenAuthorizerCached = require("../../event_samples/api-gateway-traced-authorizer-request-v2-cached.json"); +const apiGatewayWSSRequestAuthorizerConnect = require("../../event_samples/api-gateway-traced-authorizer-request-websocket-connect.json"); +const apiGatewayWSSRequestAuthorizerMessage = require("../../event_samples/api-gateway-traced-authorizer-request-websocket-message.json"); const s3Event = require("../../event_samples/s3.json"); const functionUrlEvent = require("../../event_samples/lambda-function-urls.json"); const mockWrapper = { @@ -199,7 +207,7 @@ describe("SpanInferrer", () => { expect(mockWrapper.startSpan).toBeCalledWith("aws.apigateway", { childOf: {}, - startTime: undefined, + startTime: 1642607783913, tags: { _inferred_span: { synchronicity: "sync", tag_source: "self" }, apiid: "08se3mvh28", @@ -225,7 +233,7 @@ describe("SpanInferrer", () => { expect(mockWrapper.startSpan).toBeCalledWith("aws.apigateway", { childOf: {}, - startTime: undefined, + startTime: 1583349317135, tags: { _inferred_span: { synchronicity: "sync", tag_source: "self" }, apiid: "id", @@ -320,3 +328,320 @@ describe("SpanInferrer", () => { }); }); }); + +const mockFinish = () => undefined; + +describe("Authorizer Spans", () => { + const mockWrapperWithFinish = { + startSpan: jest.fn(() => { + return { + finish: mockFinish, + }; + }), + }; + + beforeEach(() => { + mockWrapperWithFinish.startSpan = jest.fn(() => { + return { + finish: mockFinish, + }; + }); + }); + + afterEach(() => { + mockWrapperWithFinish.startSpan.mockReset(); + }); + + it("creates an inferred span for API Gateway V1 event with traced authorizers [Request Type]", () => { + const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper); + inferrer.createInferredSpan(apiGatewayV1RequestAuthorizer, {} as any, {} as SpanContext); + expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([ + "aws.apigateway.authorizer", + { + childOf: {}, + startTime: 1660939857052, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "3gsxz7lha4", + domain_name: "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + endpoint: "/dev/hello", + "http.method": "POST", + "http.url": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com/dev/hello", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "POST /hello", + resource_names: "POST /hello", + service: "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "service.name": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + stage: "dev", + }, + }, + ]); + expect(mockWrapperWithFinish.startSpan.mock.calls[1]).toEqual([ + "aws.apigateway", + { + childOf: { finish: mockFinish }, // Hack around jest mocks + startTime: 1660939857075, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "3gsxz7lha4", + domain_name: "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + endpoint: "/dev/hello", + "http.method": "POST", + "http.url": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com/dev/hello", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "POST /hello", + resource_names: "POST /hello", + service: "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "service.name": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + stage: "dev", + }, + }, + ]); + }); + + it("No inferred span for API Gateway V1 event with CACHED authorizers [Request Type]", () => { + const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper); + inferrer.createInferredSpan(apiGatewayV1RequestAuthorizerCached, {} as any, {} as SpanContext); + expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([ + "aws.apigateway", + { + childOf: {}, // Hack around jest mocks + startTime: 1660939855656, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "3gsxz7lha4", + domain_name: "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + endpoint: "/dev/hello", + "http.method": "POST", + "http.url": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com/dev/hello", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "POST /hello", + resource_names: "POST /hello", + service: "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "service.name": "3gsxz7lha4.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + stage: "dev", + }, + }, + ]); + }); + + it("creates an inferred span for API Gateway V1 event with traced authorizers [Token Type]", () => { + const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper); + inferrer.createInferredSpan(apiGatewayV1TokenAuthorizer, {} as any, {} as SpanContext); + expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([ + "aws.apigateway.authorizer", + { + childOf: {}, + startTime: 1666803196780, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "4dyr9xqip7", + domain_name: "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + endpoint: "/dev/hi", + "http.method": "GET", + "http.url": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com/dev/hi", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "GET /hi", + resource_names: "GET /hi", + service: "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "service.name": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + stage: "dev", + }, + }, + ]); + expect(mockWrapperWithFinish.startSpan.mock.calls[1]).toEqual([ + "aws.apigateway", + { + childOf: { finish: mockFinish }, // Hack around jest mocks + startTime: 1666803196783, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "4dyr9xqip7", + domain_name: "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + endpoint: "/dev/hi", + "http.method": "GET", + "http.url": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com/dev/hi", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "GET /hi", + resource_names: "GET /hi", + service: "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "service.name": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + stage: "dev", + }, + }, + ]); + }); + + it("No inferred span for API Gateway V1 event with CACHED authorizers [Token Type]", () => { + const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper); + inferrer.createInferredSpan(apiGatewayV1TokenAuthorizerCached, {} as any, {} as SpanContext); + expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([ + "aws.apigateway", + { + childOf: {}, // Hack around jest mocks + startTime: 1666803234094, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "4dyr9xqip7", + domain_name: "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + endpoint: "/dev/hi", + "http.method": "GET", + "http.url": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com/dev/hi", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "GET /hi", + resource_names: "GET /hi", + service: "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "service.name": "4dyr9xqip7.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + stage: "dev", + }, + }, + ]); + }); + + it("connects the inferred span for API Gateway V2 event with traced authorizers [Request Type]", () => { + const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper); + inferrer.createInferredSpan(apiGatewayV2RequestAuthorizer, {} as any, {} as SpanContext); + expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([ + "aws.apigateway", + { + childOf: {}, + startTime: 1665596771812, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "l9flvsey83", + domain_name: "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + endpoint: "/hello", + "http.method": "GET", + "http.url": "l9flvsey83.execute-api.sa-east-1.amazonaws.com/hello", + operation_name: "aws.httpapi", + request_id: undefined, + "resource.name": "GET /hello", + resource_names: "GET /hello", + service: "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + "service.name": "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + stage: "$default", + }, + }, + ]); + }); + + it("No inferred span for API Gateway V2 event with CACHED authorizers [Request Type]", () => { + const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper); + inferrer.createInferredSpan(apiGatewayV2TokenAuthorizerCached, {} as any, {} as SpanContext); + expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([ + "aws.apigateway", + { + childOf: {}, + startTime: 1665596856876, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "l9flvsey83", + domain_name: "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + endpoint: "/hello", + "http.method": "GET", + "http.url": "l9flvsey83.execute-api.sa-east-1.amazonaws.com/hello", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "GET /hello", + resource_names: "GET /hello", + service: "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + "service.name": "l9flvsey83.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + stage: "$default", + }, + }, + ]); + }); + + it("creates an inferred span for API Gateway Websocket Connect event with traced authorizers [Request Type]", () => { + const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper); + inferrer.createInferredSpan(apiGatewayWSSRequestAuthorizerConnect, {} as any, {} as SpanContext); + expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([ + "aws.apigateway.authorizer", + { + childOf: {}, + startTime: 1666633566931, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "85fj5nw29d", + connection_id: "ahVWscZqmjQCI1w=", + endpoint: "$connect", + event_type: "CONNECT", + "http.url": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com$connect", + message_direction: "IN", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com $connect", + resource_names: "85fj5nw29d.execute-api.sa-east-1.amazonaws.com $connect", + service: "85fj5nw29d.execute-api.sa-east-1.amazonaws.com", + "service.name": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + }, + }, + ]); + expect(mockWrapperWithFinish.startSpan.mock.calls[1]).toEqual([ + "aws.apigateway", + { + childOf: { finish: mockFinish }, // Hack around jest mocks + startTime: 1666633566947, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "85fj5nw29d", + connection_id: "ahVWscZqmjQCI1w=", + endpoint: "$connect", + event_type: "CONNECT", + "http.url": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com$connect", + message_direction: "IN", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com $connect", + resource_names: "85fj5nw29d.execute-api.sa-east-1.amazonaws.com $connect", + service: "85fj5nw29d.execute-api.sa-east-1.amazonaws.com", + "service.name": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + }, + }, + ]); + }); + + it("No inferred span for API Gateway Websocket Message event with traced authorizers [Request Type]", () => { + const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper); + inferrer.createInferredSpan(apiGatewayWSSRequestAuthorizerMessage, {} as any, {} as SpanContext); + expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([ + "aws.apigateway", + { + childOf: {}, + startTime: 1666633666203, + tags: { + _inferred_span: { synchronicity: "sync", tag_source: "self" }, + apiid: "85fj5nw29d", + connection_id: "ahVWscZqmjQCI1w=", + endpoint: "hello", + event_type: "MESSAGE", + "http.url": "85fj5nw29d.execute-api.sa-east-1.amazonaws.comhello", + message_direction: "IN", + operation_name: "aws.apigateway", + request_id: undefined, + "resource.name": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com hello", + resource_names: "85fj5nw29d.execute-api.sa-east-1.amazonaws.com hello", + service: "85fj5nw29d.execute-api.sa-east-1.amazonaws.com", + "service.name": "85fj5nw29d.execute-api.sa-east-1.amazonaws.com", + "span.type": "http", + }, + }, + ]); + }); +}); diff --git a/src/trace/span-inferrer.ts b/src/trace/span-inferrer.ts index 895aefd7..31c3b8f0 100644 --- a/src/trace/span-inferrer.ts +++ b/src/trace/span-inferrer.ts @@ -9,38 +9,48 @@ import { SQSEvent, } from "aws-lambda"; import { SpanContext, SpanOptions, TracerWrapper } from "./tracer-wrapper"; -import { eventSources, parseEventSource } from "./trigger"; +import { eventSubTypes, eventTypes, parseEventSource, parseEventSourceSubType } from "./trigger"; import { SpanWrapper } from "./span-wrapper"; +import { parentSpanFinishTimeHeader } from "./constants"; +import { logDebug } from "../utils"; +import { getInjectedAuthorizerData } from "./context"; +import { decodeAuthorizerContextEnvVar } from "../index"; + export class SpanInferrer { traceWrapper: TracerWrapper; constructor(traceWrapper: TracerWrapper) { this.traceWrapper = traceWrapper; } - public createInferredSpan(event: any, context: Context | undefined, parentSpanContext: SpanContext | undefined): any { + public createInferredSpan( + event: any, + context: Context | undefined, + parentSpanContext: SpanContext | undefined, + decodeAuthorizerContext: boolean = true, + ): any { const eventSource = parseEventSource(event); - if (eventSource === eventSources.lambdaUrl) { + if (eventSource === eventTypes.lambdaUrl) { return this.createInferredSpanForLambdaUrl(event, context); } - if (eventSource === eventSources.apiGateway) { - return this.createInferredSpanForApiGateway(event, context, parentSpanContext); + if (eventSource === eventTypes.apiGateway) { + return this.createInferredSpanForApiGateway(event, context, parentSpanContext, decodeAuthorizerContext); } - if (eventSource === eventSources.sns) { + if (eventSource === eventTypes.sns) { return this.createInferredSpanForSns(event, context, parentSpanContext); } - if (eventSource === eventSources.dynamoDB) { + if (eventSource === eventTypes.dynamoDB) { return this.createInferredSpanForDynamoDBStreamEvent(event, context, parentSpanContext); } - if (eventSource === eventSources.sqs) { + if (eventSource === eventTypes.sqs) { return this.createInferredSpanForSqs(event, context, parentSpanContext); } - if (eventSource === eventSources.kinesis) { + if (eventSource === eventTypes.kinesis) { return this.createInferredSpanForKinesis(event, context, parentSpanContext); } - if (eventSource === eventSources.s3) { + if (eventSource === eventTypes.s3) { return this.createInferredSpanForS3(event, context, parentSpanContext); } - if (eventSource === eventSources.eventBridge) { + if (eventSource === eventTypes.eventBridge) { return this.createInferredSpanForEventBridge(event, context, parentSpanContext); } } @@ -56,6 +66,7 @@ export class SpanInferrer { event: any, context: Context | undefined, parentSpanContext: SpanContext | undefined, + decodeAuthorizerContext: boolean = true, ): SpanWrapper { const options: SpanOptions = {}; const domain = event.requestContext.domainName; @@ -97,10 +108,50 @@ export class SpanInferrer { options.tags.connection_id = event.requestContext.connectionId; options.tags.event_type = event.requestContext.eventType; } - if (parentSpanContext) { - options.childOf = parentSpanContext; + let upstreamAuthorizerSpan: SpanWrapper | undefined; + const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event); + if (decodeAuthorizerContext) { + try { + const parsedUpstreamContext = getInjectedAuthorizerData(event, eventSourceSubType); + if (parsedUpstreamContext) { + let upstreamSpanOptions: SpanOptions = {}; + const startTime = parsedUpstreamContext[parentSpanFinishTimeHeader] / 1e6; + // getting an approximated endTime + if (eventSourceSubType === eventSubTypes.apiGatewayV2) { + options.startTime = startTime; // not inserting authorizer span + options.tags.operation_name = "aws.httpapi"; + } else { + upstreamSpanOptions = { + startTime, + childOf: parentSpanContext, + tags: { operation_name: "aws.apigateway.authorizer", ...options.tags }, + }; + upstreamAuthorizerSpan = new SpanWrapper( + this.traceWrapper.startSpan("aws.apigateway.authorizer", upstreamSpanOptions), + { isAsync: false }, + ); + const endTime = event.requestContext.requestTimeEpoch + event.requestContext.authorizer.integrationLatency; + upstreamAuthorizerSpan.finish(endTime); + options.startTime = endTime; // For the main function's inferred span + } + } + } catch (error) { + logDebug("Error decoding authorizer span", error as Error); + } } - options.startTime = event.requestContext.timeEpoch; + + if (!options.startTime) { + if ( + eventSourceSubType === eventSubTypes.apiGatewayV1 || + eventSourceSubType === eventSubTypes.apiGatewayWebsocket + ) { + options.startTime = event.requestContext.requestTimeEpoch; + } else { + options.startTime = event.requestContext.timeEpoch; + } + } + options.childOf = upstreamAuthorizerSpan ? upstreamAuthorizerSpan.span : parentSpanContext; + const spanWrapperOptions = { isAsync: this.isApiGatewayAsync(event) === "async", }; diff --git a/src/trace/span-wrapper.ts b/src/trace/span-wrapper.ts index c68412a5..ff217e21 100644 --- a/src/trace/span-wrapper.ts +++ b/src/trace/span-wrapper.ts @@ -20,7 +20,13 @@ export class SpanWrapper { } public endTime(): number { - return this.span._endTime; + if (this.span._endTime) { + return this.span._endTime; + } + if (this.span._duration && this.span._startTime) { + return this.span._startTime + this.span._duration; + } + throw new Error("_endTime not defined"); } public finish(timestamp = Date.now()): void { diff --git a/src/trace/trace-context-service.ts b/src/trace/trace-context-service.ts index bdec7989..72088582 100644 --- a/src/trace/trace-context-service.ts +++ b/src/trace/trace-context-service.ts @@ -25,8 +25,9 @@ export class TraceContextService { event: any, context: Context, extractor?: TraceExtractor, + decodeAuthorizerContext: boolean = true, ): Partial | undefined { - this.rootTraceContext = extractTraceContext(event, context, extractor); + this.rootTraceContext = extractTraceContext(event, context, extractor, decodeAuthorizerContext); return this.currentTraceHeaders; } diff --git a/src/trace/tracer-wrapper.ts b/src/trace/tracer-wrapper.ts index 3022d71d..896b46d7 100644 --- a/src/trace/tracer-wrapper.ts +++ b/src/trace/tracer-wrapper.ts @@ -93,4 +93,10 @@ export class TracerWrapper { traceID, }; } + + public injectSpan(span: SpanContext): any { + const dest = {}; + this.tracer.inject(span, "text_map", dest); + return dest; + } } diff --git a/src/trace/trigger.ts b/src/trace/trigger.ts index 2fdb9a74..66e60828 100644 --- a/src/trace/trigger.ts +++ b/src/trace/trigger.ts @@ -92,7 +92,7 @@ function extractEventBridgeARN(event: EventBridgeEvent) { return event.source; } -export enum eventSources { +export enum eventTypes { apiGateway = "api-gateway", applicationLoadBalancer = "application-load-balancer", cloudFront = "cloudfront", @@ -108,6 +108,28 @@ export enum eventSources { sqs = "sqs", } +export enum eventSubTypes { + apiGatewayV1 = "api-gateway-rest-api", + apiGatewayV2 = "api-gateway-http-api", + apiGatewayWebsocket = "api-gateway-websocket", + unknown = "unknown-sub-type", +} + +export function parseEventSourceSubType(event: any): eventSubTypes { + if (eventType.isAPIGatewayEvent(event)) { + return eventSubTypes.apiGatewayV1; + } + + if (eventType.isAPIGatewayEventV2(event)) { + return eventSubTypes.apiGatewayV2; + } + + if (eventType.isAPIGatewayWebsocketEvent(event)) { + return eventSubTypes.apiGatewayWebsocket; + } + + return eventSubTypes.unknown; +} /** * parseEventSource parses the triggering event to determine the source * Possible Returns: @@ -116,53 +138,53 @@ export enum eventSources { */ export function parseEventSource(event: any) { if (eventType.isLambdaUrlEvent(event)) { - return eventSources.lambdaUrl; + return eventTypes.lambdaUrl; } if ( eventType.isAPIGatewayEvent(event) || eventType.isAPIGatewayEventV2(event) || eventType.isAPIGatewayWebsocketEvent(event) ) { - return eventSources.apiGateway; + return eventTypes.apiGateway; } if (eventType.isALBEvent(event)) { - return eventSources.applicationLoadBalancer; + return eventTypes.applicationLoadBalancer; } if (eventType.isCloudWatchLogsEvent(event)) { - return eventSources.cloudWatchLogs; + return eventTypes.cloudWatchLogs; } if (eventType.isCloudWatchEvent(event)) { - return eventSources.cloudWatchEvents; + return eventTypes.cloudWatchEvents; } if (eventType.isCloudFrontRequestEvent(event)) { - return eventSources.cloudFront; + return eventTypes.cloudFront; } if (eventType.isDynamoDBStreamEvent(event)) { - return eventSources.dynamoDB; + return eventTypes.dynamoDB; } if (eventType.isKinesisStreamEvent(event)) { - return eventSources.kinesis; + return eventTypes.kinesis; } if (eventType.isS3Event(event)) { - return eventSources.s3; + return eventTypes.s3; } if (eventType.isSNSEvent(event)) { - return eventSources.sns; + return eventTypes.sns; } if (eventType.isSQSEvent(event)) { - return eventSources.sqs; + return eventTypes.sqs; } if (eventType.isEventBridgeEvent(event)) { - return eventSources.eventBridge; + return eventTypes.eventBridge; } }