diff --git a/.github/workflows/release-udp-exporter.yml b/.github/workflows/release-udp-exporter.yml deleted file mode 100644 index 95c20816..00000000 --- a/.github/workflows/release-udp-exporter.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Release ADOT OTLP UDP Exporter -on: - workflow_dispatch: - inputs: - version: - description: The version to tag the release with, e.g., 1.2.0 - required: true - -jobs: - build: - environment: Release - runs-on: ubuntu-latest - steps: - - name: Checkout Contrib Repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Node and run Unit Tests - uses: ./.github/actions/set_up - with: - node_version: "20" - package_name: "@aws/aws-otel-otlp-udp-exporter" - os: ubuntu-latest - run_unit_tests: true - - # Project dependencies and compilation are already done in the previous step - - name: Install Dependencies, Compile, and Build Tarball - id: staging_tarball_build - shell: bash - run: | - cd exporters/aws-otel-otlp-udp-exporter - npm pack - - - name: Download and run X-Ray Daemon - run: | - mkdir xray-daemon - cd xray-daemon - wget https://s3.us-west-2.amazonaws.com/aws-xray-assets.us-west-2/xray-daemon/aws-xray-daemon-linux-3.x.zip - unzip aws-xray-daemon-linux-3.x.zip - ./xray -o -n us-west-2 -f ./daemon-logs.log --log-level debug & - - - name: Setup Sample App - run: | - cd sample-applications/integ-test-http-server - npm install - npm install ../../exporters/aws-otel-otlp-udp-exporter/aws-aws-otel-otlp-udp-exporter-*.tgz - - - name: Run Sample App in Background - run: | - cd sample-applications/integ-test-http-server - node udp-exporter-test-server.js & - # Wait for test server to initialize - sleep 5 - - - name: Call Sample App Endpoint - id: call-endpoint - run: | - echo "traceId=$(curl localhost:8080/test)" >> $GITHUB_OUTPUT - - - name: Verify X-Ray daemon received traces - run: | - sleep 10 - echo "X-Ray daemon logs:" - cat xray-daemon/daemon-logs.log - # Check if the daemon received and processed some data - if grep -q "sending.*batch" xray-daemon/daemon-logs.log; then - echo "✅ X-Ray daemon processed trace data (AWS upload errors are expected)" - exit 0 - elif grep -q "processor:.*segment" xray-daemon/daemon-logs.log; then - echo "✅ X-Ray daemon processed segment data (AWS upload errors are expected)" - exit 0 - else - echo "❌ No evidence of traces being received by X-Ray daemon" - exit 1 - fi - - # TODO: Uncomment when we make the first release - # # Publish OTLP UDP Exporter to npm - # - name: Publish to npm - # working-directory: exporters/aws-otel-otlp-udp-exporter - # env: - # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - # NPM_CONFIG_PROVENANCE: true - # run: npx publish diff --git a/exporters/aws-otel-otlp-udp-exporter/.eslintignore b/exporters/aws-otel-otlp-udp-exporter/.eslintignore deleted file mode 100644 index 8cbd4317..00000000 --- a/exporters/aws-otel-otlp-udp-exporter/.eslintignore +++ /dev/null @@ -1,5 +0,0 @@ -build -node_modules -.eslintrc.js -version.ts -src/third-party \ No newline at end of file diff --git a/exporters/aws-otel-otlp-udp-exporter/.eslintrc.js b/exporters/aws-otel-otlp-udp-exporter/.eslintrc.js deleted file mode 100644 index 9389e718..00000000 --- a/exporters/aws-otel-otlp-udp-exporter/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - "env": { - "commonjs": true, - "node": true, - "mocha": true, - }, - ...require('../../eslint.config.js') -} \ No newline at end of file diff --git a/exporters/aws-otel-otlp-udp-exporter/LICENSE b/exporters/aws-otel-otlp-udp-exporter/LICENSE deleted file mode 100644 index 67db8588..00000000 --- a/exporters/aws-otel-otlp-udp-exporter/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/exporters/aws-otel-otlp-udp-exporter/README.md b/exporters/aws-otel-otlp-udp-exporter/README.md deleted file mode 100644 index 8fb53b6c..00000000 --- a/exporters/aws-otel-otlp-udp-exporter/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# AWS Distro for OpenTelemetry (ADOT) OTLP UDP Exporter - -Install this package into your NodeJS project with: - -```shell -npm install --save @aws/aws-otel-otlp-udp-exporter -``` - -## Usage - -```js -import { OTLPUdpSpanExporter } from './otlp-udp-exporter'; -import { SpanExporter } from '@opentelemetry/sdk-trace-base'; - -// ... - -let otlpUdpSpanExporter: SpanExporter = new OTLPUdpSpanExporter('127.0.0.1:2000'); -``` diff --git a/exporters/aws-otel-otlp-udp-exporter/package.json b/exporters/aws-otel-otlp-udp-exporter/package.json deleted file mode 100644 index 841f931b..00000000 --- a/exporters/aws-otel-otlp-udp-exporter/package.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "name": "@aws/aws-otel-otlp-udp-exporter", - "version": "0.0.1", - "description": "This package provides an OTLP UDP Exporter for OpenTelemetry.", - "author": { - "name": "Amazon Web Services", - "url": "http://aws.amazon.com" - }, - "homepage": "https://github.com/aws-observability/aws-otel-js-instrumentation/tree/main/exporters/aws-otel-otlp-udp-exporter#readme", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - }, - "publishConfig": { - "access": "public" - }, - "main": "build/src/index.js", - "types": "build/src/index.d.ts", - "repository": "aws-observability/aws-otel-js-instrumentation", - "scripts": { - "clean": "rimraf build/*", - "compile": "tsc -p .", - "lint": "eslint . --ext .ts", - "lint:fix": "eslint . --ext .ts --fix", - "precompile": "tsc --version && lerna run version:update --scope @aws/aws-otel-otlp-udp-exporter --include-dependencies", - "prewatch": "npm run precompile", - "prepublishOnly": "npm run compile", - "tdd": "yarn test -- --watch-extensions ts --watch", - "test": "nyc ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'", - "test:coverage": "nyc --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'", - "watch": "tsc -w" - }, - "nyc": { - "all": true, - "include": [ - "src/**/*.ts" - ], - "exclude": [ - "src/third-party/**/*.ts" - ] - }, - "bugs": { - "url": "https://github.com/aws-observability/aws-otel-js-instrumentation/issues" - }, - "keywords": [ - "aws", - "amazon", - "adot", - "adotjs", - "adot-js", - "adot js", - "xray", - "x-ray", - "x ray", - "awsxray", - "awsdistroopentelemetry", - "opentelemetry", - "otel", - "awslambda", - "nodejs", - "trace", - "tracing", - "profiling", - "instrumentation" - ], - "devDependencies": { - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/contrib-test-utils": "^0.45.0", - "@types/mocha": "7.0.2", - "@types/node": "18.6.5", - "@types/sinon": "10.0.18", - "expect": "29.2.0", - "mocha": "7.2.0", - "nyc": "15.1.0", - "rimraf": "5.0.5", - "sinon": "15.2.0", - "ts-mocha": "10.0.0", - "typescript": "4.4.4" - }, - "dependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/otlp-transformer": "0.57.1", - "@opentelemetry/sdk-trace-base": "1.30.1" - }, - "files": [ - "build/src/**/*.js", - "build/src/**/*.js.map", - "build/src/**/*.d.ts", - "build/src/**/*.json" - ] -} diff --git a/exporters/aws-otel-otlp-udp-exporter/src/index.ts b/exporters/aws-otel-otlp-udp-exporter/src/index.ts deleted file mode 100644 index ac3b958d..00000000 --- a/exporters/aws-otel-otlp-udp-exporter/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -export { OTLPUdpSpanExporter } from './otlp-udp-exporter'; diff --git a/exporters/aws-otel-otlp-udp-exporter/src/otlp-udp-exporter.ts b/exporters/aws-otel-otlp-udp-exporter/src/otlp-udp-exporter.ts deleted file mode 100644 index 443d749c..00000000 --- a/exporters/aws-otel-otlp-udp-exporter/src/otlp-udp-exporter.ts +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import * as dgram from 'dgram'; -import { diag } from '@opentelemetry/api'; -import { ExportResult, ExportResultCode } from '@opentelemetry/core'; -import { ProtobufTraceSerializer } from '@opentelemetry/otlp-transformer'; -import { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base'; - -const DEFAULT_ENDPOINT = '127.0.0.1:2000'; -const PROTOCOL_HEADER = '{"format":"json","version":1}\n'; -const DEFAULT_FORMAT_OTEL_TRACES_BINARY_PREFIX = 'T1S'; - -export class UdpExporter { - private _endpoint: string; - private _host: string; - private _port: number; - private _socket: dgram.Socket; - - constructor(endpoint?: string) { - this._endpoint = endpoint || DEFAULT_ENDPOINT; - [this._host, this._port] = this._parseEndpoint(this._endpoint); - this._socket = dgram.createSocket('udp4'); - this._socket.unref(); - } - - sendData(data: Uint8Array, signalFormatPrefix: string): void { - const base64EncodedString = Buffer.from(data).toString('base64'); - const message = `${PROTOCOL_HEADER}${signalFormatPrefix}${base64EncodedString}`; - - try { - this._socket.send(Buffer.from(message, 'utf-8'), this._port, this._host, err => { - if (err) { - throw err; - } - }); - } catch (err) { - diag.error('Error sending UDP data: %s', err); - throw err; - } - } - - shutdown(): void { - this._socket.close(); - } - - private _parseEndpoint(endpoint: string): [string, number] { - try { - const [host, port] = endpoint.split(':'); - return [host, parseInt(port, 10)]; - } catch (err) { - throw new Error(`Invalid endpoint: ${endpoint}`); - } - } -} - -export class OTLPUdpSpanExporter implements SpanExporter { - private _udpExporter: UdpExporter; - private _signalPrefix: string; - private _endpoint: string; - - constructor(endpoint?: string, _signalPrefix?: string) { - if (endpoint == null) { - if (isLambdaEnvironment()) { - this._endpoint = getXrayDaemonEndpoint() || DEFAULT_ENDPOINT; - } else { - this._endpoint = DEFAULT_ENDPOINT; - } - } else { - this._endpoint = endpoint; - } - - this._udpExporter = new UdpExporter(this._endpoint); - this._signalPrefix = _signalPrefix || DEFAULT_FORMAT_OTEL_TRACES_BINARY_PREFIX; - } - - export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void { - const serializedData = ProtobufTraceSerializer.serializeRequest(spans); - if (serializedData == null) { - return; - } - try { - this._udpExporter.sendData(serializedData, this._signalPrefix); - return resultCallback({ code: ExportResultCode.SUCCESS }); - } catch (err) { - diag.error('Error exporting spans: %s', err); - return resultCallback({ code: ExportResultCode.FAILED }); - } - } - - forceFlush(): Promise { - return Promise.resolve(); - } - - /** Shutdown exporter. */ - shutdown(): Promise { - return new Promise((resolve, reject) => { - try { - this._udpExporter.shutdown(); - resolve(); - } catch (error) { - reject(error); - } - }); - } -} - -function isLambdaEnvironment() { - // detect if running in AWS Lambda environment - return process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined; -} - -function getXrayDaemonEndpoint() { - return process.env.AWS_XRAY_DAEMON_ADDRESS; -} diff --git a/exporters/aws-otel-otlp-udp-exporter/test/otlp-udp-exporter.test.ts b/exporters/aws-otel-otlp-udp-exporter/test/otlp-udp-exporter.test.ts deleted file mode 100644 index ce084903..00000000 --- a/exporters/aws-otel-otlp-udp-exporter/test/otlp-udp-exporter.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { diag, SpanContext, SpanKind } from '@opentelemetry/api'; -import { ExportResultCode } from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; -import { ProtobufTraceSerializer } from '@opentelemetry/otlp-transformer'; -import { OTLPUdpSpanExporter, UdpExporter } from '../src/otlp-udp-exporter'; -import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import * as sinon from 'sinon'; -import expect from 'expect'; -import { Socket } from 'dgram'; - -describe('UdpExporterTest', () => { - const endpoint = '127.0.0.1:3000'; - const host = '127.0.0.1'; - const port = 3000; - let udpExporter: UdpExporter; - let socketSend: sinon.SinonStub; - let socketClose: sinon.SinonStub<[callback?: (() => void) | undefined], Socket>; - let diagErrorSpy: sinon.SinonSpy<[message: string, ...args: unknown[]], void>; - - beforeEach(() => { - udpExporter = new UdpExporter(endpoint); - - // Stub the _socket methods - socketSend = sinon.stub(udpExporter['_socket'], 'send'); - socketClose = sinon.stub(udpExporter['_socket'], 'close'); - - // Spy on diag.error using sinon - diagErrorSpy = sinon.spy(diag, 'error'); - }); - - afterEach(() => { - sinon.restore(); // Restore the original dgram behavior - }); - - it('should parse the endpoint correctly', () => { - expect(udpExporter['_host']).toBe(host); - expect(udpExporter['_port']).toBe(port); - }); - - it('should send UDP data correctly', () => { - const data = new Uint8Array([1, 2, 3]); - const prefix = 'T1'; - const encodedData = '{"format":"json","version":1}\nT1AQID'; - const protbufBinary = Buffer.from(encodedData, 'utf-8'); - udpExporter.sendData(data, prefix); - sinon.assert.calledOnce(socketSend); - expect(socketSend.getCall(0).args[0]).toEqual(protbufBinary); - }); - - it('should handle errors when sending UDP data', () => { - const errorMessage = 'UDP send error'; - socketSend.yields(new Error(errorMessage)); // Simulate an error - - const data = new Uint8Array([1, 2, 3]); - // Expect the sendData method to throw the error - expect(() => udpExporter.sendData(data, 'T1')).toThrow(errorMessage); - // Assert that diag.error was called with the correct error message - expect(diagErrorSpy.calledOnce).toBe(true); - expect(diagErrorSpy.calledWith('Error sending UDP data: %s', sinon.match.instanceOf(Error))).toBe(true); - }); - - it('should close the socket on shutdown', () => { - udpExporter.shutdown(); - expect(socketClose.calledOnce).toBe(true); - }); - - it('should throw when provided invalid endpoint', () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - expect(() => new UdpExporter(123)).toThrow(new Error('Invalid endpoint: 123')); - }); -}); - -describe('OTLPUdpSpanExporterTest', () => { - let otlpUdpSpanExporter: OTLPUdpSpanExporter; - let udpExporterMock: { sendData: any; shutdown: any }; - let diagErrorSpy: sinon.SinonSpy<[message: string, ...args: unknown[]], void>; - const endpoint = '127.0.0.1:3000'; - const prefix = 'T1'; - const serializedData = new Uint8Array([1, 2, 3]); // Mock serialized data - // Mock ReadableSpan object - const mockSpanData: ReadableSpan = { - name: 'spanName', - kind: SpanKind.SERVER, - spanContext: () => { - const spanContext: SpanContext = { - traceId: '00000000000000000000000000000008', - spanId: '0000000000000009', - traceFlags: 0, - }; - return spanContext; - }, - startTime: [0, 0], - endTime: [0, 1], - status: { code: 0 }, - attributes: {}, - links: [], - events: [], - duration: [0, 1], - ended: true, - resource: new Resource({}), - instrumentationLibrary: { name: 'mockedLibrary' }, - droppedAttributesCount: 0, - droppedEventsCount: 0, - droppedLinksCount: 0, - }; - const spans: ReadableSpan[] = [mockSpanData]; // Mock span data - - beforeEach(() => { - // Mock UdpExporter methods - udpExporterMock = { - sendData: sinon.stub(), - shutdown: sinon.stub().resolves(), - }; - - // Stub the UdpExporter constructor to return our mock - sinon.stub(UdpExporter.prototype, 'sendData').callsFake(udpExporterMock.sendData); - sinon.stub(UdpExporter.prototype, 'shutdown').callsFake(udpExporterMock.shutdown); - - // Stub the diag.error method - diagErrorSpy = sinon.spy(diag, 'error'); - - // Create an instance of OTLPUdpSpanExporter - otlpUdpSpanExporter = new OTLPUdpSpanExporter(endpoint, prefix); - }); - - afterEach(() => { - // Restore the original functionality after each test - sinon.restore(); - }); - - it('should export spans successfully', () => { - const callback = sinon.stub(); - // Stub ProtobufTraceSerializer.serializeRequest - sinon.stub(ProtobufTraceSerializer, 'serializeRequest').returns(serializedData); - - otlpUdpSpanExporter.export(spans, callback); - - expect(udpExporterMock.sendData.calledOnceWith(serializedData, 'T1')).toBe(true); - expect(callback.calledOnceWith({ code: ExportResultCode.SUCCESS })).toBe(true); - expect(diagErrorSpy.notCalled).toBe(true); // Ensure no error was logged - }); - - it('should handle serialization failure', () => { - // Make serializeRequest return null - sinon.stub(ProtobufTraceSerializer, 'serializeRequest').returns(undefined); - const callback = sinon.stub(); - - otlpUdpSpanExporter.export(spans, callback); - - expect(callback.notCalled).toBe(true); - expect(udpExporterMock.sendData.notCalled).toBe(true); - expect(diagErrorSpy.notCalled).toBe(true); - }); - - it('should handle errors during export', () => { - const error = new Error('Export error'); - udpExporterMock.sendData.throws(error); - - const callback = sinon.stub(); - - otlpUdpSpanExporter.export(spans, callback); - - expect(diagErrorSpy.calledOnceWith('Error exporting spans: %s', sinon.match.instanceOf(Error))).toBe(true); - expect(callback.calledOnceWith({ code: ExportResultCode.FAILED })).toBe(true); - }); - - it('should forceFlush without throwing', async () => { - expect(otlpUdpSpanExporter.forceFlush()).resolves.not.toThrow(); - }); - - it('should shutdown the UDP exporter successfully', async () => { - await otlpUdpSpanExporter.shutdown(); - expect(udpExporterMock.shutdown.calledOnce).toBe(true); - }); - - it('should use expected Environment Variables to configure endpoint', () => { - process.env.AWS_LAMBDA_FUNCTION_NAME = 'testFunctionName'; - process.env.AWS_XRAY_DAEMON_ADDRESS = 'someaddress:1234'; - - const exporter = new OTLPUdpSpanExporter(undefined); - expect(exporter['_endpoint']).toBe('someaddress:1234'); - expect(exporter['_udpExporter']['_host']).toBe('someaddress'); - expect(exporter['_udpExporter']['_port']).toBe(1234); - - delete process.env.AWS_XRAY_DAEMON_ADDRESS; - delete process.env.AWS_LAMBDA_FUNCTION_NAME; - }); -}); diff --git a/exporters/aws-otel-otlp-udp-exporter/tsconfig.json b/exporters/aws-otel-otlp-udp-exporter/tsconfig.json deleted file mode 100644 index 835319a4..00000000 --- a/exporters/aws-otel-otlp-udp-exporter/tsconfig.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "compilerOptions": { - "rootDir": ".", - "outDir": "build", - - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "declaration": true, - "declarationMap": true, - "forceConsistentCasingInFileNames": true, - "inlineSources": true, - "module": "commonjs", - "noEmitOnError": true, - "noFallthroughCasesInSwitch": true, - "noImplicitOverride": true, - "noImplicitReturns": true, - "noUnusedLocals": true, - "pretty": true, - "sourceMap": true, - "strict": true, - "strictNullChecks": true, - "target": "es2017", - "incremental": true, - "newLine": "LF", - // This command allows TypeScript to import `sql_dialect_keywords.json` - "resolveJsonModule": true - }, - "include": [ - "src/**/*.ts", - "test/**/*.ts" - ], - "exclude": [ - "node_modules" - ] -} diff --git a/package-lock.json b/package-lock.json index 46596f90..9e55211e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,7 @@ "version": "0.5.0-dev0", "license": "Apache-2.0", "workspaces": [ - "aws-distro-opentelemetry-node-autoinstrumentation/", - "exporters/aws-otel-otlp-udp-exporter" + "aws-distro-opentelemetry-node-autoinstrumentation/" ], "devDependencies": { "@types/mocha": "7.0.2", @@ -158,6 +157,7 @@ "exporters/aws-otel-otlp-udp-exporter": { "name": "@aws/aws-otel-otlp-udp-exporter", "version": "0.0.1", + "extraneous": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.9.0", @@ -183,19 +183,6 @@ "node": ">=14" } }, - "exporters/aws-otel-otlp-udp-exporter/node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -1955,10 +1942,6 @@ "resolved": "aws-distro-opentelemetry-node-autoinstrumentation", "link": true }, - "node_modules/@aws/aws-otel-otlp-udp-exporter": { - "resolved": "exporters/aws-otel-otlp-udp-exporter", - "link": true - }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", diff --git a/package.json b/package.json index 2eb21839..0d82b9c8 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,6 @@ ] }, "workspaces": [ - "aws-distro-opentelemetry-node-autoinstrumentation/", - "exporters/aws-otel-otlp-udp-exporter" + "aws-distro-opentelemetry-node-autoinstrumentation/" ] } diff --git a/sample-applications/integ-test-http-server/package.json b/sample-applications/integ-test-http-server/package.json deleted file mode 100644 index 8500f5dd..00000000 --- a/sample-applications/integ-test-http-server/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "simple-express-server-for-local-testing", - "version": "1.0.0", - "description": "", - "private": true, - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "Apache-2.0", - "dependencies": { - "@aws/aws-otel-otlp-udp-exporter": "file:../../exporters/aws-otel-otlp-udp-exporter/aws-aws-otel-otlp-udp-exporter-0.0.1.tgz", - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/id-generator-aws-xray": "^1.2.2", - "@opentelemetry/propagator-aws-xray": "^1.25.1", - "@opentelemetry/sdk-node": "^0.52.1", - "@opentelemetry/sdk-trace-base": "^1.25.1", - "@opentelemetry/sdk-trace-node": "^1.25.1", - "@types/express": "^4.17.21", - "express": "^4.19.2" - }, - "devDependencies": { - "@types/node": "^22.1.0" - } -} diff --git a/sample-applications/integ-test-http-server/udp-exporter-test-server.js b/sample-applications/integ-test-http-server/udp-exporter-test-server.js deleted file mode 100644 index 9f51e718..00000000 --- a/sample-applications/integ-test-http-server/udp-exporter-test-server.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; - -const { trace, SpanKind, context } = require('@opentelemetry/api'); -const { AlwaysOnSampler } = require('@opentelemetry/sdk-trace-node'); -const express = require('express'); -const process = require('process'); -const opentelemetry = require("@opentelemetry/sdk-node"); -const { SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base'); -const { AWSXRayPropagator } = require("@opentelemetry/propagator-aws-xray"); -const { AWSXRayIdGenerator } = require("@opentelemetry/id-generator-aws-xray"); -const { OTLPUdpSpanExporter } = require("@aws/aws-otel-otlp-udp-exporter") - -const _traceExporter = new OTLPUdpSpanExporter(); -const _spanProcessor = new SimpleSpanProcessor(_traceExporter); - -const PORT = parseInt(process.env.SAMPLE_APP_PORT || '8080', 10); -const app = express(); - -app.get('/', (req, res) => { - res.send(`healthcheck`) -}); - -app.get('/test', (req, res) => { - const tracer = trace.getTracer("testTracer"); - let ctx = context.active(); - let span = tracer.startSpan("testSpan", {kind: SpanKind.SERVER}, ctx); - let traceId = span.spanContext().traceId; - span.end(); - let xrayFormatTraceId = "1-" + traceId.substring(0,8) + "-" + traceId.substring(8); - console.log(`X-Ray Trace ID is: ${xrayFormatTraceId}`); - - res.send(`${xrayFormatTraceId}`); -}); - -app.listen(PORT, async () => { - await nodeSDKBuilder(); - console.log(`Listening for requests on http://localhost:${PORT}`); -}); - -async function nodeSDKBuilder() { - const sdk = new opentelemetry.NodeSDK({ - textMapPropagator: new AWSXRayPropagator(), - instrumentations: [], - spanProcessor: _spanProcessor, - sampler: new AlwaysOnSampler(), - idGenerator: new AWSXRayIdGenerator(), - }); - - // this enables the API to record telemetry - await sdk.start(); - - // gracefully shut down the SDK on process exit - process.on('SIGTERM', () => { - sdk.shutdown() - .then(() => console.log('Tracing terminated')) - .catch((error) => console.log('Error terminating tracing', error)) - .finally(() => process.exit(0)); - }); -}