diff --git a/package-lock.json b/package-lock.json index cd2eb7e6aa..f8e6f77d0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,8 +31,8 @@ "lerna-changelog": "2.2.0", "markdownlint-cli2": "0.13.0", "minimatch": "^9.0.3", - "nx": "15.9.7", "mocha": "^10.7.3", + "nx": "15.9.7", "prettier": "2.8.8", "process": "0.11.10", "semver": "^7.6.0", @@ -95,6 +95,8 @@ "devDependencies": { "@opentelemetry/api": "^1.0.0", "@opentelemetry/contrib-test-utils": "^0.42.0", + "@opentelemetry/instrumentation-fs": "^0.16.0", + "@opentelemetry/instrumentation-http": "^0.54.0", "@types/mocha": "8.2.3", "@types/node": "18.18.14", "@types/sinon": "10.0.20", @@ -138,6 +140,7 @@ "devDependencies": { "@opentelemetry/api": "^1.0.0", "@opentelemetry/contrib-test-utils": "^0.42.0", + "@opentelemetry/instrumentation-http": "^0.54.0", "@types/mocha": "8.2.3", "@types/node": "18.18.14", "@types/sinon": "10.0.20", @@ -180,7 +183,7 @@ "devDependencies": { "@opentelemetry/api": "^1.0.0", "@opentelemetry/contrib-test-utils": "^0.42.0", - "@opentelemetry/instrumentation-fs": "*", + "@opentelemetry/instrumentation-fs": "^0.16.0", "@types/mocha": "8.2.3", "@types/node": "18.18.14", "@types/sinon": "10.0.20", @@ -226,6 +229,7 @@ "devDependencies": { "@opentelemetry/api": "^1.0.0", "@opentelemetry/contrib-test-utils": "^0.42.0", + "@opentelemetry/instrumentation-http": "^0.54.0", "@types/mocha": "8.2.3", "@types/node": "18.18.14", "@types/semver": "7.5.8", @@ -36913,6 +36917,7 @@ }, "devDependencies": { "@opentelemetry/api": "^1.3.0", + "@opentelemetry/contrib-test-utils": "^0.40.0", "@opentelemetry/core": "^1.8.0", "@opentelemetry/propagator-aws-xray": "^1.25.1", "@opentelemetry/propagator-aws-xray-lambda": "^0.52.1", @@ -36932,6 +36937,72 @@ "@opentelemetry/api": "^1.3.0" } }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dev": true, + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/contrib-test-utils": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/contrib-test-utils/-/contrib-test-utils-0.40.0.tgz", + "integrity": "sha512-hsxA+i4RfM7z46yoSAza7ugE5bMRu82sZm1UlZiB9mUeBKUFyQO24Vslr1bu5jGmeu1XI25Lb1TAF95KX06K/A==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/exporter-jaeger": "^1.3.1", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/resources": "^1.8.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.8.0", + "@opentelemetry/sdk-trace-node": "^1.8.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/contrib-test-utils/node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/core": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", @@ -36956,6 +37027,146 @@ "node": ">=14" } }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/propagator-aws-xray": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-1.25.1.tgz", @@ -36986,6 +37197,197 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dev": true, + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@types/node": { "version": "18.18.14", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.14.tgz", @@ -47469,6 +47871,7 @@ "version": "file:plugins/node/opentelemetry-instrumentation-aws-lambda", "requires": { "@opentelemetry/api": "^1.3.0", + "@opentelemetry/contrib-test-utils": "^0.40.0", "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.54.0", "@opentelemetry/propagator-aws-xray": "^1.25.1", @@ -47485,6 +47888,54 @@ "typescript": "4.4.4" }, "dependencies": { + "@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dev": true, + "requires": { + "@opentelemetry/api": "^1.0.0" + } + }, + "@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "dev": true, + "requires": {} + }, + "@opentelemetry/contrib-test-utils": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/contrib-test-utils/-/contrib-test-utils-0.40.0.tgz", + "integrity": "sha512-hsxA+i4RfM7z46yoSAza7ugE5bMRu82sZm1UlZiB9mUeBKUFyQO24Vslr1bu5jGmeu1XI25Lb1TAF95KX06K/A==", + "dev": true, + "requires": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/exporter-jaeger": "^1.3.1", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/resources": "^1.8.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.8.0", + "@opentelemetry/sdk-trace-node": "^1.8.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "dependencies": { + "@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dev": true, + "requires": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + } + } + } + }, "@opentelemetry/core": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", @@ -47502,6 +47953,103 @@ } } }, + "@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dev": true, + "requires": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + } + }, + "@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + } + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + } + }, + "@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "dependencies": { + "@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true + } + } + }, + "@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + } + }, + "@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dev": true, + "requires": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + } + }, + "@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dev": true, + "requires": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + } + }, "@opentelemetry/propagator-aws-xray": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-1.25.1.tgz", @@ -47520,6 +48068,140 @@ "@opentelemetry/propagator-aws-xray": "1.25.1" } }, + "@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1" + } + }, + "@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1" + } + }, + "@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "dependencies": { + "@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true + } + } + }, + "@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dev": true, + "requires": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + } + }, + "@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + } + }, + "@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dev": true, + "requires": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "dependencies": { + "@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dev": true, + "requires": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true + } + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "dependencies": { + "@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true + } + } + }, + "@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dev": true, + "requires": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + } + }, "@types/node": { "version": "18.18.14", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.14.tgz", @@ -50512,6 +51194,8 @@ "@opentelemetry/api": "^1.0.0", "@opentelemetry/contrib-test-utils": "^0.42.0", "@opentelemetry/core": "^1.0.0", + "@opentelemetry/instrumentation-fs": "^0.16.0", + "@opentelemetry/instrumentation-http": "^0.54.0", "@opentelemetry/resources": "^1.10.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/mocha": "8.2.3", @@ -50547,6 +51231,7 @@ "@opentelemetry/api": "^1.0.0", "@opentelemetry/contrib-test-utils": "^0.42.0", "@opentelemetry/core": "^1.25.1", + "@opentelemetry/instrumentation-http": "^0.54.0", "@opentelemetry/resources": "^1.10.1", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/mocha": "8.2.3", @@ -50581,7 +51266,7 @@ "@opentelemetry/api": "^1.0.0", "@opentelemetry/contrib-test-utils": "^0.42.0", "@opentelemetry/core": "^1.26.0", - "@opentelemetry/instrumentation-fs": "*", + "@opentelemetry/instrumentation-fs": "^0.16.0", "@opentelemetry/resources": "^1.10.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/mocha": "8.2.3", @@ -50618,6 +51303,7 @@ "@opentelemetry/api": "^1.0.0", "@opentelemetry/contrib-test-utils": "^0.42.0", "@opentelemetry/core": "^1.0.0", + "@opentelemetry/instrumentation-http": "^0.54.0", "@opentelemetry/resources": "^1.10.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/mocha": "8.2.3", diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md b/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md index dd0568d652..e82b37da1a 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md @@ -35,6 +35,9 @@ const { registerInstrumentations } = require('@opentelemetry/instrumentation'); const provider = new NodeTracerProvider(); provider.register(); +// Note that this needs to appear after the tracer provider is registered, +// otherwise the instrumentation will not properly flush data after each +// lambda invocation. registerInstrumentations({ instrumentations: [ new AwsLambdaInstrumentation({ @@ -46,7 +49,7 @@ registerInstrumentations({ In your Lambda function configuration, add or update the `NODE_OPTIONS` environment variable to require the wrapper, e.g., -`NODE_OPTIONS=--require lambda-wrapper` +`NODE_OPTIONS=--require lambda-wrapper --experimental-loader @opentelemetry/instrumentation/hook.mjs` ## AWS Lambda Instrumentation Options diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/package.json b/plugins/node/opentelemetry-instrumentation-aws-lambda/package.json index 5fbf08999e..d50aa79a4a 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/package.json +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/package.json @@ -43,6 +43,7 @@ }, "devDependencies": { "@opentelemetry/api": "^1.3.0", + "@opentelemetry/contrib-test-utils": "^0.40.0", "@opentelemetry/core": "^1.8.0", "@opentelemetry/propagator-aws-xray": "^1.25.1", "@opentelemetry/propagator-aws-xray-lambda": "^0.52.1", diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts index 3dbe50e7ef..c3a47e2975 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts @@ -14,9 +14,6 @@ * limitations under the License. */ -import * as path from 'path'; -import * as fs from 'fs'; - import { InstrumentationBase, InstrumentationNodeModuleDefinition, @@ -55,7 +52,13 @@ import { import { AwsLambdaInstrumentationConfig, EventContextExtractor } from './types'; /** @knipignore */ import { PACKAGE_NAME, PACKAGE_VERSION } from './version'; -import { LambdaModule } from './internal-types'; +import { + isInvalidHandler, + moduleRootAndHandler, + resolveHandler, + splitHandlerString, + tryPath, +} from './user-function'; const headerGetter: TextMapGetter = { keys(carrier): string[] { @@ -88,69 +91,110 @@ export class AwsLambdaInstrumentation extends InstrumentationBase { + const [container, functionName] = resolveHandler( + moduleExports, + handlerPath + ); + if ( + container == null || + functionName == null || + typeof container[functionName] !== 'function' + ) { + this._diag.debug( + 'Skipping lambda instrumentation: _HANDLER/lambdaHandler did not resolve to a function.', + { + taskRoot, + handlerDef, + filename, + moduleRoot, + module, + handlerPath, + } + ); + return moduleExports; + } + + if (isWrapped(container[functionName])) { + this._unwrap(container, functionName); + } + this._wrap(container, functionName, this._getHandler(lambdaStartTime)); + return moduleExports; + }; + const unpatch = (moduleExports?: object) => { + if (moduleExports == null) return; + const [container, functionName] = resolveHandler( + moduleExports, + handlerPath + ); + if ( + container == null || + functionName == null || + typeof container[functionName] !== 'function' + ) { + return; + } + + this._unwrap(container, functionName); + }; + return [ new InstrumentationNodeModuleDefinition( - // NB: The patching infrastructure seems to match names backwards, this must be the filename, while - // InstrumentationNodeModuleFile must be the module name. + // The patching infrastructure properly supports absolute paths when registering hooks but not when + // actually matching against filenames when patching, so we need to provide a file instrumentation + // that will actually match by using a relative path. filename, ['*'], - undefined, - undefined, - [ - new InstrumentationNodeModuleFile( - module, - ['*'], - (moduleExports: LambdaModule) => { - if (isWrapped(moduleExports[functionName])) { - this._unwrap(moduleExports, functionName); - } - this._wrap( - moduleExports, - functionName, - this._getHandler(lambdaStartTime) - ); - return moduleExports; - }, - (moduleExports?: LambdaModule) => { - if (moduleExports == null) return; - this._unwrap(moduleExports, functionName); - } - ), - ] + patch, + unpatch, + [new InstrumentationNodeModuleFile(module, ['*'], patch, unpatch)] ), ]; } diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/src/user-function.ts b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/user-function.ts new file mode 100644 index 0000000000..2bb2ff6cf9 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/user-function.ts @@ -0,0 +1,123 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Adapted from https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/v3.1.0/src/UserFunction.js + */ + +import * as path from 'path'; +import * as fs from 'fs'; +import { LambdaModule } from './internal-types'; + +const FUNCTION_EXPR = /^([^.]*)\.(.*)$/; +const RELATIVE_PATH_SUBSTRING = '..'; + +/** + * Break the full handler string into two pieces, the module root and the actual + * handler string. + * Given './somepath/something/module.nestedobj.handler' this returns + * ['./somepath/something', 'module.nestedobj.handler'] + */ +export function moduleRootAndHandler( + fullHandlerString: string +): [moduleRoot: string, handler: string] { + const handlerString = path.basename(fullHandlerString); + const moduleRoot = fullHandlerString.substring( + 0, + fullHandlerString.indexOf(handlerString) + ); + return [moduleRoot, handlerString]; +} + +/** + * Split the handler string into two pieces: the module name and the path to + * the handler function. + */ +export function splitHandlerString( + handler: string +): [module: string | undefined, functionPath: string | undefined] { + const match = handler.match(FUNCTION_EXPR); + if (match && match.length === 3) { + return [match[1], match[2]]; + } else { + return [undefined, undefined]; + } +} + +/** + * Resolve the user's handler function key and its containing object from the module. + */ +export function resolveHandler( + object: object, + nestedProperty: string +): [container: LambdaModule | undefined, handlerKey: string | undefined] { + const nestedPropertyKeys = nestedProperty.split('.'); + const handlerKey = nestedPropertyKeys.pop(); + + const container = nestedPropertyKeys.reduce( + (nested, key) => { + return nested && (nested as Partial>)[key]; + }, + object + ); + + if (container) { + return [container as LambdaModule, handlerKey]; + } else { + return [undefined, undefined]; + } +} + +/** + * Attempt to determine the user's module path. + */ +export function tryPath( + appRoot: string, + moduleRoot: string, + module: string +): string | undefined { + const lambdaStylePath = path.resolve(appRoot, moduleRoot, module); + + const extensionless = fs.existsSync(lambdaStylePath); + if (extensionless) { + return lambdaStylePath; + } + + const extensioned = + (fs.existsSync(lambdaStylePath + '.js') && lambdaStylePath + '.js') || + (fs.existsSync(lambdaStylePath + '.mjs') && lambdaStylePath + '.mjs') || + (fs.existsSync(lambdaStylePath + '.cjs') && lambdaStylePath + '.cjs'); + if (extensioned) { + return extensioned; + } + + try { + const nodeStylePath = require.resolve(module, { + paths: [appRoot, moduleRoot], + }); + return nodeStylePath; + } catch { + return undefined; + } +} + +export function isInvalidHandler(fullHandlerString: string): boolean { + if (fullHandlerString.includes(RELATIVE_PATH_SUBSTRING)) { + return true; + } else { + return false; + } +} diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.force-flush.test.ts b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.force-flush.test.ts index 0a067dc758..31cffb00c3 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.force-flush.test.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.force-flush.test.ts @@ -20,6 +20,7 @@ import * as path from 'path'; import { AwsLambdaInstrumentation } from '../../src'; +import { load } from '../vendor/UserFunction'; import { BatchSpanProcessor, InMemorySpanExporter, @@ -71,8 +72,8 @@ describe('force flush', () => { instrumentation.setMeterProvider(provider); }; - const lambdaRequire = (module: string) => - require(path.resolve(__dirname, '..', module)); + const lambdaLoadHandler = (handler: string = process.env._HANDLER!) => + load(process.env.LAMBDA_TASK_ROOT!, handler); beforeEach(() => { oldEnv = { ...process.env }; @@ -100,18 +101,15 @@ describe('force flush', () => { provider.forceFlush = forceFlush; initializeHandlerTracing('lambda-test/sync.handler', provider); + const handler = await lambdaLoadHandler(); await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').handler( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); assert.strictEqual(forceFlushed, true); @@ -134,18 +132,15 @@ describe('force flush', () => { nodeTracerProvider.forceFlush = forceFlush; initializeHandlerTracing('lambda-test/sync.handler', provider); + const handler = await lambdaLoadHandler(); await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').handler( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); assert.strictEqual(forceFlushed, true); @@ -165,18 +160,15 @@ describe('force flush', () => { provider.forceFlush = forceFlush; initializeHandlerMetrics('lambda-test/sync.handler', provider); + const handler = await lambdaLoadHandler(); await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').handler( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); assert.strictEqual(forceFlushed, true); @@ -217,19 +209,16 @@ describe('force flush', () => { instrumentation.setMeterProvider(meterProvider); let callbackCount = 0; + const handler = await lambdaLoadHandler(); await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').handler( - 'arg', - ctx, - (err: Error, res: any) => { - callbackCount++; - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + callbackCount++; + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); assert.strictEqual(tracerForceFlushed, true); diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.test.ts b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.test.ts index 7b338cbd6d..cc0a47ece5 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.test.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.test.ts @@ -24,6 +24,8 @@ import { AwsLambdaInstrumentationConfig, lambdaMaxInitInMilliseconds, } from '../../src'; +import { load } from '../vendor/UserFunction'; +import { runTestFixture } from '@opentelemetry/contrib-test-utils'; import { BatchSpanProcessor, InMemorySpanExporter, @@ -129,8 +131,8 @@ describe('lambda handler', () => { return provider; }; - const lambdaRequire = (module: string) => - require(path.resolve(__dirname, '..', module)); + const lambdaLoadHandler = (handler: string = process.env._HANDLER!) => + load(process.env.LAMBDA_TASK_ROOT!, handler); const sampledAwsSpanContext: SpanContext = { traceId: '8a3c60f7d188f8fa79d48a391a778fa6', @@ -170,10 +172,21 @@ describe('lambda handler', () => { it('should export a valid span', async () => { initializeHandler('lambda-test/async.handler'); - const result = await lambdaRequire('lambda-test/async').handler( - 'arg', - ctx - ); + const handler = await lambdaLoadHandler(); + const result = await handler('arg', ctx); + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + const [span] = spans; + assert.strictEqual(spans.length, 1); + assertSpanSuccess(span); + assert.strictEqual(span.parentSpanId, undefined); + }); + + it('should support nested handlers', async () => { + initializeHandler('lambda-test/async.deeply.nested.handler'); + + const handler = await lambdaLoadHandler(); + const result = await handler('arg', ctx); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); const [span] = spans; @@ -185,9 +198,10 @@ describe('lambda handler', () => { it('should record error', async () => { initializeHandler('lambda-test/async.error'); + const handler = await lambdaLoadHandler(); let err: Error; try { - await lambdaRequire('lambda-test/async').error('arg', ctx); + await handler('arg', ctx); } catch (e: any) { err = e; } @@ -202,9 +216,10 @@ describe('lambda handler', () => { it('should record string error', async () => { initializeHandler('lambda-test/async.stringerror'); + const handler = await lambdaLoadHandler(); let err: string; try { - await lambdaRequire('lambda-test/async').stringerror('arg', ctx); + await handler('arg', ctx); } catch (e: any) { err = e; } @@ -218,10 +233,8 @@ describe('lambda handler', () => { it('context should have parent trace', async () => { initializeHandler('lambda-test/async.context'); - const result = await lambdaRequire('lambda-test/async').context( - 'arg', - ctx - ); + const handler = await lambdaLoadHandler(); + const result = await handler('arg', ctx); const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(span.spanContext().traceId, result); @@ -230,10 +243,8 @@ describe('lambda handler', () => { it('context should have parent trace', async () => { initializeHandler('lambda-test/async.context'); - const result = await lambdaRequire('lambda-test/async').context( - 'arg', - ctx - ); + const handler = await lambdaLoadHandler(); + const result = await handler('arg', ctx); const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(span.spanContext().traceId, result); @@ -244,18 +255,36 @@ describe('lambda handler', () => { it('should export a valid span', async () => { initializeHandler('lambda-test/sync.handler'); + const handler = await lambdaLoadHandler(); const result = await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').handler( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); + }); + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + const [span] = spans; + assert.strictEqual(spans.length, 1); + assertSpanSuccess(span); + assert.strictEqual(span.parentSpanId, undefined); + }); + + it('should support nested handlers', async () => { + initializeHandler('lambda-test/sync.deeply.nested.handler'); + + const handler = await lambdaLoadHandler(); + const result = await new Promise((resolve, reject) => { + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); + } + }); }); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); @@ -268,10 +297,9 @@ describe('lambda handler', () => { it('should record coldstart', async () => { initializeHandler('lambda-test/sync.handler'); - const handlerModule = lambdaRequire('lambda-test/sync'); - + const handler = await lambdaLoadHandler(); const result1 = await new Promise((resolve, reject) => { - handlerModule.handler('arg', ctx, (err: Error, res: any) => { + handler('arg', ctx, (err: Error, res: any) => { if (err) { reject(err); } else { @@ -281,7 +309,7 @@ describe('lambda handler', () => { }); const result2 = await new Promise((resolve, reject) => { - handlerModule.handler('arg', ctx, (err: Error, res: any) => { + handler('arg', ctx, (err: Error, res: any) => { if (err) { reject(err); } else { @@ -310,18 +338,15 @@ describe('lambda handler', () => { initializeHandler('lambda-test/sync.handler'); + const handler = await lambdaLoadHandler(); const result = await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').handler( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); @@ -337,18 +362,15 @@ describe('lambda handler', () => { lambdaStartTime: Date.now() - 2 * lambdaMaxInitInMilliseconds, }); + const handler = await lambdaLoadHandler(); const result = await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').handler( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); @@ -362,13 +384,10 @@ describe('lambda handler', () => { it('should record error', async () => { initializeHandler('lambda-test/sync.error'); + const handler = await lambdaLoadHandler(); let err: Error; try { - lambdaRequire('lambda-test/sync').error( - 'arg', - ctx, - (err: Error, res: any) => {} - ); + handler('arg', ctx, (err: Error, res: any) => {}); } catch (e: any) { err = e; } @@ -383,20 +402,17 @@ describe('lambda handler', () => { it('should record error in callback', async () => { initializeHandler('lambda-test/sync.callbackerror'); + const handler = await lambdaLoadHandler(); let err: Error; try { await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').callbackerror( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); } catch (e: any) { err = e; @@ -412,13 +428,10 @@ describe('lambda handler', () => { it('should record string error', async () => { initializeHandler('lambda-test/sync.stringerror'); + const handler = await lambdaLoadHandler(); let err: string; try { - lambdaRequire('lambda-test/sync').stringerror( - 'arg', - ctx, - (err: Error, res: any) => {} - ); + handler('arg', ctx, (err: Error, res: any) => {}); } catch (e: any) { err = e; } @@ -433,18 +446,15 @@ describe('lambda handler', () => { it('context should have parent trace', async () => { initializeHandler('lambda-test/sync.context'); + const handler = await lambdaLoadHandler(); const result = await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').context( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); const spans = memoryExporter.getFinishedSpans(); const [span] = spans; @@ -454,18 +464,15 @@ describe('lambda handler', () => { it('context should have parent trace', async () => { initializeHandler('lambda-test/sync.context'); + const handler = await lambdaLoadHandler(); const result = await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').context( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); const spans = memoryExporter.getFinishedSpans(); const [span] = spans; @@ -476,20 +483,17 @@ describe('lambda handler', () => { it('should record string error in callback', async () => { initializeHandler('lambda-test/sync.callbackstringerror'); + const handler = await lambdaLoadHandler(); let err: string; try { await new Promise((resolve, reject) => { - lambdaRequire('lambda-test/sync').callbackstringerror( - 'arg', - ctx, - (err: Error, res: any) => { - if (err) { - reject(err); - } else { - resolve(res); - } + handler('arg', ctx, (err: Error, res: any) => { + if (err) { + reject(err); + } else { + resolve(res); } - ); + }); }); } catch (e: any) { err = e; @@ -517,10 +521,8 @@ describe('lambda handler', () => { }, }; - const result = await lambdaRequire('lambda-test/async').handler( - proxyEvent, - ctx - ); + const handler = await lambdaLoadHandler(); + const result = await handler(proxyEvent, ctx); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); @@ -537,10 +539,8 @@ describe('lambda handler', () => { propagation.setGlobalPropagator(new AWSXRayLambdaPropagator()); initializeHandler('lambda-test/async.handler'); - const result = await lambdaRequire('lambda-test/async').handler( - 'arg', - ctx - ); + const handler = await lambdaLoadHandler(); + const result = await handler('arg', ctx); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); @@ -575,10 +575,8 @@ describe('lambda handler', () => { }, }; - const result = await lambdaRequire('lambda-test/async').handler( - otherEvent, - ctx - ); + const handler = await lambdaLoadHandler(); + const result = await handler(otherEvent, ctx); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); @@ -605,14 +603,12 @@ describe('lambda handler', () => { eventContextExtractor: customExtractor, }); + const handler = await lambdaLoadHandler(); const testSpan = provider.getTracer('test').startSpan('random_span'); await context.with( trace.setSpan(context.active(), testSpan), async () => { - await lambdaRequire('lambda-test/async').handler( - { message: 'event with no context' }, - ctx - ); + await handler({ message: 'event with no context' }, ctx); } ); @@ -631,7 +627,8 @@ describe('lambda handler', () => { }, }); - await lambdaRequire('lambda-test/async').handler('arg', ctx); + const handler = await lambdaLoadHandler(); + await handler('arg', ctx); const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); @@ -664,10 +661,8 @@ describe('lambda handler', () => { it('async - success', async () => { initializeHandler('lambda-test/async.handler', config); - const res = await lambdaRequire('lambda-test/async').handler( - 'arg', - ctx - ); + const handler = await lambdaLoadHandler(); + const res = await handler('arg', ctx); const [span] = memoryExporter.getFinishedSpans(); assert.strictEqual(span.attributes[RES_ATTR], res); }); @@ -675,9 +670,10 @@ describe('lambda handler', () => { it('async - error', async () => { initializeHandler('lambda-test/async.error', config); + const handler = await lambdaLoadHandler(); let err: Error; try { - await lambdaRequire('lambda-test/async').error('arg', ctx); + await handler('arg', ctx); } catch (e: any) { err = e; } @@ -688,12 +684,9 @@ describe('lambda handler', () => { it('sync - success', async () => { initializeHandler('lambda-test/sync.handler', config); + const handler = await lambdaLoadHandler(); const result = await new Promise((resolve, _reject) => { - lambdaRequire('lambda-test/sync').handler( - 'arg', - ctx, - (_err: Error, res: any) => resolve(res) - ); + handler('arg', ctx, (_err: Error, res: any) => resolve(res)); }); const [span] = memoryExporter.getFinishedSpans(); assert.strictEqual(span.attributes[RES_ATTR], result); @@ -702,9 +695,10 @@ describe('lambda handler', () => { it('sync - error', async () => { initializeHandler('lambda-test/sync.error', config); + const handler = await lambdaLoadHandler(); let err: Error; try { - lambdaRequire('lambda-test/sync').error('arg', ctx, () => {}); + handler('arg', ctx, () => {}); } catch (e: any) { err = e; } @@ -715,35 +709,79 @@ describe('lambda handler', () => { it('sync - error with callback', async () => { initializeHandler('lambda-test/sync.callbackerror', config); + const handler = await lambdaLoadHandler(); let error: Error; await new Promise((resolve, _reject) => { - lambdaRequire('lambda-test/sync').callbackerror( - 'arg', - ctx, - (err: Error, _res: any) => { - error = err; - resolve({}); - } - ); + handler('arg', ctx, (err: Error, _res: any) => { + error = err; + resolve({}); + }); }); const [span] = memoryExporter.getFinishedSpans(); assert.strictEqual(span.attributes[ERR_ATTR], error!.message); }); }); + }); - describe('.cjs lambda bundle', () => { - it('should export a valid span', async () => { - initializeHandler('lambda-test/commonjs.handler'); - const result = await lambdaRequire('lambda-test/commonjs.cjs').handler( - 'arg', - ctx - ); - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.strictEqual(span.parentSpanId, undefined); + describe('extensionless lambda bundle', () => { + it('should export a valid span', async () => { + initializeHandler('lambda-test/extensionless.handler'); + const handler = await lambdaLoadHandler(); + const result = await handler('arg', ctx); + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + const [span] = spans; + assert.strictEqual(spans.length, 1); + assertSpanSuccess(span); + assert.strictEqual(span.parentSpanId, undefined); + }); + }); + + describe('.cjs lambda bundle', () => { + it('should export a valid span', async () => { + initializeHandler('lambda-test/commonjs.handler'); + const handler = await lambdaLoadHandler(); + const result = await handler('arg', ctx); + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + const [span] = spans; + assert.strictEqual(spans.length, 1); + assertSpanSuccess(span); + assert.strictEqual(span.parentSpanId, undefined); + }); + }); + + describe('.mjs lambda bundle', () => { + it('should export a valid span', async () => { + await runTestFixture({ + cwd: path.dirname(__dirname), + argv: ['lambda-test/use-lambda.mjs'], + env: { + NODE_OPTIONS: + '--experimental-loader=@opentelemetry/instrumentation/hook.mjs', + NODE_NO_WARNINGS: '1', + }, + checkResult: err => { + assert.ifError(err); + }, + checkCollector: collector => { + const spans = collector.sortedSpans; + assert.strictEqual(spans.length, 1); + const span = spans[0]; + assert.strictEqual(span.kind, 2); + assert.strictEqual(span.name, 'my_function'); + assert.strictEqual( + span.attributes.find(a => a.key === SEMATTRS_FAAS_EXECUTION)?.value + .stringValue, + 'aws_request_id' + ); + assert.strictEqual( + span.attributes.find(a => a.key === 'faas.id')?.value.stringValue, + 'my_arn' + ); + assert.strictEqual(span.status.code, 0); + assert.strictEqual(span.status.message, undefined); + }, }); }); }); @@ -755,10 +793,8 @@ describe('lambda handler', () => { }); const otherEvent = {}; - const result = await lambdaRequire('lambda-test/async').handler( - otherEvent, - ctx - ); + const handler = await lambdaLoadHandler('lambda-test/async.handler'); + const result = await handler(otherEvent, ctx); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); @@ -768,4 +804,80 @@ describe('lambda handler', () => { assert.strictEqual(span.parentSpanId, undefined); }); }); + + describe('missing handler', () => { + it('should skip instrumentation', async () => { + const handler = await lambdaLoadHandler('lambda-test/async.handler'); + const result = await handler('arg', ctx); + + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + }); + }); + + describe('invalid handler string', () => { + it('should skip instrumentation', async () => { + initializeHandler('lambda-test/async..handler'); + + const handler = await lambdaLoadHandler('lambda-test/async.handler'); + const result = await handler('arg', ctx); + + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + }); + }); + + describe('missing handler function name', () => { + it('should skip instrumentation', async () => { + initializeHandler('lambda-test/async'); + + const handler = await lambdaLoadHandler('lambda-test/async.handler'); + const result = await handler('arg', ctx); + + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + }); + }); + + describe('non-existent handler module', () => { + it('should skip instrumentation', async () => { + initializeHandler('lambda-test/callback.handle'); + + const handler = await lambdaLoadHandler('lambda-test/async.handler'); + const result = await handler('arg', ctx); + + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + }); + }); + + describe('non-existent handler function', () => { + it('should skip instrumentation', async () => { + initializeHandler('lambda-test/async.handle'); + + const handler = await lambdaLoadHandler('lambda-test/async.handler'); + const result = await handler('arg', ctx); + + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + }); + }); + + describe('non-existent nested handler function', () => { + it('should skip instrumentation', async () => { + initializeHandler('lambda-test/async.nested.handle'); + + const handler = await lambdaLoadHandler('lambda-test/async.handler'); + const result = await handler('arg', ctx); + + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + }); + }); }); diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/async.js b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/async.js index c6c2e529cf..d7cdb1eca3 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/async.js +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/async.js @@ -19,19 +19,29 @@ exports.handler = async function (event, context) { return 'ok'; }; +exports.deeply = { + nested: { + handler: async function (event, context) { + return 'ok'; + }, + }, +}; + exports.error = async function (event, context) { throw new Error('handler error'); -} +}; exports.stringerror = async function (event, context) { throw 'handler error'; -} +}; exports.context = async function (event, context) { return api.trace.getSpan(api.context.active()).spanContext().traceId; }; exports.handler_return_baggage = async function (event, context) { - const [baggageEntryKey, baggageEntryValue] = api.propagation.getBaggage(api.context.active()).getAllEntries()[0]; + const [baggageEntryKey, baggageEntryValue] = api.propagation + .getBaggage(api.context.active()) + .getAllEntries()[0]; return `${baggageEntryKey}=${baggageEntryValue.value}`; -} +}; diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/commonjs.cjs b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/commonjs.cjs index 3fc61adccf..a14326637a 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/commonjs.cjs +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/commonjs.cjs @@ -1,3 +1,3 @@ exports.handler = async function (event, context) { - return "ok"; -}; \ No newline at end of file + return 'ok'; +}; diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/extensionless b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/extensionless new file mode 100644 index 0000000000..06c24dedcb --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/extensionless @@ -0,0 +1,4 @@ +#!/usr/bin/env node +exports.handler = async function (event, context) { + return 'ok'; +}; diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/module.mjs b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/module.mjs new file mode 100644 index 0000000000..e223a09a86 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/module.mjs @@ -0,0 +1,3 @@ +export async function handler(event, context) { + return 'ok'; +} diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/sync.js b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/sync.js index e9f38d8ea7..4dd043274e 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/sync.js +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/sync.js @@ -19,21 +19,29 @@ exports.handler = function (event, context, callback) { callback(null, 'ok'); }; +exports.deeply = { + nested: { + handler: function (event, context, callback) { + callback(null, 'ok'); + }, + }, +}; + exports.error = function (event, context, callback) { throw new Error('handler error'); -} +}; exports.callbackerror = function (event, context, callback) { callback(new Error('handler error')); -} +}; exports.stringerror = function (event, context, callback) { throw 'handler error'; -} +}; exports.callbackstringerror = function (event, context, callback) { callback('handler error'); -} +}; exports.context = function (event, context, callback) { callback(null, api.trace.getSpan(api.context.active()).spanContext().traceId); diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/use-lambda.mjs b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/use-lambda.mjs new file mode 100644 index 0000000000..04e4b80685 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/lambda-test/use-lambda.mjs @@ -0,0 +1,26 @@ +import * as path from 'path'; +import { fileURLToPath } from 'url'; + +import { trace } from '@opentelemetry/api'; +import { createTestNodeSdk } from '@opentelemetry/contrib-test-utils'; + +import { AwsLambdaInstrumentation } from '../../build/src/index.js'; +import { load } from '../vendor/UserFunction.js'; + +process.env.LAMBDA_TASK_ROOT = path.dirname(fileURLToPath(import.meta.url)); +process.env._HANDLER = 'module.handler'; + +const instrumentation = new AwsLambdaInstrumentation(); +const sdk = createTestNodeSdk({ + serviceName: 'use-lambda', + instrumentations: [instrumentation], +}); +sdk.start(); +instrumentation.setTracerProvider(trace.getTracerProvider()); + +const handler = await load(process.env.LAMBDA_TASK_ROOT, process.env._HANDLER); +await handler('arg', { + functionName: 'my_function', + invokedFunctionArn: 'my_arn', + awsRequestId: 'aws_request_id', +}); diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/Errors.js b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/Errors.js new file mode 100644 index 0000000000..d34c2f5bac --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/Errors.js @@ -0,0 +1,123 @@ +// https://raw.githubusercontent.com/aws/aws-lambda-nodejs-runtime-interface-client/v3.1.0/src/Errors.js + +/** + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Defines custom error types throwable by the runtime. + */ + +'use strict'; + +const util = require('util'); + +function _isError(obj) { + return ( + obj && + obj.name && + obj.message && + obj.stack && + typeof obj.name === 'string' && + typeof obj.message === 'string' && + typeof obj.stack === 'string' + ); +} + +function intoError(err) { + if (err instanceof Error) { + return err; + } else { + return new Error(err); + } +} + +module.exports.intoError = intoError; + +/** + * Attempt to convert an object into a response object. + * This method accounts for failures when serializing the error object. + */ +function toRapidResponse(error) { + try { + if (util.types.isNativeError(error) || _isError(error)) { + return { + errorType: error.name, + errorMessage: error.message, + trace: error.stack.split('\n'), + }; + } else { + return { + errorType: typeof error, + errorMessage: error.toString(), + trace: [], + }; + } + } catch (_err) { + return { + errorType: 'handled', + errorMessage: + 'callback called with Error argument, but there was a problem while retrieving one or more of its message, name, and stack', + }; + } +} + +module.exports.toRapidResponse = toRapidResponse; + +/** + * Format an error with the expected properties. + * For compatability, the error string always starts with a tab. + */ +module.exports.toFormatted = error => { + try { + return ( + '\t' + JSON.stringify(error, (_k, v) => _withEnumerableProperties(v)) + ); + } catch (err) { + return '\t' + JSON.stringify(toRapidResponse(error)); + } +}; + +/** + * Error name, message, code, and stack are all members of the superclass, which + * means they aren't enumerable and don't normally show up in JSON.stringify. + * This method ensures those interesting properties are available along with any + * user-provided enumerable properties. + */ +function _withEnumerableProperties(error) { + if (error instanceof Error) { + let ret = Object.assign( + { + errorType: error.name, + errorMessage: error.message, + code: error.code, + }, + error + ); + if (typeof error.stack == 'string') { + ret.stack = error.stack.split('\n'); + } + return ret; + } else { + return error; + } +} + +const errorClasses = [ + class ImportModuleError extends Error {}, + class HandlerNotFound extends Error {}, + class MalformedHandlerName extends Error {}, + class UserCodeSyntaxError extends Error {}, + class MalformedStreamingHandler extends Error {}, + class InvalidStreamingOperation extends Error {}, + class UnhandledPromiseRejection extends Error { + constructor(reason, promise) { + super(reason); + this.reason = reason; + this.promise = promise; + } + }, +]; + +errorClasses.forEach(e => { + module.exports[e.name] = e; + e.prototype.name = `Runtime.${e.name}`; +}); diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/HttpResponseStream.js b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/HttpResponseStream.js new file mode 100644 index 0000000000..4e1386a03b --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/HttpResponseStream.js @@ -0,0 +1,35 @@ +// https://raw.githubusercontent.com/aws/aws-lambda-nodejs-runtime-interface-client/v3.1.0/src/HttpResponseStream.js + +/** + * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * HttpResponseStream is NOT used by the runtime. + * It is only exposed in the `awslambda` variable for customers to use. + */ + +'use strict'; + +const METADATA_PRELUDE_CONTENT_TYPE = + 'application/vnd.awslambda.http-integration-response'; +const DELIMITER_LEN = 8; + +// Implements the application/vnd.awslambda.http-integration-response content type. +class HttpResponseStream { + static from(underlyingStream, prelude) { + underlyingStream.setContentType(METADATA_PRELUDE_CONTENT_TYPE); + + // JSON.stringify is required. NULL byte is not allowed in metadataPrelude. + const metadataPrelude = JSON.stringify(prelude); + + underlyingStream._onBeforeFirstWrite = write => { + write(metadataPrelude); + + // Write 8 null bytes after the JSON prelude. + write(new Uint8Array(DELIMITER_LEN)); + }; + + return underlyingStream; + } +} + +module.exports.HttpResponseStream = HttpResponseStream; diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/UserFunction.d.ts b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/UserFunction.d.ts new file mode 100644 index 0000000000..b47f266081 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/UserFunction.d.ts @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export declare function load( + appRoot: string, + fullHandlerString: string +): Promise; diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/UserFunction.js b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/UserFunction.js new file mode 100644 index 0000000000..1fb58c00c0 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/vendor/UserFunction.js @@ -0,0 +1,301 @@ +// https://raw.githubusercontent.com/aws/aws-lambda-nodejs-runtime-interface-client/v3.1.0/src/UserFunction.js + +/** + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This module defines the functions for loading the user's code as specified + * in a handler string. + */ + +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const { + HandlerNotFound, + MalformedHandlerName, + ImportModuleError, + UserCodeSyntaxError, +} = require('./Errors'); +const { HttpResponseStream } = require('./HttpResponseStream'); + +const FUNCTION_EXPR = /^([^.]*)\.(.*)$/; +const RELATIVE_PATH_SUBSTRING = '..'; +const HANDLER_STREAMING = Symbol.for('aws.lambda.runtime.handler.streaming'); +const HANDLER_HIGHWATERMARK = Symbol.for( + 'aws.lambda.runtime.handler.streaming.highWaterMark' +); +const STREAM_RESPONSE = 'response'; + +// `awslambda.streamifyResponse` function is provided by default. +const NoGlobalAwsLambda = + process.env['AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA'] === '1' || + process.env['AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA'] === 'true'; + +/** + * Break the full handler string into two pieces, the module root and the actual + * handler string. + * Given './somepath/something/module.nestedobj.handler' this returns + * ['./somepath/something', 'module.nestedobj.handler'] + */ +function _moduleRootAndHandler(fullHandlerString) { + let handlerString = path.basename(fullHandlerString); + let moduleRoot = fullHandlerString.substring( + 0, + fullHandlerString.indexOf(handlerString) + ); + return [moduleRoot, handlerString]; +} + +/** + * Split the handler string into two pieces: the module name and the path to + * the handler function. + */ +function _splitHandlerString(handler) { + let match = handler.match(FUNCTION_EXPR); + if (!match || match.length != 3) { + throw new MalformedHandlerName('Bad handler'); + } + return [match[1], match[2]]; // [module, function-path] +} + +/** + * Resolve the user's handler function from the module. + */ +function _resolveHandler(object, nestedProperty) { + return nestedProperty.split('.').reduce((nested, key) => { + return nested && nested[key]; + }, object); +} + +function _tryRequireFile(file, extension) { + const path = file + (extension || ''); + return fs.existsSync(path) ? require(path) : undefined; +} + +async function _tryAwaitImport(file, extension) { + const path = file + (extension || ''); + + if (fs.existsSync(path)) { + return await import(path); + } + + return undefined; +} + +function _hasFolderPackageJsonTypeModule(folder) { + // Check if package.json exists, return true if type === "module" in package json. + // If there is no package.json, and there is a node_modules, return false. + // Check parent folder otherwise, if there is one. + if (folder.endsWith('/node_modules')) { + return false; + } + + const pj = path.join(folder, '/package.json'); + if (fs.existsSync(pj)) { + try { + const pkg = JSON.parse(fs.readFileSync(pj)); + if (pkg) { + if (pkg.type === 'module') { + return true; + } else { + return false; + } + } + } catch (e) { + console.warn( + `${pj} cannot be read, it will be ignored for ES module detection purposes.`, + e + ); + return false; + } + } + + if (folder === '/') { + // We have reached root without finding either a package.json or a node_modules. + return false; + } + + return _hasFolderPackageJsonTypeModule(path.resolve(folder, '..')); +} + +function _hasPackageJsonTypeModule(file) { + // File must have a .js extension + const jsPath = file + '.js'; + return fs.existsSync(jsPath) + ? _hasFolderPackageJsonTypeModule(path.resolve(path.dirname(jsPath))) + : false; +} + +/** + * Attempt to load the user's module. + * Attempts to directly resolve the module relative to the application root, + * then falls back to the more general require(). + */ +async function _tryRequire(appRoot, moduleRoot, module) { + const lambdaStylePath = path.resolve(appRoot, moduleRoot, module); + + // Extensionless files are loaded via require. + const extensionless = _tryRequireFile(lambdaStylePath); + if (extensionless) { + return extensionless; + } + + // If package.json type != module, .js files are loaded via require. + const pjHasModule = _hasPackageJsonTypeModule(lambdaStylePath); + if (!pjHasModule) { + const loaded = _tryRequireFile(lambdaStylePath, '.js'); + if (loaded) { + return loaded; + } + } + + // If still not loaded, try .js, .mjs, and .cjs in that order. + // Files ending with .js are loaded as ES modules when the nearest parent package.json + // file contains a top-level field "type" with a value of "module". + // https://nodejs.org/api/packages.html#packages_type + const loaded = + (pjHasModule && (await _tryAwaitImport(lambdaStylePath, '.js'))) || + (await _tryAwaitImport(lambdaStylePath, '.mjs')) || + _tryRequireFile(lambdaStylePath, '.cjs'); + if (loaded) { + return loaded; + } + + // Why not just require(module)? + // Because require() is relative to __dirname, not process.cwd(). And the + // runtime implementation is not located in /var/task + // This won't work (yet) for esModules as import.meta.resolve is still experimental + // See: https://nodejs.org/api/esm.html#esm_import_meta_resolve_specifier_parent + const nodeStylePath = require.resolve(module, { + paths: [appRoot, moduleRoot], + }); + + return require(nodeStylePath); +} + +/** + * Load the user's application or throw a descriptive error. + * @throws Runtime errors in two cases + * 1 - UserCodeSyntaxError if there's a syntax error while loading the module + * 2 - ImportModuleError if the module cannot be found + */ +async function _loadUserApp(appRoot, moduleRoot, module) { + if (!NoGlobalAwsLambda) { + globalThis.awslambda = { + streamifyResponse: (handler, options) => { + handler[HANDLER_STREAMING] = STREAM_RESPONSE; + if (typeof options?.highWaterMark === 'number') { + handler[HANDLER_HIGHWATERMARK] = parseInt(options.highWaterMark); + } + return handler; + }, + HttpResponseStream: HttpResponseStream, + }; + } + + try { + return await _tryRequire(appRoot, moduleRoot, module); + } catch (e) { + if (e instanceof SyntaxError) { + throw new UserCodeSyntaxError(e); + } else if (e.code !== undefined && e.code === 'MODULE_NOT_FOUND') { + throw new ImportModuleError(e); + } else { + throw e; + } + } +} + +function _throwIfInvalidHandler(fullHandlerString) { + if (fullHandlerString.includes(RELATIVE_PATH_SUBSTRING)) { + throw new MalformedHandlerName( + `'${fullHandlerString}' is not a valid handler name. Use absolute paths when specifying root directories in handler names.` + ); + } +} + +function _isHandlerStreaming(handler) { + if ( + typeof handler[HANDLER_STREAMING] === 'undefined' || + handler[HANDLER_STREAMING] === null || + handler[HANDLER_STREAMING] === false + ) { + return false; + } + + if (handler[HANDLER_STREAMING] === STREAM_RESPONSE) { + return STREAM_RESPONSE; + } else { + throw new MalformedStreamingHandler( + 'Only response streaming is supported.' + ); + } +} + +function _highWaterMark(handler) { + if ( + typeof handler[HANDLER_HIGHWATERMARK] === 'undefined' || + handler[HANDLER_HIGHWATERMARK] === null || + handler[HANDLER_HIGHWATERMARK] === false + ) { + return undefined; + } + + const hwm = parseInt(handler[HANDLER_HIGHWATERMARK]); + return isNaN(hwm) ? undefined : hwm; +} + +/** + * Load the user's function with the approot and the handler string. + * @param appRoot {string} + * The path to the application root. + * @param handlerString {string} + * The user-provided handler function in the form 'module.function'. + * @return userFuction {function} + * The user's handler function. This function will be passed the event body, + * the context object, and the callback function. + * @throws In five cases:- + * 1 - if the handler string is incorrectly formatted an error is thrown + * 2 - if the module referenced by the handler cannot be loaded + * 3 - if the function in the handler does not exist in the module + * 4 - if a property with the same name, but isn't a function, exists on the + * module + * 5 - the handler includes illegal character sequences (like relative paths + * for traversing up the filesystem '..') + * Errors for scenarios known by the runtime, will be wrapped by Runtime.* errors. + */ +module.exports.load = async function (appRoot, fullHandlerString) { + _throwIfInvalidHandler(fullHandlerString); + + let [moduleRoot, moduleAndHandler] = _moduleRootAndHandler(fullHandlerString); + let [module, handlerPath] = _splitHandlerString(moduleAndHandler); + + let userApp = await _loadUserApp(appRoot, moduleRoot, module); + let handlerFunc = _resolveHandler(userApp, handlerPath); + + if (!handlerFunc) { + throw new HandlerNotFound( + `${fullHandlerString} is undefined or not exported` + ); + } + + if (typeof handlerFunc !== 'function') { + throw new HandlerNotFound(`${fullHandlerString} is not a function`); + } + + return handlerFunc; +}; + +module.exports.isHandlerFunction = function (value) { + return typeof value === 'function'; +}; + +module.exports.getHandlerMetadata = function (handlerFunc) { + return { + streaming: _isHandlerStreaming(handlerFunc), + highWaterMark: _highWaterMark(handlerFunc), + }; +}; + +module.exports.STREAM_RESPONSE = STREAM_RESPONSE; diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/tsconfig.json b/plugins/node/opentelemetry-instrumentation-aws-lambda/tsconfig.json index 28be80d266..47c24b6178 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/tsconfig.json +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/tsconfig.json @@ -4,8 +4,5 @@ "rootDir": ".", "outDir": "build" }, - "include": [ - "src/**/*.ts", - "test/**/*.ts" - ] + "include": ["src/**/*.ts", "test/**/*.ts", "test/vendor/**/*"] }