diff --git a/package-lock.json b/package-lock.json index df11a4e701..c172dd7a04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1401,6 +1401,55 @@ "node": ">=12.0.0" } }, + "node_modules/@aws-sdk/client-kinesis": { + "version": "3.85.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-kinesis/-/client-kinesis-3.85.0.tgz", + "integrity": "sha512-TlBYyNCkNZqnarpReaYCJTHQeVpjb6XpVcpoznsPnRpPjcm57JVhuErpKjU6tOKmtAl0uKrmJsjupizgQwFMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/client-sts": "3.85.0", + "@aws-sdk/config-resolver": "3.80.0", + "@aws-sdk/credential-provider-node": "3.85.0", + "@aws-sdk/eventstream-serde-browser": "3.78.0", + "@aws-sdk/eventstream-serde-config-resolver": "3.78.0", + "@aws-sdk/eventstream-serde-node": "3.78.0", + "@aws-sdk/fetch-http-handler": "3.78.0", + "@aws-sdk/hash-node": "3.78.0", + "@aws-sdk/invalid-dependency": "3.78.0", + "@aws-sdk/middleware-content-length": "3.78.0", + "@aws-sdk/middleware-host-header": "3.78.0", + "@aws-sdk/middleware-logger": "3.78.0", + "@aws-sdk/middleware-retry": "3.80.0", + "@aws-sdk/middleware-serde": "3.78.0", + "@aws-sdk/middleware-signing": "3.78.0", + "@aws-sdk/middleware-stack": "3.78.0", + "@aws-sdk/middleware-user-agent": "3.78.0", + "@aws-sdk/node-config-provider": "3.80.0", + "@aws-sdk/node-http-handler": "3.82.0", + "@aws-sdk/protocol-http": "3.78.0", + "@aws-sdk/smithy-client": "3.85.0", + "@aws-sdk/types": "3.78.0", + "@aws-sdk/url-parser": "3.78.0", + "@aws-sdk/util-base64-browser": "3.58.0", + "@aws-sdk/util-base64-node": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-node": "3.55.0", + "@aws-sdk/util-defaults-mode-browser": "3.85.0", + "@aws-sdk/util-defaults-mode-node": "3.85.0", + "@aws-sdk/util-user-agent-browser": "3.78.0", + "@aws-sdk/util-user-agent-node": "3.80.0", + "@aws-sdk/util-utf8-browser": "3.55.0", + "@aws-sdk/util-utf8-node": "3.55.0", + "@aws-sdk/util-waiter": "3.78.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/@aws-sdk/client-lambda": { "version": "3.85.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.85.0.tgz", @@ -11395,7 +11444,6 @@ "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.2.tgz", "integrity": "sha512-iwUxrFm/ZFCXhzhtZ6JnoJzAsqUrVfBAZUTQj8ypXGtIjwXZpKqmgYiuqrDERiydDI5gesqvsC4Rqe57GGhbVg==", "dev": true, - "optional": true, "dependencies": { "@smithy/types": "^2.10.0", "tslib": "^2.5.0" @@ -11681,7 +11729,6 @@ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.4.0.tgz", "integrity": "sha512-Mf2f7MMy31W8LisJ9O+7J5cKiNwBwBBLU6biQ7/sFSFdhuOxPN7hOPoZ8vlaFjvrpfOUJw9YOpjGyNTKuvomOQ==", "dev": true, - "optional": true, "dependencies": { "@smithy/abort-controller": "^2.1.2", "@smithy/protocol-http": "^3.2.0", @@ -11712,7 +11759,6 @@ "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.2.0.tgz", "integrity": "sha512-VRp0YITYIQum+rX4zeZ3cW1wl9r90IQzQN+VLS1NxdSMt6NLsJiJqR9czTxlaeWNrLHsFAETmjmdrS48Ug1liA==", "dev": true, - "optional": true, "dependencies": { "@smithy/types": "^2.10.0", "tslib": "^2.5.0" @@ -11726,7 +11772,6 @@ "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.2.tgz", "integrity": "sha512-wk6QpuvBBLJF5w8aADsZOtxaHY9cF5MZe1Ry3hSqqBxARdUrMoXi/jukUz5W0ftXGlbA398IN8dIIUj3WXqJXg==", "dev": true, - "optional": true, "dependencies": { "@smithy/types": "^2.10.0", "@smithy/util-uri-escape": "^2.1.1", @@ -12021,7 +12066,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", "dev": true, - "optional": true, "dependencies": { "tslib": "^2.5.0" }, @@ -36298,6 +36342,7 @@ }, "devDependencies": { "@aws-sdk/client-dynamodb": "3.85.0", + "@aws-sdk/client-kinesis": "3.85.0", "@aws-sdk/client-lambda": "3.85.0", "@aws-sdk/client-s3": "3.85.0", "@aws-sdk/client-sns": "3.85.0", @@ -36306,6 +36351,7 @@ "@opentelemetry/api": "^1.3.0", "@opentelemetry/contrib-test-utils": "^0.42.0", "@opentelemetry/sdk-trace-base": "^1.8.0", + "@smithy/node-http-handler": "2.4.0", "@types/mocha": "8.2.3", "@types/node": "18.18.14", "@types/sinon": "10.0.20", @@ -39402,6 +39448,51 @@ "uuid": "^8.3.2" } }, + "@aws-sdk/client-kinesis": { + "version": "3.85.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-kinesis/-/client-kinesis-3.85.0.tgz", + "integrity": "sha512-TlBYyNCkNZqnarpReaYCJTHQeVpjb6XpVcpoznsPnRpPjcm57JVhuErpKjU6tOKmtAl0uKrmJsjupizgQwFMIA==", + "dev": true, + "requires": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/client-sts": "3.85.0", + "@aws-sdk/config-resolver": "3.80.0", + "@aws-sdk/credential-provider-node": "3.85.0", + "@aws-sdk/eventstream-serde-browser": "3.78.0", + "@aws-sdk/eventstream-serde-config-resolver": "3.78.0", + "@aws-sdk/eventstream-serde-node": "3.78.0", + "@aws-sdk/fetch-http-handler": "3.78.0", + "@aws-sdk/hash-node": "3.78.0", + "@aws-sdk/invalid-dependency": "3.78.0", + "@aws-sdk/middleware-content-length": "3.78.0", + "@aws-sdk/middleware-host-header": "3.78.0", + "@aws-sdk/middleware-logger": "3.78.0", + "@aws-sdk/middleware-retry": "3.80.0", + "@aws-sdk/middleware-serde": "3.78.0", + "@aws-sdk/middleware-signing": "3.78.0", + "@aws-sdk/middleware-stack": "3.78.0", + "@aws-sdk/middleware-user-agent": "3.78.0", + "@aws-sdk/node-config-provider": "3.80.0", + "@aws-sdk/node-http-handler": "3.82.0", + "@aws-sdk/protocol-http": "3.78.0", + "@aws-sdk/smithy-client": "3.85.0", + "@aws-sdk/types": "3.78.0", + "@aws-sdk/url-parser": "3.78.0", + "@aws-sdk/util-base64-browser": "3.58.0", + "@aws-sdk/util-base64-node": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-node": "3.55.0", + "@aws-sdk/util-defaults-mode-browser": "3.85.0", + "@aws-sdk/util-defaults-mode-node": "3.85.0", + "@aws-sdk/util-user-agent-browser": "3.78.0", + "@aws-sdk/util-user-agent-node": "3.80.0", + "@aws-sdk/util-utf8-browser": "3.55.0", + "@aws-sdk/util-utf8-node": "3.55.0", + "@aws-sdk/util-waiter": "3.78.0", + "tslib": "^2.3.1" + } + }, "@aws-sdk/client-lambda": { "version": "3.85.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.85.0.tgz", @@ -46779,6 +46870,7 @@ "version": "file:plugins/node/opentelemetry-instrumentation-aws-sdk", "requires": { "@aws-sdk/client-dynamodb": "3.85.0", + "@aws-sdk/client-kinesis": "3.85.0", "@aws-sdk/client-lambda": "3.85.0", "@aws-sdk/client-s3": "3.85.0", "@aws-sdk/client-sns": "3.85.0", @@ -46791,6 +46883,7 @@ "@opentelemetry/propagation-utils": "^0.30.12", "@opentelemetry/sdk-trace-base": "^1.8.0", "@opentelemetry/semantic-conventions": "^1.27.0", + "@smithy/node-http-handler": "2.4.0", "@types/mocha": "8.2.3", "@types/node": "18.18.14", "@types/sinon": "10.0.20", @@ -50280,7 +50373,6 @@ "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.2.tgz", "integrity": "sha512-iwUxrFm/ZFCXhzhtZ6JnoJzAsqUrVfBAZUTQj8ypXGtIjwXZpKqmgYiuqrDERiydDI5gesqvsC4Rqe57GGhbVg==", "dev": true, - "optional": true, "requires": { "@smithy/types": "^2.10.0", "tslib": "^2.5.0" @@ -50533,7 +50625,6 @@ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.4.0.tgz", "integrity": "sha512-Mf2f7MMy31W8LisJ9O+7J5cKiNwBwBBLU6biQ7/sFSFdhuOxPN7hOPoZ8vlaFjvrpfOUJw9YOpjGyNTKuvomOQ==", "dev": true, - "optional": true, "requires": { "@smithy/abort-controller": "^2.1.2", "@smithy/protocol-http": "^3.2.0", @@ -50558,7 +50649,6 @@ "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.2.0.tgz", "integrity": "sha512-VRp0YITYIQum+rX4zeZ3cW1wl9r90IQzQN+VLS1NxdSMt6NLsJiJqR9czTxlaeWNrLHsFAETmjmdrS48Ug1liA==", "dev": true, - "optional": true, "requires": { "@smithy/types": "^2.10.0", "tslib": "^2.5.0" @@ -50569,7 +50659,6 @@ "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.2.tgz", "integrity": "sha512-wk6QpuvBBLJF5w8aADsZOtxaHY9cF5MZe1Ry3hSqqBxARdUrMoXi/jukUz5W0ftXGlbA398IN8dIIUj3WXqJXg==", "dev": true, - "optional": true, "requires": { "@smithy/types": "^2.10.0", "@smithy/util-uri-escape": "^2.1.1", @@ -50810,7 +50899,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", "dev": true, - "optional": true, "requires": { "tslib": "^2.5.0" } diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/package.json b/plugins/node/opentelemetry-instrumentation-aws-sdk/package.json index afc98d3bae..49f16ccff7 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-sdk/package.json +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/package.json @@ -52,11 +52,13 @@ }, "devDependencies": { "@aws-sdk/client-dynamodb": "3.85.0", + "@aws-sdk/client-kinesis": "3.85.0", "@aws-sdk/client-lambda": "3.85.0", "@aws-sdk/client-s3": "3.85.0", "@aws-sdk/client-sns": "3.85.0", "@aws-sdk/client-sqs": "3.85.0", "@aws-sdk/types": "3.78.0", + "@smithy/node-http-handler": "2.4.0", "@opentelemetry/api": "^1.3.0", "@opentelemetry/contrib-test-utils": "^0.42.0", "@opentelemetry/sdk-trace-base": "^1.8.0", diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/enums.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/enums.ts index c3a36dcb12..764bd173c7 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/enums.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/enums.ts @@ -22,4 +22,10 @@ export enum AttributeNames { AWS_REQUEST_ID = 'aws.request.id', AWS_REQUEST_EXTENDED_ID = 'aws.request.extended_id', AWS_SIGNATURE_VERSION = 'aws.signature.version', + + // TODO: Add these semantic attributes to: + // - https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttributes.ts + // For S3, see specification: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/object-stores/s3.md + AWS_S3_BUCKET = 'aws.s3.bucket', + AWS_KINESIS_STREAM_NAME = 'aws.kinesis.stream.name', } diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/ServicesExtensions.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/ServicesExtensions.ts index cb739a2011..609ed7f7a0 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/ServicesExtensions.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/ServicesExtensions.ts @@ -24,6 +24,8 @@ import { import { DynamodbServiceExtension } from './dynamodb'; import { SnsServiceExtension } from './sns'; import { LambdaServiceExtension } from './lambda'; +import { S3ServiceExtension } from './s3'; +import { KinesisServiceExtension } from './kinesis'; export class ServicesExtensions implements ServiceExtension { services: Map = new Map(); @@ -33,6 +35,8 @@ export class ServicesExtensions implements ServiceExtension { this.services.set('SNS', new SnsServiceExtension()); this.services.set('DynamoDB', new DynamodbServiceExtension()); this.services.set('Lambda', new LambdaServiceExtension()); + this.services.set('S3', new S3ServiceExtension()); + this.services.set('Kinesis', new KinesisServiceExtension()); } requestPreSpanHook( diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/kinesis.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/kinesis.ts new file mode 100644 index 0000000000..76ecdf805c --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/kinesis.ts @@ -0,0 +1,42 @@ +/* + * 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. + */ +import { Attributes, SpanKind } from '@opentelemetry/api'; +import { AttributeNames } from '../enums'; +import { AwsSdkInstrumentationConfig, NormalizedRequest } from '../types'; +import { RequestMetadata, ServiceExtension } from './ServiceExtension'; + +export class KinesisServiceExtension implements ServiceExtension { + requestPreSpanHook( + request: NormalizedRequest, + _config: AwsSdkInstrumentationConfig + ): RequestMetadata { + const streamName = request.commandInput?.StreamName; + const spanKind: SpanKind = SpanKind.CLIENT; + const spanAttributes: Attributes = {}; + + if (streamName) { + spanAttributes[AttributeNames.AWS_KINESIS_STREAM_NAME] = streamName; + } + + const isIncoming = false; + + return { + isIncoming, + spanAttributes, + spanKind, + }; + } +} diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/s3.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/s3.ts new file mode 100644 index 0000000000..5bf0830e11 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/s3.ts @@ -0,0 +1,42 @@ +/* + * 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. + */ +import { Attributes, SpanKind } from '@opentelemetry/api'; +import { AttributeNames } from '../enums'; +import { AwsSdkInstrumentationConfig, NormalizedRequest } from '../types'; +import { RequestMetadata, ServiceExtension } from './ServiceExtension'; + +export class S3ServiceExtension implements ServiceExtension { + requestPreSpanHook( + request: NormalizedRequest, + _config: AwsSdkInstrumentationConfig + ): RequestMetadata { + const bucketName = request.commandInput?.Bucket; + const spanKind: SpanKind = SpanKind.CLIENT; + const spanAttributes: Attributes = {}; + + if (bucketName) { + spanAttributes[AttributeNames.AWS_S3_BUCKET] = bucketName; + } + + const isIncoming = false; + + return { + isIncoming, + spanAttributes, + spanKind, + }; + } +} diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/utils.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/utils.ts index e3d0db6bdb..cb8aaa6591 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/utils.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/utils.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { NormalizedRequest } from './types'; import { Attributes, Context, context } from '@opentelemetry/api'; import { SEMATTRS_RPC_METHOD, @@ -21,6 +20,7 @@ import { SEMATTRS_RPC_SYSTEM, } from '@opentelemetry/semantic-conventions'; import { AttributeNames } from './enums'; +import { NormalizedRequest } from './types'; const toPascalCase = (str: string): string => typeof str === 'string' ? str.charAt(0).toUpperCase() + str.slice(1) : str; diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/test/aws-sdk-v3.test.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/aws-sdk-v3.test.ts index 0e3a752c1b..29a1f6bded 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-sdk/test/aws-sdk-v3.test.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/aws-sdk-v3.test.ts @@ -84,6 +84,9 @@ describe('instrumentation-aws-sdk-v3', () => { expect(span.attributes[SEMATTRS_RPC_SYSTEM]).toEqual('aws-api'); expect(span.attributes[SEMATTRS_RPC_METHOD]).toEqual('PutObject'); expect(span.attributes[SEMATTRS_RPC_SERVICE]).toEqual('S3'); + expect(span.attributes[AttributeNames.AWS_S3_BUCKET]).toEqual( + 'ot-demo-test' + ); expect(span.attributes[AttributeNames.AWS_REGION]).toEqual(region); expect(span.name).toEqual('S3.PutObject'); expect(span.kind).toEqual(SpanKind.CLIENT); @@ -108,6 +111,9 @@ describe('instrumentation-aws-sdk-v3', () => { expect(span.attributes[SEMATTRS_RPC_SYSTEM]).toEqual('aws-api'); expect(span.attributes[SEMATTRS_RPC_METHOD]).toEqual('PutObject'); expect(span.attributes[SEMATTRS_RPC_SERVICE]).toEqual('S3'); + expect(span.attributes[AttributeNames.AWS_S3_BUCKET]).toEqual( + 'ot-demo-test' + ); expect(span.attributes[AttributeNames.AWS_REGION]).toEqual(region); expect(span.name).toEqual('S3.PutObject'); expect(span.attributes[SEMATTRS_HTTP_STATUS_CODE]).toEqual(200); @@ -134,6 +140,9 @@ describe('instrumentation-aws-sdk-v3', () => { expect(span.attributes[SEMATTRS_RPC_SYSTEM]).toEqual('aws-api'); expect(span.attributes[SEMATTRS_RPC_METHOD]).toEqual('PutObject'); expect(span.attributes[SEMATTRS_RPC_SERVICE]).toEqual('S3'); + expect(span.attributes[AttributeNames.AWS_S3_BUCKET]).toEqual( + 'ot-demo-test' + ); expect(span.attributes[AttributeNames.AWS_REGION]).toEqual(region); expect(span.name).toEqual('S3.PutObject'); expect(span.attributes[SEMATTRS_HTTP_STATUS_CODE]).toEqual(200); @@ -167,6 +176,9 @@ describe('instrumentation-aws-sdk-v3', () => { expect(span.attributes[SEMATTRS_RPC_SYSTEM]).toEqual('aws-api'); expect(span.attributes[SEMATTRS_RPC_METHOD]).toEqual('PutObject'); expect(span.attributes[SEMATTRS_RPC_SERVICE]).toEqual('S3'); + expect(span.attributes[AttributeNames.AWS_S3_BUCKET]).toEqual( + 'invalid-bucket-name' + ); expect(span.attributes[SEMATTRS_HTTP_STATUS_CODE]).toEqual(403); expect(span.attributes[AttributeNames.AWS_REGION]).toEqual(region); expect(span.attributes[AttributeNames.AWS_REQUEST_ID]).toEqual( diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/test/kinesis.test.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/kinesis.test.ts new file mode 100644 index 0000000000..cdd4d92cfa --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/kinesis.test.ts @@ -0,0 +1,76 @@ +/* + * 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. + */ + +import { + getTestSpans, + registerInstrumentationTesting, +} from '@opentelemetry/contrib-test-utils'; +import { AwsInstrumentation } from '../src'; +import { AttributeNames } from '../src/enums'; +registerInstrumentationTesting(new AwsInstrumentation()); + +import { DescribeStreamCommand, KinesisClient } from '@aws-sdk/client-kinesis'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; +import * as fs from 'fs'; +import * as nock from 'nock'; + +import { SpanKind } from '@opentelemetry/api'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { expect } from 'expect'; + +const region = 'us-east-1'; + +describe('Kinesis - v3', () => { + describe('DescribeStream', () => { + it('Request span attributes - adds Stream Name', async () => { + const dummyStreamName = 'dummy-stream-name'; + + nock(`https://kinesis.${region}.amazonaws.com`) + .post('/') + .reply( + 200, + fs.readFileSync( + './test/mock-responses/kinesis-describe-stream.json', + 'utf8' + ) + ); + + const params = { + StreamName: dummyStreamName, + }; + + // Use NodeHttpHandler to use HTTP instead of HTTP2 because nock does not support HTTP2 + const client = new KinesisClient({ + region: region, + requestHandler: new NodeHttpHandler(), + }); + await client.send(new DescribeStreamCommand(params)); + + const testSpans: ReadableSpan[] = getTestSpans(); + const describeSpans: ReadableSpan[] = testSpans.filter( + (s: ReadableSpan) => { + return s.name === 'Kinesis.DescribeStream'; + } + ); + expect(describeSpans.length).toBe(1); + const describeSpan = describeSpans[0]; + expect( + describeSpan.attributes[AttributeNames.AWS_KINESIS_STREAM_NAME] + ).toBe(dummyStreamName); + expect(describeSpan.kind).toBe(SpanKind.CLIENT); + }); + }); +}); diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/test/mock-responses/kinesis-describe-stream.json b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/mock-responses/kinesis-describe-stream.json new file mode 100644 index 0000000000..7d60744f59 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/mock-responses/kinesis-describe-stream.json @@ -0,0 +1 @@ +{"StreamDescription":{"EncryptionType":"NONE","EnhancedMonitoring":[{"ShardLevelMetrics":[]}],"HasMoreShards":false,"RetentionPeriodHours":24,"Shards":[{"HashKeyRange":{"EndingHashKey":"113238940823489329203432849230874303284","StartingHashKey":"0"},"SequenceNumberRange":{"StartingSequenceNumber":"43987583475293457385930053489574375382543783454534789435"},"ShardId":"shardId-000000000000"},{"HashKeyRange":{"EndingHashKey":"226854911280625642308916401214512140969","StartingHashKey":"113427455579312821154458202477256070485"},"SequenceNumberRange":{"StartingSequenceNumber":"49654869303348141538950437146463849462533012903803486226"},"ShardId":"shardId-000000000001"},{"HashKeyRange":{"EndingHashKey":"340282366920284953463374607431768211455","StartingHashKey":"226854911280625642308916382954512140970"},"SequenceNumberRange":{"StartingSequenceNumber":"49654869303370442228501967769610713180805661265309466658"},"ShardId":"shardId-000000000002"}],"StreamARN":"arn:aws:kinesis:us-east-1:123456789012:stream/dummy-stream-name","StreamCreationTimestamp":1.723630733E9,"StreamModeDetails":{"StreamMode":"PROVISIONED"},"StreamName":"dummy-stream-name","StreamStatus":"ACTIVE"}} \ No newline at end of file diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/test/s3.test.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/s3.test.ts new file mode 100644 index 0000000000..47795dd466 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/s3.test.ts @@ -0,0 +1,68 @@ +/* + * 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. + */ + +import { + getTestSpans, + registerInstrumentationTesting, +} from '@opentelemetry/contrib-test-utils'; +import { AwsInstrumentation } from '../src'; +import { AttributeNames } from '../src/enums'; +registerInstrumentationTesting(new AwsInstrumentation()); + +import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; +import * as fs from 'fs'; +import * as nock from 'nock'; + +import { SpanKind } from '@opentelemetry/api'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { expect } from 'expect'; + +const region = 'us-east-1'; + +describe('S3 - v3', () => { + describe('PutObject', () => { + it('Request span attributes - adds bucket Name', async () => { + const dummyBucketName = 'ot-demo-test'; + + nock(`https://${dummyBucketName}.s3.${region}.amazonaws.com/`) + .put('/aws-ot-s3-test-object.txt?x-id=PutObject') + .reply( + 200, + fs.readFileSync('./test/mock-responses/s3-put-object.xml', 'utf8') + ); + + const params = { + Bucket: dummyBucketName, + Key: 'aws-ot-s3-test-object.txt', + }; + const client = new S3Client({ region }); + await client.send(new PutObjectCommand(params)); + + const testSpans: ReadableSpan[] = getTestSpans(); + const listObjectsSpans: ReadableSpan[] = testSpans.filter( + (s: ReadableSpan) => { + return s.name === 'S3.PutObject'; + } + ); + expect(listObjectsSpans.length).toBe(1); + const listObjectsSpan = listObjectsSpans[0]; + expect(listObjectsSpan.attributes[AttributeNames.AWS_S3_BUCKET]).toBe( + dummyBucketName + ); + expect(listObjectsSpan.kind).toBe(SpanKind.CLIENT); + }); + }); +});