├── .eslintrc.json
├── .github
├── CODEOWNERS
├── PULL_REQUEST_TEMPLATE.md
├── stale.yml
└── workflows
│ ├── continuous-monitoring.yml
│ ├── deprecate_version.yml
│ ├── lint.yml
│ ├── pr-build.yml
│ ├── release.yml
│ └── smoke-test.yml
├── .gitignore
├── .nycrc.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── README.md
├── images
└── example_servicemap.png
├── lerna.json
├── package-lock.json
├── package.json
├── packages
├── core
│ ├── .eslintignore
│ ├── .eslintrc.json
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── Gruntfile.js
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── doc-src
│ │ └── templates
│ │ │ └── layout.tmpl
│ ├── jsdoc_conf.json
│ ├── lib
│ │ ├── aws-xray.d.ts
│ │ ├── aws-xray.js
│ │ ├── capture.d.ts
│ │ ├── capture.js
│ │ ├── context_utils.d.ts
│ │ ├── context_utils.js
│ │ ├── daemon_config.d.ts
│ │ ├── daemon_config.js
│ │ ├── database
│ │ │ ├── sql_data.d.ts
│ │ │ └── sql_data.js
│ │ ├── env
│ │ │ ├── aws_lambda.js
│ │ │ └── sqs_message_helper.js
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ ├── logger.d.ts
│ │ ├── logger.js
│ │ ├── middleware
│ │ │ ├── incoming_request_data.d.ts
│ │ │ ├── incoming_request_data.js
│ │ │ ├── mw_utils.d.ts
│ │ │ ├── mw_utils.js
│ │ │ └── sampling
│ │ │ │ ├── default_sampler.js
│ │ │ │ ├── local_reservoir.js
│ │ │ │ ├── local_sampler.js
│ │ │ │ ├── reservoir.js
│ │ │ │ ├── rule_cache.js
│ │ │ │ ├── rule_poller.js
│ │ │ │ ├── sampling_rule.js
│ │ │ │ ├── service_connector.js
│ │ │ │ └── target_poller.js
│ │ ├── patchers
│ │ │ ├── aws3_p.d.ts
│ │ │ ├── aws3_p.ts
│ │ │ ├── aws_p.d.ts
│ │ │ ├── aws_p.js
│ │ │ ├── call_capturer.js
│ │ │ ├── http_p.d.ts
│ │ │ ├── http_p.js
│ │ │ ├── promise_p.d.ts
│ │ │ └── promise_p.js
│ │ ├── resources
│ │ │ ├── aws_whitelist.json
│ │ │ └── default_sampling_rules.json
│ │ ├── segment_emitter.d.ts
│ │ ├── segment_emitter.js
│ │ ├── segments
│ │ │ ├── attributes
│ │ │ │ ├── aws.d.ts
│ │ │ │ ├── aws.js
│ │ │ │ ├── captured_exception.js
│ │ │ │ ├── remote_request_data.js
│ │ │ │ ├── subsegment.d.ts
│ │ │ │ ├── subsegment.js
│ │ │ │ ├── trace_id.d.ts
│ │ │ │ └── trace_id.js
│ │ │ ├── plugins
│ │ │ │ ├── ec2_plugin.d.ts
│ │ │ │ ├── ec2_plugin.js
│ │ │ │ ├── ecs_plugin.d.ts
│ │ │ │ ├── ecs_plugin.js
│ │ │ │ ├── elastic_beanstalk_plugin.d.ts
│ │ │ │ ├── elastic_beanstalk_plugin.js
│ │ │ │ └── plugin.js
│ │ │ ├── segment.d.ts
│ │ │ ├── segment.js
│ │ │ ├── segment_utils.d.ts
│ │ │ └── segment_utils.js
│ │ ├── utils.d.ts
│ │ └── utils.js
│ ├── package.json
│ ├── test-d
│ │ └── index.test-d.ts
│ ├── test
│ │ ├── integration
│ │ │ └── segment_maintained_across_shared_promise.test.js
│ │ ├── resources
│ │ │ ├── custom_sampling.json
│ │ │ └── custom_whitelist.json
│ │ └── unit
│ │ │ ├── aws-xray.test.js
│ │ │ ├── capture.test.js
│ │ │ ├── context_utils.test.js
│ │ │ ├── daemon_config.test.js
│ │ │ ├── env
│ │ │ ├── aws_lambda.test.js
│ │ │ └── sqs_message_helper.test.js
│ │ │ ├── logger.test.js
│ │ │ ├── middleware
│ │ │ ├── incoming_request_data.test.js
│ │ │ └── mw_utils.test.js
│ │ │ ├── patchers
│ │ │ ├── aws3_p.test.js
│ │ │ ├── aws_p.test.js
│ │ │ ├── call_capturer.test.js
│ │ │ ├── http_p.test.js
│ │ │ └── promise_p.test.js
│ │ │ ├── sampling
│ │ │ ├── local_reservoir.test.js
│ │ │ ├── local_sampler.test.js
│ │ │ ├── rule_cache.test.js
│ │ │ └── service_connector.test.js
│ │ │ ├── segment_emitter.test.js
│ │ │ ├── segments
│ │ │ ├── attributes
│ │ │ │ ├── aws.test.js
│ │ │ │ ├── captured_exception.test.js
│ │ │ │ ├── remote_request_data.test.js
│ │ │ │ ├── subsegment.test.js
│ │ │ │ └── trace_id.test.js
│ │ │ ├── plugins
│ │ │ │ ├── ec2_plugin.test.js
│ │ │ │ ├── ecs_plugin.test.js
│ │ │ │ ├── elastic_beanstalk_plugin.test.js
│ │ │ │ └── plugin.test.js
│ │ │ ├── segment.test.js
│ │ │ └── segment_utils.test.js
│ │ │ ├── test_utils.js
│ │ │ └── utils.test.js
│ ├── test_async
│ │ └── integration
│ │ │ └── segment_maintained_across_awaited.test.js
│ └── tsconfig.json
├── express
│ ├── .eslintrc.json
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── lib
│ │ ├── express_mw.d.ts
│ │ ├── express_mw.js
│ │ ├── index.d.ts
│ │ └── index.js
│ ├── package.json
│ ├── test-d
│ │ └── index.test-d.ts
│ ├── test
│ │ ├── test_utils.js
│ │ └── unit
│ │ │ └── express_mw.test.js
│ └── tsconfig.json
├── full_sdk
│ ├── .eslintrc.json
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── lib
│ │ ├── index.d.ts
│ │ └── index.js
│ ├── package.json
│ ├── test-d
│ │ └── index.test-d.ts
│ └── tsconfig.json
├── mysql
│ ├── .eslintrc.json
│ ├── .npmignore
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── lib
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ ├── mysql_p.d.ts
│ │ └── mysql_p.js
│ ├── package.json
│ ├── test-d
│ │ └── index.test-d.ts
│ ├── test
│ │ ├── test_utils.js
│ │ └── unit
│ │ │ └── mysql_p.test.js
│ └── tsconfig.json
├── postgres
│ ├── .eslintrc.json
│ ├── .npmignore
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── lib
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ ├── postgres_p.d.ts
│ │ └── postgres_p.js
│ ├── package.json
│ ├── test-d
│ │ └── index.test-d.ts
│ ├── test
│ │ ├── test_utils.js
│ │ └── unit
│ │ │ └── postgres_p.test.js
│ └── tsconfig.json
├── restify
│ ├── .eslintrc.json
│ ├── .npmignore
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── lib
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ ├── restify_mw.d.ts
│ │ └── restify_mw.js
│ ├── package.json
│ ├── test-d
│ │ └── index.test-d.ts
│ ├── test
│ │ ├── test_utils.js
│ │ └── unit
│ │ │ └── restify_mw.test.js
│ └── tsconfig.json
└── test_express
│ ├── .eslintrc.json
│ ├── package.json
│ └── test
│ ├── aws.js
│ ├── express.js
│ ├── helpers.js
│ └── shaky_stream.js
├── scripts
└── cp-with-structure.sh
├── sdk_contrib
├── fastify
│ ├── .eslintignore
│ ├── .eslintrc.json
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── lib
│ │ ├── hooks
│ │ │ ├── on-error.hook.js
│ │ │ ├── on-request.hook.js
│ │ │ └── on-response.hook.js
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ ├── plugin.d.ts
│ │ ├── plugin.js
│ │ └── private
│ │ │ └── configure-aws-x-ray-sync.js
│ ├── package.json
│ ├── sample
│ │ ├── index.js
│ │ └── xray-plugin.js
│ ├── test-d
│ │ └── index.test-d.ts.js
│ └── test
│ │ ├── .eslintrc
│ │ └── unit
│ │ └── xray.test.js
├── fetch
│ ├── .eslintignore
│ ├── .eslintrc.json
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── lib
│ │ ├── fetch_p.d.ts
│ │ ├── fetch_p.js
│ │ ├── subsegment_fetch.d.ts
│ │ └── subsegment_fetch.js
│ ├── package.json
│ ├── test-d
│ │ └── index.test-d.ts
│ ├── test
│ │ ├── global.js
│ │ ├── integration
│ │ │ └── fetch_p.test.js
│ │ └── unit
│ │ │ └── fetch_p.test.js
│ └── tsconfig.json
├── hapi
│ ├── .eslintignore
│ ├── .eslintrc.json
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── lib
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ ├── plugin.js
│ │ └── xray.js
│ ├── package.json
│ ├── sample
│ │ ├── index.js
│ │ └── xray-plugin.js
│ ├── test-d
│ │ └── index.test-d.ts.js
│ └── test
│ │ ├── .eslintrc
│ │ └── unit
│ │ └── xray.test.js
└── koa
│ ├── .eslintrc.json
│ ├── LICENSE
│ ├── NOTICE.txt
│ ├── README.md
│ ├── lib
│ ├── index.d.ts
│ ├── index.js
│ ├── koa_mw.d.ts
│ └── koa_mw.js
│ ├── package.json
│ ├── test-d
│ └── index.test-d.ts.js
│ └── test
│ ├── .eslintrc
│ ├── test_utils.js
│ └── unit
│ └── koa_mw.test.js
└── smoke_test
├── package.json
└── test
└── smoke.test.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "mocha": true,
5 | "es6": true
6 | },
7 | "parser": "@typescript-eslint/parser",
8 | "extends": [
9 | "plugin:@typescript-eslint/recommended",
10 | "plugin:import/recommended",
11 | "plugin:import/typescript"
12 | ],
13 | "rules": {
14 | "indent": [
15 | "error",
16 | 2
17 | ],
18 | "curly": [
19 | "error",
20 | "all"
21 | ],
22 | "space-before-blocks": "error",
23 | "no-console": "off",
24 | "brace-style": "error",
25 | "keyword-spacing": "error",
26 | "import/no-unresolved": "off",
27 | "linebreak-style": [
28 | "error",
29 | "unix"
30 | ],
31 | "quotes": [
32 | "error",
33 | "single"
34 | ],
35 | "semi": [
36 | "error",
37 | "always"
38 | ],
39 | "eol-last": [
40 | "error",
41 | "always"
42 | ],
43 | "no-trailing-spaces": "error",
44 |
45 | /** Turn off enforcement */
46 | "@typescript-eslint/ban-types": "off",
47 | "@typescript-eslint/ban-ts-comment": "off",
48 | "@typescript-eslint/no-var-requires": "off",
49 | "@typescript-eslint/no-empty-function": "off",
50 | "@typescript-eslint/no-empty-interface": "off",
51 | "@typescript-eslint/no-explicit-any": "off",
52 | "@typescript-eslint/explicit-module-boundary-types": "off",
53 | "prefer-rest-params": "off",
54 | "@typescript-eslint/no-non-null-assertion": "off"
55 | },
56 | "parserOptions": {
57 | "ecmaVersion": 2020
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | #####################################################
2 | #
3 | # List of approvers for this repository
4 | #
5 | #####################################################
6 | #
7 | # Learn about CODEOWNERS file format:
8 | # https://help.github.com/en/articles/about-code-owners
9 | #
10 |
11 | * @aws/aws-x-ray
12 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | *Issue #, if available:*
2 |
3 | *Description of changes:*
4 |
5 |
6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
7 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 30
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Limit to only `issues` or `pulls`
6 | only: issues
7 | # Issues with these labels will never be considered stale
8 | exemptLabels:
9 | - pinned
10 | - bug
11 | - enhancement
12 | - feature-request
13 | - help wanted
14 | - work-in-progress
15 | - pending release
16 | # Label to use when marking an issue as stale
17 | staleLabel: stale
18 | # Comment to post when marking an issue as stale. Set to `false` to disable
19 | markComment: >
20 | This issue has been automatically marked as stale because it has not had
21 | recent activity. It will be closed if no further activity occurs in next 7 days. Thank you
22 | for your contributions.
23 | # Comment to post when closing a stale issue. Set to `false` to disable
24 | closeComment: false
25 |
--------------------------------------------------------------------------------
/.github/workflows/continuous-monitoring.yml:
--------------------------------------------------------------------------------
1 | name: Continuous monitoring of distribution channels
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '0/10 * * * *'
7 |
8 | permissions:
9 | id-token: write
10 | contents: read
11 |
12 | jobs:
13 | smoke-tests:
14 | name: Run smoke tests
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Checkout AWS XRay SDK Node Repository @ default branch latest
18 | uses: actions/checkout@v2
19 |
20 | - name: Configure AWS Credentials
21 | uses: aws-actions/configure-aws-credentials@v4
22 | with:
23 | role-to-assume: ${{ secrets.AWS_INTEG_TEST_ROLE_ARN }}
24 | aws-region: us-east-1
25 |
26 | - name: Setup Node
27 | uses: actions/setup-node@v1
28 | with:
29 | node-version: '16.x'
30 |
31 | - name: Run smoke test
32 | id: distribution-availability
33 | run: |
34 | cd smoke_test
35 | npm install
36 | npm run test-smoke
37 |
38 | - name: Publish metric on X-Ray Node SDK distribution availability
39 | if: ${{ always() }}
40 | run: |
41 | if [[ "${{ steps.distribution-availability.outcome }}" == "failure" ]]; then
42 | aws cloudwatch put-metric-data --metric-name XRayNodeSDKDistributionUnavailability --dimensions failure=rate --namespace MonitorSDK --value 1 --timestamp $(date +%s)
43 | else
44 | aws cloudwatch put-metric-data --metric-name XRayNodeSDKDistributionUnavailability --dimensions failure=rate --namespace MonitorSDK --value 0 --timestamp $(date +%s)
45 | fi
46 |
--------------------------------------------------------------------------------
/.github/workflows/deprecate_version.yml:
--------------------------------------------------------------------------------
1 | name: Deprecate X-Ray Node SDK Version
2 | on:
3 | workflow_dispatch:
4 |
5 | jobs:
6 | deprecate_xray_node_sdk_version:
7 | name: Deprecate X-Ray Node SDK version in NPM registry
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Setup Node
11 | uses: actions/setup-node@v1
12 | with:
13 | node-version: '16.x'
14 | registry-url: 'https://registry.npmjs.org'
15 |
16 | - run: npm install -g npm@8.19.4
17 |
18 | - name: Deprecate Version 3.7.0
19 | run: |
20 | npm deprecate aws-xray-sdk@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
21 | npm deprecate aws-xray-sdk-core@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
22 | npm deprecate aws-xray-sdk-express@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
23 | npm deprecate aws-xray-sdk-postgres@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
24 | npm deprecate aws-xray-sdk-mysql@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
25 | npm deprecate aws-xray-sdk-restify@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
26 | npm deprecate aws-xray-sdk-hapi@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
27 | npm deprecate aws-xray-sdk-koa2@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
28 | npm deprecate aws-xray-sdk-fastify@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
29 | npm deprecate aws-xray-sdk-fetch@3.7.0 "3.7.0 is deprecated due to known issue in Lambda"
30 | env:
31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 | lint:
11 | name: Check code style
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout AWS XRay SDK Node Repository @ default branch latest
15 | uses: actions/checkout@v3
16 |
17 | - name: Setup Node
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version: 16.x
21 |
22 | - run: npm install -g npm@8.19.4
23 |
24 | - name: Cache NPM modules
25 | uses: actions/cache@v3
26 | with:
27 | path: |
28 | node_modules
29 | package-lock.json
30 | packages/*/node_modules
31 | packages/*/package-lock.json
32 | key: lint-${{ runner.os }}-${{ hashFiles('package.json', 'packages/*/package.json') }}-06142023
33 |
34 | - name: Bootstrap
35 | run: |
36 | npm ci
37 | npx lerna bootstrap --no-ci --hoist
38 |
39 | - name: Lint
40 | run: npx lerna run lint
41 |
--------------------------------------------------------------------------------
/.github/workflows/pr-build.yml:
--------------------------------------------------------------------------------
1 | name: Node.js SDK Continuous Build
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 | build:
11 | name: Build Node ${{ matrix.node-version }} on ${{ matrix.os }}
12 | runs-on: ${{ matrix.os }}
13 | strategy:
14 | fail-fast: false
15 | matrix:
16 | os:
17 | - macos-latest
18 | - ubuntu-latest
19 | - windows-latest
20 | node-version:
21 | - 14.x
22 | - 16.x
23 | - 18.x
24 | - 20.x
25 | include:
26 | - os: ubuntu-latest
27 | node-version: 16.x
28 | coverage: true
29 | - os: macos-13
30 | node-version: 14.x
31 | exclude:
32 | # Issue with npm6 on windows resulting in failing workflows:
33 | # https://github.com/npm/cli/issues/4341#issuecomment-1040608101
34 | # Since node14 is EOL, we can drop this set from our tests.
35 | # We still test node14 on other platforms.
36 | - os: windows-latest
37 | node-version: 14.x
38 | # https://github.com/actions/runner-images/issues/9741
39 | # macos-latest provides only ARM hosts
40 | # https://github.com/nodejs/node/issues/36161
41 | # https://github.com/nodejs/node/issues/40126
42 | # Without workarounds, Node.js 14 isn't supported on ARM macos
43 | # As workaround, test on macos-13 version instead
44 | - os: macos-latest
45 | node-version: 14.x
46 |
47 | steps:
48 | - name: Checkout AWS XRay SDK Node Repository @ default branch latest
49 | uses: actions/checkout@v3
50 |
51 | - name: Setup Node ${{ matrix.node-version }}
52 | uses: actions/setup-node@v3
53 | with:
54 | node-version: ${{ matrix.node-version }}
55 | check-latest: true
56 |
57 | - run: npm install -g npm@8.19.4
58 |
59 | - name: Cache NPM modules
60 | uses: actions/cache@v3
61 | with:
62 | path: |
63 | node_modules
64 | package-lock.json
65 | packages/*/node_modules
66 | packages/*/package-lock.json
67 | key: ${{ matrix.os }}-${{ matrix.node-version }}-${{ hashFiles('package.json', 'packages/*/package.json') }}-06142023
68 |
69 | - name: Bootstrap
70 | run: |
71 | npm ci
72 | npx lerna bootstrap --no-ci --hoist
73 |
74 | - name: Build
75 | run: |
76 | npx lerna run compile
77 | shell: bash
78 |
79 | - name: Execute tests with Lerna
80 | if: '!matrix.coverage'
81 | run: |
82 | npx lerna run test
83 | shell: bash
84 |
85 | # Only need to report coverage once, so only run instrumented tests on one Node version/OS
86 | # Use lerna to get reports from all packages
87 | - name: Report coverage
88 | if: matrix.coverage
89 | run: |
90 | npx lerna run testcov
91 | npx lerna run reportcov
92 | env:
93 | CI: true
94 | - name: Upload coverage to Codecov
95 | uses: codecov/codecov-action@v4
96 | if: matrix.coverage
97 | with:
98 | directory: ./packages/core/
99 | token: ${{ secrets.CODECOV_TOKEN }}
100 | files: ./coverage.lcov
101 | verbose: true
102 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release X-Ray Node SDK to Github and NPM
2 | on:
3 | workflow_dispatch:
4 | inputs:
5 | version:
6 | description: The version to tag the release with, e.g., 1.2.0, 1.2.1-alpha.1
7 | required: true
8 |
9 | jobs:
10 | publish_xray_node_sdk:
11 | name: Test and publish X-Ray Node SDK to NPM registry and Github
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout Repository
15 | uses: actions/checkout@v2
16 | with:
17 | fetch-depth: 0
18 |
19 | - name: Setup Node
20 | uses: actions/setup-node@v1
21 | with:
22 | node-version: '16.x'
23 | registry-url: 'https://registry.npmjs.org'
24 |
25 | - run: npm install -g npm@8.19.4
26 |
27 | - name: Cache NPM modules
28 | uses: actions/cache@v3
29 | with:
30 | path: |
31 | node_modules
32 | package-lock.json
33 | packages/*/node_modules
34 | packages/*/package-lock.json
35 | key: release-ubuntu-latest-${{ hashFiles('package.json', 'packages/*/package.json') }}-06142023
36 |
37 | - name: Bootstrap
38 | run: |
39 | npm ci
40 | npx lerna bootstrap --no-ci --hoist
41 |
42 | - name: Build
43 | run: |
44 | npx lerna run compile
45 | shell: bash
46 |
47 | - name: Execute tests with Lerna
48 | run: |
49 | npx lerna run test
50 | shell: bash
51 |
52 | - name: Publish package to npm
53 | run: npx lerna publish from-package --yes --exact --no-verify-access
54 | env:
55 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
56 |
57 | - name: Create Release
58 | id: create_release
59 | uses: actions/create-release@v1
60 | env:
61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62 | with:
63 | tag_name: 'aws-xray-sdk-node@${{ github.event.inputs.version }}'
64 | release_name: 'Release ${{ github.event.inputs.version }}'
65 | body: 'Please see [CHANGELOG](https://github.com/aws/aws-xray-sdk-node/blob/master/CHANGELOG.md) for details.'
66 | draft: true
67 | prerelease: false
68 |
--------------------------------------------------------------------------------
/.github/workflows/smoke-test.yml:
--------------------------------------------------------------------------------
1 | name: Node.js Continuous Integration Smoke Test
2 |
3 | on:
4 | pull_request:
5 | branches: [master]
6 |
7 | jobs:
8 | smoke-test:
9 | name: Run smoke test
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout AWS XRay SDK Node Repository @ default branch latest
13 | uses: actions/checkout@v2
14 |
15 | - name: Setup Node
16 | uses: actions/setup-node@v1
17 | with:
18 | node-version: '16.x'
19 |
20 | # Use npm pack to bundle individual packages into their final distribution tarball form
21 | # Then aggregate all of those tarballs in the full_sdk package, to be used by smoke test
22 | - name: Pack SDK packages
23 | id: package
24 | run: |
25 | npm install typescript
26 | core_tar=$(cd packages/core && npm pack | tail -1)
27 | express_tar=$(cd packages/express && npm pack | tail -1)
28 | mysql_tar=$(cd packages/mysql && npm pack | tail -1)
29 | postgres_tar=$(cd packages/postgres && npm pack | tail -1)
30 | cd packages/full_sdk
31 | npm install ../core/$core_tar
32 | npm install ../express/$express_tar
33 | npm install ../mysql/$mysql_tar
34 | npm install ../postgres/$postgres_tar
35 | full_tar=$(npm pack | tail -1)
36 | echo "::set-output name=full_sdk::$full_tar"
37 |
38 | # Manually install the locally bundled packages first, then pull in other test dependencies from NPM
39 | # Remove the entire source code directory to force smoke test to depend on packed tarball
40 | - name: Run smoke test
41 | run: |
42 | cd smoke_test
43 | npm install ../packages/full_sdk/${{ steps.package.outputs.full_sdk }}
44 | npm install
45 | rm -rf ../packages/
46 | npm run test-smoke
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | docs
4 | .idea/
5 | .nyc_output/
6 | *.lcov
7 | dist
8 |
--------------------------------------------------------------------------------
/.nycrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "all": true,
3 | "include": [
4 | "**/dist/lib/**/*.js"
5 | ],
6 | "exclude": [
7 | "**/*.d.ts",
8 | "**/sample/**",
9 | "**/test/**",
10 | "**/test-d/**",
11 | "**/test_async/**",
12 | "**/docs/**"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check [existing open](https://github.com/aws/aws-xray-sdk-node/issues), or [recently closed](https://github.com/aws/aws-xray-sdk-node/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *master* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional documents on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws/aws-xray-sdk-node/labels/help%20wanted) issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](https://github.com/aws/aws-xray-sdk-node/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
62 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
--------------------------------------------------------------------------------
/images/example_servicemap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws/aws-xray-sdk-node/f17e750e785bd1144bfb282055b12707ca0f3727/images/example_servicemap.png
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "lerna": "3.15.0",
3 | "packages": [
4 | ".",
5 | "packages/*",
6 | "sdk_contrib/*"
7 | ],
8 | "version": "independent",
9 | "command": {
10 | "publish": {
11 | "allowBranch": ["master", "2.x"]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-node",
3 | "version": "3.10.3",
4 | "private": true,
5 | "license": "Apache-2.0",
6 | "overrides": {
7 | "cls-hooked": {
8 | "semver": "^7.5.3"
9 | }
10 | },
11 | "devDependencies": {
12 | "@hapi/hapi": "^20.0.0",
13 | "@smithy/config-resolver": "^3.0.5",
14 | "@smithy/middleware-stack": "^3.0.3",
15 | "@smithy/node-config-provider": "^3.1.4",
16 | "@smithy/smithy-client": "^3.1.7",
17 | "@types/chai": "^4.2.12",
18 | "@types/koa": "^2.11.3",
19 | "@types/mocha": "^8.0.0",
20 | "@types/node": "^10.17.28",
21 | "@types/sinon": "^9.0.4",
22 | "@types/sinon-chai": "^3.2.4",
23 | "@typescript-eslint/eslint-plugin": "^4.25.0",
24 | "@typescript-eslint/parser": "^4.25.0",
25 | "aws-sdk": "^2.304.0",
26 | "aws-xray-sdk-core": "3.10.3",
27 | "aws-xray-sdk-express": "3.10.3",
28 | "chai": "^4.2.0",
29 | "cls-hooked": "^4.2.2",
30 | "codecov": "^3.8.3",
31 | "eslint": "^7.5.0",
32 | "eslint-config-prettier": "^6.11.0",
33 | "eslint-config-semistandard": "^15.0.1",
34 | "eslint-config-standard": "^14.1.1",
35 | "eslint-plugin-import": "^2.23.3",
36 | "eslint-plugin-mocha": "^7.0.1",
37 | "eslint-plugin-node": "^11.1.0",
38 | "eslint-plugin-prettier": "^3.1.4",
39 | "eslint-plugin-promise": "^4.2.1",
40 | "eslint-plugin-standard": "^4.0.1",
41 | "express": "^4.16.4",
42 | "fastify": "^4.0.1",
43 | "fastify-plugin": "^4.2.0",
44 | "grunt": "^1.0.4",
45 | "grunt-contrib-clean": "^1.0.0",
46 | "grunt-jsdoc": "^2.4.0",
47 | "koa": "^2.13.0",
48 | "lerna": "^6.6.2",
49 | "mocha": "^10.2.0",
50 | "nock": "^13.2.9",
51 | "nyc": "^15.1.0",
52 | "prettier": "^2.0.5",
53 | "rewire": "^4.0.1",
54 | "sinon": "^9.0.2",
55 | "sinon-chai": "^3.5.0",
56 | "tsd": "^0.25.0",
57 | "typescript": "^4.4.4",
58 | "upath": "^1.2.0"
59 | },
60 | "engines": {
61 | "node": ">= 14.x",
62 | "npm": ">= 2.x"
63 | },
64 | "dependencies": {
65 | "aws-xray-sdk": "file:packages/full_sdk",
66 | "aws-xray-sdk-core": "file:packages/core",
67 | "aws-xray-sdk-express": "file:packages/express",
68 | "aws-xray-sdk-fastify": "file:sdk_contrib/fastify",
69 | "aws-xray-sdk-fetch": "file:sdk_contrib/fetch",
70 | "aws-xray-sdk-hapi": "file:sdk_contrib/hapi",
71 | "aws-xray-sdk-koa2": "file:sdk_contrib/koa",
72 | "aws-xray-sdk-mysql": "file:packages/mysql",
73 | "aws-xray-sdk-node": "file:",
74 | "aws-xray-sdk-postgres": "file:packages/postgres",
75 | "aws-xray-sdk-restify": "file:packages/restify",
76 | "test-aws-xray-sdk-express": "file:packages/test_express"
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/packages/core/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/packages/core/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/core/.npmignore:
--------------------------------------------------------------------------------
1 | .npmignore
2 | node_modules
3 | npm-debug.log
4 | docs
5 | AWSXRay.log
6 | Config
7 | fat_sdk
8 | core
9 | express
10 | mysql
11 | postgres
12 |
--------------------------------------------------------------------------------
/packages/core/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 | // Project configuration.
3 | grunt.initConfig({
4 | jsdoc: {
5 | dist: {
6 | src: ['lib/**/*.js', 'README.md'],
7 | dest: 'docs',
8 | options: {
9 | configure: 'jsdoc_conf.json'
10 | }
11 | }
12 | },
13 | clean: {
14 | folder: ['docs']
15 | }
16 | });
17 |
18 | // Register jsdoc as a grunt task
19 | grunt.loadNpmTasks('grunt-jsdoc');
20 | grunt.loadNpmTasks('grunt-contrib-clean');
21 |
22 | grunt.registerTask('docs', ['clean', 'jsdoc']);
23 | };
24 |
--------------------------------------------------------------------------------
/packages/core/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK Core for JavaScript
2 | Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/packages/core/doc-src/templates/layout.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JSDoc:
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/packages/core/jsdoc_conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": {
3 | "default": {
4 | "layoutFile": "doc-src/templates/layout.tmpl"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/core/lib/aws-xray.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-vars */
2 | import * as ec2Plugin from './segments/plugins/ec2_plugin';
3 | import * as ecsPlugin from './segments/plugins/ecs_plugin';
4 | import * as elasticBeanstalkPlugin from './segments/plugins/elastic_beanstalk_plugin';
5 | import * as segmentUtils from './segments/segment_utils';
6 | import * as utils from './utils';
7 | import * as middleware from './middleware/mw_utils';
8 | import Segment = require('./segments/segment');
9 | import Subsegment = require('./segments/attributes/subsegment');
10 | import sqlData = require('./database/sql_data');
11 | import TraceID = require('./segments/attributes/trace_id');
12 |
13 | export namespace plugins {
14 | const EC2Plugin: typeof ec2Plugin;
15 | const ECSPlugin: typeof ecsPlugin;
16 | const ElasticBeanstalkPlugin: typeof elasticBeanstalkPlugin;
17 |
18 | type EC2Plugin = typeof ec2Plugin;
19 | type ECSPlugin = typeof ecsPlugin;
20 | type ElasticBeanstalkPlugin = typeof elasticBeanstalkPlugin;
21 |
22 | type EC2Metadata = ec2Plugin.EC2Metadata;
23 | type ECSMetadata = ecsPlugin.ECSMetadata;
24 | type ElasticBeanstalkMetadata = elasticBeanstalkPlugin.ElasticBeanstalkMetadata;
25 |
26 | type Plugin = EC2Plugin | ECSPlugin | ElasticBeanstalkPlugin;
27 | }
28 |
29 | export function config(plugins: plugins.Plugin[]): void;
30 |
31 | export { appendAWSWhitelist, setAWSWhitelist } from './segments/attributes/aws';
32 |
33 | export { setStreamingThreshold } from './segments/segment_utils';
34 |
35 | export { setLogger, getLogger, Logger } from './logger';
36 |
37 | export { setDaemonAddress } from './daemon_config';
38 |
39 | export { captureAsyncFunc, captureCallbackFunc, captureFunc } from './capture';
40 |
41 | export { captureAWS, captureAWSClient } from './patchers/aws_p';
42 |
43 | export { captureAWSClient as captureAWSv3Client } from './patchers/aws3_p';
44 |
45 | export { captureHTTPs, captureHTTPsGlobal } from './patchers/http_p';
46 |
47 | export { capturePromise } from './patchers/promise_p';
48 |
49 | export { utils };
50 |
51 | export namespace database {
52 | const SqlData: typeof sqlData;
53 | type SqlData = sqlData;
54 | }
55 |
56 | export { middleware };
57 |
58 | export {
59 | getNamespace,
60 | resolveSegment,
61 | resolveManualSegmentParams,
62 | getSegment,
63 | setSegment,
64 | isAutomaticMode,
65 | enableAutomaticMode,
66 | enableManualMode,
67 | setContextMissingStrategy
68 | } from './context_utils';
69 |
70 | export {
71 | Segment,
72 | Subsegment,
73 | TraceID,
74 | segmentUtils as SegmentUtils
75 | };
76 |
77 | export type SegmentLike = Segment | Subsegment;
78 |
--------------------------------------------------------------------------------
/packages/core/lib/capture.d.ts:
--------------------------------------------------------------------------------
1 | import Segment = require('./segments/segment');
2 | import Subsegment = require('./segments/attributes/subsegment');
3 |
4 | export function captureFunc(name: string, fcn: (subsegment?: Subsegment) => T, parent?: Segment | Subsegment): T;
5 |
6 | export function captureAsyncFunc(
7 | name: string,
8 | fcn: (subsegment?: Subsegment) => T,
9 | parent?: Segment | Subsegment
10 | ): T;
11 |
12 | export function captureCallbackFunc(
13 | name: string,
14 | fcn: (...args: S) => T,
15 | parent?: Segment | Subsegment
16 | ): (...args: S) => T;
17 |
--------------------------------------------------------------------------------
/packages/core/lib/context_utils.d.ts:
--------------------------------------------------------------------------------
1 | import { Namespace } from 'cls-hooked';
2 | import Segment = require('./segments/segment');
3 | import Subsegment = require('./segments/attributes/subsegment');
4 |
5 | export function getNamespace(): Namespace;
6 |
7 | export function resolveSegment(segment?: Segment | Subsegment | null): Segment | Subsegment | undefined;
8 |
9 | export function resolveManualSegmentParams(segment?: Segment | Subsegment | null): Segment | Subsegment | undefined;
10 |
11 | export function getSegment(): Segment | Subsegment | undefined;
12 |
13 | export function setSegment(segment: Segment | Subsegment): void;
14 |
15 | export function isAutomaticMode(): boolean;
16 |
17 | export function enableAutomaticMode(): void;
18 |
19 | export function enableManualMode(): void;
20 |
21 | export type ContextMissingStrategy = 'LOG_ERROR' | 'RUNTIME_ERROR' | 'IGNORE_ERROR' | ((msg: string) => void);
22 |
23 | export function setContextMissingStrategy(strategy: ContextMissingStrategy): void;
24 |
--------------------------------------------------------------------------------
/packages/core/lib/daemon_config.d.ts:
--------------------------------------------------------------------------------
1 | export function setDaemonAddress(address: string): void;
2 |
--------------------------------------------------------------------------------
/packages/core/lib/daemon_config.js:
--------------------------------------------------------------------------------
1 | var logger = require('./logger');
2 |
3 | /**
4 | * A module representing the X-Ray daemon configuration including the udp and tcp addresses.
5 | * @module DaemonConfig
6 | */
7 | var DaemonConfig = {
8 | udp_ip: '127.0.0.1',
9 | udp_port: 2000,
10 | tcp_ip: '127.0.0.1',
11 | tcp_port: 2000,
12 |
13 | setDaemonAddress: function setDaemonAddress(address) {
14 | if (!process.env.AWS_XRAY_DAEMON_ADDRESS) {
15 | processAddress(address);
16 | logger.getLogger().info('Configured daemon address to ' + address + '.');
17 | } else {
18 | logger.getLogger().warn('Ignoring call to setDaemonAddress as AWS_XRAY_DAEMON_ADDRESS is set. '+
19 | 'The current daemon address will not be changed.');
20 | }
21 | }
22 | };
23 |
24 | var processAddress = function processAddress(address) {
25 | if (address.indexOf(':') === -1) {
26 | throw new Error('Invalid Daemon Address. You must specify an ip and port.');
27 | } else {
28 | var splitAddress = address.split(' ');
29 | if (splitAddress.length === 1) {
30 | // in format of 127.0.0.1:2000
31 | if (address.indexOf('udp') > -1 || address.indexOf('tcp') > -1) {
32 | throw new Error('Invalid Daemon Address. You must specify both tcp and udp addresses.');
33 | }
34 | var addr = address.split(':');
35 | if (!addr[0]) {
36 | throw new Error('Invalid Daemon Address. You must specify an ip.');
37 | }
38 | DaemonConfig.udp_ip = addr[0];
39 | DaemonConfig.tcp_ip = addr[0];
40 | DaemonConfig.udp_port = addr[1];
41 | DaemonConfig.tcp_port = addr[1];
42 | } else if (splitAddress.length === 2) {
43 | // in format of udp:127.0.0.1:2000 tcp:127.0.0.1:2001
44 | var part_1 = splitAddress[0].split(':');
45 | var part_2 = splitAddress[1].split(':');
46 | var addr_map = {};
47 | addr_map[part_1[0]] = part_1;
48 | addr_map[part_2[0]] = part_2;
49 |
50 | DaemonConfig.udp_ip = addr_map['udp'][1];
51 | DaemonConfig.udp_port = parseInt(addr_map['udp'][2]);
52 | DaemonConfig.tcp_ip = addr_map['tcp'][1];
53 | DaemonConfig.tcp_port = parseInt(addr_map['tcp'][2]);
54 |
55 | if (!DaemonConfig.udp_port || !DaemonConfig.tcp_port) {
56 | throw new Error('Invalid Daemon Address. You must specify port number.');
57 | }
58 | }
59 | }
60 | };
61 |
62 | if (process.env.AWS_XRAY_DAEMON_ADDRESS) {
63 | processAddress(process.env.AWS_XRAY_DAEMON_ADDRESS);
64 | }
65 | module.exports = DaemonConfig;
66 |
--------------------------------------------------------------------------------
/packages/core/lib/database/sql_data.d.ts:
--------------------------------------------------------------------------------
1 | declare class SqlData {
2 | database_version?: string;
3 | driver_version?: string;
4 | preparation?: string;
5 | url?: string;
6 | user?: string;
7 |
8 | constructor(databaseVer?: string, driverVer?: string, user?: string, url?: string, queryType?: string);
9 | }
10 |
11 | export = SqlData;
12 |
--------------------------------------------------------------------------------
/packages/core/lib/database/sql_data.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents a SQL database call.
3 | * @constructor
4 | * @param {string} databaseVer - The version on the database (user supplied).
5 | * @param {string} driverVer - The version on the database driver (user supplied).
6 | * @param {string} user - The user associated to the database call.
7 | * @param {string} queryType - The SQL query type.
8 | */
9 |
10 | function SqlData(databaseVer, driverVer, user, url, queryType) {
11 | this.init(databaseVer, driverVer, user, url, queryType);
12 | }
13 |
14 | SqlData.prototype.init = function init(databaseVer, driverVer, user, url, queryType) {
15 | if (databaseVer) {
16 | this.database_version = databaseVer;
17 | }
18 |
19 | if (driverVer) {
20 | this.driver_version = driverVer;
21 | }
22 |
23 | if (queryType) {
24 | this.preparation = queryType;
25 | }
26 |
27 | this.url = url;
28 | this.user = user;
29 | };
30 |
31 | module.exports = SqlData;
32 |
--------------------------------------------------------------------------------
/packages/core/lib/env/sqs_message_helper.js:
--------------------------------------------------------------------------------
1 | class SqsMessageHelper {
2 |
3 | static isSampled(message) {
4 | const {attributes} = message; // extract attributes from message
5 | if (!('AWSTraceHeader' in attributes)) {
6 | return false;
7 | }
8 | return attributes['AWSTraceHeader'].includes('Sampled=1');
9 | }
10 | }
11 |
12 | export default SqsMessageHelper;
13 |
--------------------------------------------------------------------------------
/packages/core/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from './aws-xray';
2 |
--------------------------------------------------------------------------------
/packages/core/lib/index.js:
--------------------------------------------------------------------------------
1 | // Convenience file to require the SDK from the root of the repository
2 | module.exports = require('./aws-xray');
3 |
--------------------------------------------------------------------------------
/packages/core/lib/logger.d.ts:
--------------------------------------------------------------------------------
1 | export interface Logger {
2 | debug(...args: any[]): any;
3 | info(...args: any[]): any;
4 | warn(...args: any[]): any;
5 | error(...args: any[]): any;
6 | }
7 |
8 | export function setLogger(logObj: Logger): void;
9 |
10 | export function getLogger(): Logger;
11 |
--------------------------------------------------------------------------------
/packages/core/lib/logger.js:
--------------------------------------------------------------------------------
1 | var validLogLevels = [ 'debug', 'info', 'warn', 'error', 'silent' ];
2 | var defaultLogLevel = validLogLevels.indexOf('error');
3 | var logLevel = calculateLogLevel(process.env.AWS_XRAY_DEBUG_MODE ? 'debug' : process.env.AWS_XRAY_LOG_LEVEL);
4 |
5 | var logger = {
6 | error: createLoggerForLevel('error'),
7 | info: createLoggerForLevel('info'),
8 | warn: createLoggerForLevel('warn'),
9 | debug: createLoggerForLevel('debug'),
10 | };
11 |
12 | function createLoggerForLevel(level) {
13 | var loggerLevel = validLogLevels.indexOf(level);
14 | var consoleMethod = console[level] || console.log || (() => {});
15 |
16 | if (loggerLevel >= logLevel) {
17 | return (message, meta) => {
18 | if (message || meta) {
19 | consoleMethod(formatLogMessage(level, message, meta));
20 | }
21 | };
22 | } else {
23 | return () => {};
24 | }
25 | }
26 |
27 | function calculateLogLevel(level) {
28 | if (level) {
29 | var normalisedLevel = level.toLowerCase();
30 | var index = validLogLevels.indexOf(normalisedLevel);
31 | return index >= 0 ? index : defaultLogLevel;
32 | }
33 |
34 | // Silently ignore invalid log levels, default to default level
35 | return defaultLogLevel;
36 | }
37 |
38 | function createTimestamp(date) {
39 | var tzo = -date.getTimezoneOffset(), // Negate to make this tzo = local - UTC
40 | dif = tzo >= 0 ? '+' : '-',
41 | pad = function(num) {
42 | var norm = Math.floor(Math.abs(num));
43 | return (norm < 10 ? '0' : '') + norm;
44 | };
45 |
46 | return new Date(date.getTime() + (tzo * 60 * 1000)).toISOString()
47 | .replace(/T/, ' ')
48 | .replace(/Z/, ' ') +
49 | dif + pad(tzo / 60) +
50 | ':' + pad(tzo % 60);
51 | }
52 |
53 | function isLambdaFunction() {
54 | return process.env.LAMBDA_TASK_ROOT !== undefined;
55 | }
56 |
57 | function formatLogMessage(level, message, meta) {
58 | var messageParts = [];
59 |
60 | if (!isLambdaFunction()) {
61 | messageParts.push(createTimestamp(new Date()));
62 | messageParts.push(`[${level.toUpperCase()}]`);
63 | }
64 |
65 | if (message) {
66 | messageParts.push(message);
67 | }
68 |
69 | var logString = messageParts.join(' ');
70 | var metaDataString = formatMetaData(meta);
71 | return [logString, metaDataString].filter(str => str.length > 0).join('\n ');
72 | }
73 |
74 | function formatMetaData(meta) {
75 | if (!meta) {
76 | return '';
77 | }
78 |
79 | return ((typeof(meta) === 'string') ? meta : JSON.stringify(meta));
80 | }
81 |
82 | var logging = {
83 | setLogger: function setLogger(logObj) {
84 | logger = logObj;
85 | },
86 |
87 | getLogger: function getLogger() {
88 | return logger;
89 | }
90 | };
91 |
92 | module.exports = logging;
93 |
--------------------------------------------------------------------------------
/packages/core/lib/middleware/incoming_request_data.d.ts:
--------------------------------------------------------------------------------
1 | import * as http from 'http';
2 |
3 | declare class IncomingRequestData {
4 | request: { [key: string]: any };
5 |
6 | constructor(req: http.IncomingMessage);
7 |
8 | close(res: http.ServerResponse): void;
9 | }
10 |
11 | export = IncomingRequestData;
12 |
--------------------------------------------------------------------------------
/packages/core/lib/middleware/incoming_request_data.js:
--------------------------------------------------------------------------------
1 | var { getHttpResponseData } = require('../segments/segment_utils');
2 |
3 | /**
4 | * Represents an incoming HTTP/HTTPS call.
5 | * @constructor
6 | * @param {http.IncomingMessage|https.IncomingMessage} req - The request object from the HTTP/HTTPS call.
7 | */
8 |
9 | function IncomingRequestData(req) {
10 | this.init(req);
11 | }
12 |
13 | IncomingRequestData.prototype.init = function init(req) {
14 | var forwarded = !!req.headers['x-forwarded-for'];
15 | var url;
16 |
17 | if (req.connection) {
18 | url = ((req.connection.secure || req.connection.encrypted) ? 'https://' : 'http://') +
19 | ((req.headers['host'] || '') + (req.url || ''));
20 | }
21 |
22 | this.request = {
23 | method: req.method || '',
24 | user_agent: req.headers['user-agent'] || '',
25 | client_ip: getClientIp(req) || '',
26 | url: url || '',
27 | };
28 |
29 | if (forwarded) {
30 | this.request.x_forwarded_for = forwarded;
31 | }
32 | };
33 |
34 | var getClientIp = function getClientIp(req) {
35 | var clientIp;
36 |
37 | if (req.headers['x-forwarded-for']) {
38 | clientIp = (req.headers['x-forwarded-for'] || '').split(',')[0];
39 | } else if (req.connection && req.connection.remoteAddress) {
40 | clientIp = req.connection.remoteAddress;
41 | } else if (req.socket && req.socket.remoteAddress) {
42 | clientIp = req.socket.remoteAddress;
43 | } else if (req.connection && req.connection.socket && req.connection.socket.remoteAddress) {
44 | clientIp = req.connection.socket.remoteAddress;
45 | }
46 |
47 | return clientIp;
48 | };
49 |
50 | /**
51 | * Closes the local and automatically captures the response data.
52 | * @param {http.ServerResponse|https.ServerResponse} res - The response object from the HTTP/HTTPS call.
53 | */
54 |
55 | IncomingRequestData.prototype.close = function close(res) {
56 | this.response = getHttpResponseData(res);
57 | };
58 |
59 | module.exports = IncomingRequestData;
60 |
--------------------------------------------------------------------------------
/packages/core/lib/middleware/mw_utils.d.ts:
--------------------------------------------------------------------------------
1 | import * as http from 'http';
2 | import Segment = require('../segments/segment');
3 | import IncomingRequestData = require('./incoming_request_data');
4 |
5 | export const defaultName: string | undefined;
6 |
7 | export const dynamicNaming: boolean;
8 |
9 | export const hostPattern: string | null;
10 |
11 | export function enableDynamicNaming(hostPattern?: string): void;
12 |
13 | export function processHeaders(req?: Partial>): { [key: string]: string };
14 |
15 | export function resolveName(hostHeader?: string): string;
16 |
17 | export function resolveSampling(
18 | amznTraceHeader: { [key: string]: string },
19 | segment: Segment,
20 | res: http.ServerResponse
21 | ): void;
22 |
23 | export function setDefaultName(name: string): void;
24 |
25 | export function disableCentralizedSampling(): void;
26 |
27 | export function middlewareLog(message: string, url: string, segment: Segment): void;
28 |
29 | export function traceRequestResponseCycle(req: http.IncomingMessage, res: http.ServerResponse): Segment;
30 |
31 | export interface BaseRuleConfig {
32 | http_method: string;
33 | url_path: string;
34 | fixed_target: number;
35 | rate: number;
36 | description?: string;
37 | }
38 |
39 | export interface RuleConfigV1 extends BaseRuleConfig {
40 | service_name: string;
41 | }
42 |
43 | export interface RuleConfigV2 extends BaseRuleConfig {
44 | host: string;
45 | }
46 |
47 | export type RuleConfig = RuleConfigV1 | RuleConfigV2;
48 |
49 | export interface DefaultRuleConfig {
50 | fixed_target: number;
51 | rate: number;
52 | }
53 |
54 | export interface RulesConfig {
55 | version: number;
56 | default: DefaultRuleConfig;
57 | rules?: RuleConfig[];
58 | }
59 |
60 | export function setSamplingRules(source: string | RulesConfig): void;
61 |
62 | export {
63 | IncomingRequestData
64 | };
65 |
--------------------------------------------------------------------------------
/packages/core/lib/middleware/sampling/default_sampler.js:
--------------------------------------------------------------------------------
1 | var logger = require('../../logger');
2 | const util = require('util');
3 |
4 | var SegmentUtils = require('../../segments/segment_utils');
5 | /**
6 | * The default sampler used to make sampling decisions when the decisions are absent in the incoming requests.
7 | * The sampler use pollers to poll sampling rules from X-Ray service.
8 | * @module DefaultSampler
9 | */
10 | var DefaultSampler = {
11 | localSampler: require('./local_sampler'),
12 | rulePoller: require('./rule_poller'),
13 | targetPoller: require('./target_poller'),
14 | ruleCache: require('./rule_cache'),
15 | started: false,
16 |
17 | /**
18 | * Makes a sample decision based on the sample request.
19 | * @param {object} sampleRequest - Contains information for rules matching.
20 | * @module DefaultSampler
21 | * @function shouldSample
22 | */
23 | shouldSample: function shouldSample(sampleRequest) {
24 | try {
25 | if (!this.started) {
26 | this.start();
27 | }
28 | if (!sampleRequest.serviceType) {
29 | sampleRequest.serviceType = SegmentUtils.origin;
30 | }
31 | var now = Math.floor(new Date().getTime() / 1000);
32 | var matchedRule = this.ruleCache.getMatchedRule(sampleRequest, now);
33 | if (matchedRule) {
34 | logger.getLogger().debug(util.format('Rule %s is matched.', matchedRule.getName()));
35 | return processMatchedRule(matchedRule, now);
36 | } else {
37 | logger.getLogger().info('No effective centralized sampling rule match. Fallback to local rules.');
38 | return this.localSampler.shouldSample(sampleRequest);
39 | }
40 | } catch (err) {
41 | logger.getLogger().error('Unhandled exception by the SDK during making sampling decisions: ' + err);
42 | }
43 | },
44 |
45 | /**
46 | * Set local rules in case there is a need to fallback.
47 | * @module DefaultSampler
48 | * @function setLocalRules
49 | */
50 | setLocalRules: function setLocalRules(source) {
51 | this.localSampler.setLocalRules(source);
52 | },
53 |
54 | /**
55 | * Start the pollers to poll sampling rules and targets from X-Ray service.
56 | * @module DefaultSampler
57 | * @function start
58 | */
59 | start: function start() {
60 | if (!this.started) {
61 | this.rulePoller.start();
62 | this.targetPoller.start();
63 | this.started = true;
64 | }
65 | }
66 | };
67 |
68 | var processMatchedRule = function processMatchedRule(rule, now) {
69 | // As long as a rule is matched we increment request counter.
70 | rule.incrementRequestCount();
71 | var reservoir = rule.getReservoir();
72 | var sample = true;
73 | // We check if we can borrow or take from reservoir first.
74 | var decision = reservoir.borrowOrTake(now, rule.canBorrow());
75 | if (decision === 'borrow') {
76 | rule.incrementBorrowCount();
77 | } else if (decision === 'take') {
78 | rule.incrementSampledCount();
79 | } else if (Math.random() <= rule.getRate()) {
80 | // Otherwise we compute based on FixedRate of this sampling rule.
81 | rule.incrementSampledCount();
82 | } else {
83 | sample = false;
84 | }
85 |
86 | if (sample) {
87 | return rule.getName();
88 | } else {
89 | return false;
90 | }
91 | };
92 |
93 | module.exports = DefaultSampler;
94 |
--------------------------------------------------------------------------------
/packages/core/lib/middleware/sampling/local_reservoir.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Represents a LocalReservoir object that keeps track of the number of traces per second sampled and
4 | * the fixed rate for a given sampling rule defined locally.
5 | * It also decides if a given trace should be sampled or not based on the state of current second.
6 | * @constructor
7 | * @param {number} fixedTarget - An integer value to specify the maximum number of traces per second to sample.
8 | * @param {number} fallbackRate - A value between 0 and 1 indicating the sampling rate after the maximum traces per second has been hit.
9 | */
10 |
11 | function LocalReservoir (fixedTarget, fallbackRate) {
12 | this.init(fixedTarget, fallbackRate);
13 | }
14 |
15 | LocalReservoir.prototype.init = function init(fixedTarget, fallbackRate) {
16 | this.usedThisSecond = 0;
17 |
18 | if (typeof fixedTarget === 'number' && fixedTarget % 1 === 0 && fixedTarget >= 0) {
19 | this.fixedTarget = fixedTarget;
20 | } else {
21 | throw new Error('Error in sampling file. Rule attribute "fixed_target" must be a non-negative integer.');
22 | }
23 |
24 | if (typeof fallbackRate === 'number' && fallbackRate >= 0 && fallbackRate <= 1) {
25 | this.fallbackRate = fallbackRate;
26 | } else {
27 | throw new Error('Error in sampling file. Rule attribute "rate" must be a number between 0 and 1 inclusive.');
28 | }
29 | };
30 |
31 | LocalReservoir.prototype.isSampled = function isSampled() {
32 | var now = Math.round(new Date().getTime() / 1000);
33 |
34 | if (now !== this.thisSecond) {
35 | this.usedThisSecond = 0;
36 | this.thisSecond = now;
37 | }
38 |
39 | if (this.usedThisSecond >= this.fixedTarget) {
40 | return Math.random() < this.fallbackRate;
41 | }
42 |
43 | this.usedThisSecond++;
44 | return true;
45 | };
46 |
47 | module.exports = LocalReservoir;
48 |
--------------------------------------------------------------------------------
/packages/core/lib/middleware/sampling/reservoir.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents a Reservoir object that keeps track of the number of traces per second sampled and
3 | * the fixed rate for a given sampling rule. This information is fetched from X-Ray serivce.
4 | * It decides if a given trace should be borrowed or sampled or not sampled based on the state of current second.
5 | * @constructor
6 | */
7 | function Reservoir () {
8 | this.init();
9 | }
10 |
11 | Reservoir.prototype.init = function init() {
12 | this.quota = null;
13 | this.TTL = null;
14 | this.takenThisSec = 0;
15 | this.borrowedThisSec = 0;
16 | this.reportInterval = 1;
17 | this.reportElapsed = 0;
18 | };
19 |
20 | Reservoir.prototype.borrowOrTake = function borrowOrTake(now, canBorrow) {
21 | this.adjustThisSec(now);
22 | // Don't borrow if the quota is available and fresh.
23 | if (this.quota >= 0 && this.TTL >= now) {
24 | if (this.takenThisSec >= this.quota) {
25 | return false;
26 | }
27 |
28 | this.takenThisSec++;
29 | return 'take';
30 | }
31 |
32 | // Otherwise try to borrow if the quota is not present or expired.
33 | if (canBorrow) {
34 | if (this.borrowedThisSec >= 1) {
35 | return false;
36 | }
37 |
38 | this.borrowedThisSec++;
39 | return 'borrow';
40 | }
41 | };
42 |
43 | Reservoir.prototype.adjustThisSec = function adjustThisSec(now) {
44 | if (now !== this.thisSec) {
45 | this.takenThisSec = 0;
46 | this.borrowedThisSec = 0;
47 | this.thisSec = now;
48 | }
49 | };
50 |
51 | Reservoir.prototype.loadNewQuota = function loadNewQuota(quota, TTL, interval) {
52 | if (quota) {
53 | this.quota = quota;
54 | }
55 | if (TTL) {
56 | this.TTL = TTL;
57 | }
58 | if (interval) {
59 | this.reportInterval = interval/10;
60 | } // Report interval is always time of 10.
61 | };
62 |
63 | Reservoir.prototype.timeToReport = function timeToReport() {
64 | if (this.reportElapsed + 1 >= this.reportInterval) {
65 | this.reportElapsed = 0;
66 | return true;
67 | } else {
68 | this.reportElapsed += 1;
69 | return false;
70 | }
71 | };
72 |
73 | module.exports = Reservoir;
74 |
--------------------------------------------------------------------------------
/packages/core/lib/middleware/sampling/rule_cache.js:
--------------------------------------------------------------------------------
1 | var TTL = 60 * 60; // The cache expires 1 hour after the last refresh time.
2 |
3 | /**
4 | * The rule cache that stores sampling rules fetched from X-Ray service.
5 | * @module RuleCache
6 | */
7 | var RuleCache = {
8 | rules: [],
9 | lastUpdated: null,
10 |
11 | /**
12 | * Tries to find a valid rule that matches the sample request.
13 | * @param {object} sampleRequest - Contains information for rules matching.
14 | * @param {number} now - Current epoch in seconds.
15 | * @module RuleCache
16 | * @function getMatchedRule
17 | */
18 | getMatchedRule: function getMatchedRule(sampleRequest, now) {
19 | if (isExpired(now)) {
20 | return null;
21 | }
22 | var matchedRule;
23 | this.rules.forEach(function(rule) {
24 | if (!matchedRule && rule.match(sampleRequest)) {
25 | matchedRule = rule;
26 | }
27 | if (rule.isDefault() && !matchedRule) {
28 | matchedRule = rule;
29 | }
30 | });
31 | return matchedRule;
32 | },
33 |
34 | /**
35 | * Load rules fetched from X-Ray service in order sorted by priorities.
36 | * @param {object} rules - Newly fetched rules to load.
37 | * @module RuleCache
38 | * @function loadRules
39 | */
40 | loadRules: function loadRules(rules) {
41 | // Record the old rules for later merging.
42 | var oldRules = {};
43 | this.rules.forEach(function(rule) {
44 | oldRules[rule.getName()] = rule;
45 | });
46 |
47 | // Update the rules in the cache.
48 | this.rules = rules;
49 |
50 | // Transfer state information to refreshed rules.
51 | this.rules.forEach(function(rule) {
52 | var oldRule = oldRules[rule.getName()];
53 | if (oldRule) {
54 | rule.merge(oldRule);
55 | }
56 | });
57 |
58 | // The cache should maintain the order of the rules based on
59 | // priority. If priority is the same we sort name by alphabet
60 | // as rule name is unique.
61 | this.rules.sort(function(a, b) {
62 | var v = a.getPriority() - b.getPriority();
63 | if (v !== 0) {
64 | return v;
65 | }
66 | if (a.getName() > b.getName()) {
67 | return 1;
68 | } else {
69 | return -1;
70 | }
71 | });
72 | },
73 |
74 | /**
75 | * Load targets fetched from X-Ray service.
76 | * @param {object} targetsMapping - Newly fetched targets map with rule name as key.
77 | * @module RuleCache
78 | * @function loadTargets
79 | */
80 | loadTargets: function loadTargets(targetsMapping) {
81 | this.rules.forEach(function(rule) {
82 | var target = targetsMapping[rule.getName()];
83 | if (target) {
84 | rule.getReservoir().loadNewQuota(target.quota, target.TTL, target.interval);
85 | rule.setRate(target.rate);
86 | }
87 | });
88 | },
89 |
90 | getRules: function getRules() {
91 | return this.rules;
92 | },
93 |
94 | timestamp: function timestamp(now) {
95 | this.lastUpdated = now;
96 | },
97 |
98 | getLastUpdated: function getLastUpdated() {
99 | return this.lastUpdated;
100 | }
101 | };
102 |
103 | var isExpired = function isExpired(now) {
104 | // The cache is considered expired if it is never loaded.
105 | if (!RuleCache.getLastUpdated()) {
106 | return true;
107 | }
108 | return now > RuleCache.getLastUpdated() + TTL;
109 | };
110 |
111 | module.exports = RuleCache;
112 |
--------------------------------------------------------------------------------
/packages/core/lib/middleware/sampling/rule_poller.js:
--------------------------------------------------------------------------------
1 | var logger = require('../../logger');
2 | var ServiceConnector = require('./service_connector');
3 | var ruleCache = require('./rule_cache');
4 | var DEFAULT_INTERVAL = 5 * 60 * 1000; // 5 minutes on sampling rules fetch
5 |
6 | /**
7 | * The RulePoller that periodically fetch sampling rules from X-Ray service
8 | * and load them into RuleCache.
9 | * @module RulePoller
10 | */
11 | var RulePoller = {
12 |
13 | start: function start() {
14 | if (this.poller) {
15 | clearInterval(this.poller);
16 | }
17 |
18 | // Refresh sampling rules cache with no jitter upon start.
19 | refresh(false);
20 | this.poller = setInterval(refresh, DEFAULT_INTERVAL);
21 | this.poller.unref();
22 | },
23 | };
24 |
25 | var refresh = function refresh(jitter) {
26 | // Add jitter by default unless explicitly told not to.
27 | jitter = typeof jitter === 'undefined' ? true : jitter;
28 |
29 | if (jitter) {
30 | var delay = getJitter();
31 | setTimeout(refreshWithFirewall, delay);
32 | } else {
33 | refreshWithFirewall();
34 | }
35 | };
36 |
37 | var refreshWithFirewall = function refreshWithFirewall() {
38 | try {
39 | refreshCache();
40 | } catch (e) {
41 | logger.getLogger().warn('Encountered unexpected exception when fetching sampling rules: ' + e);
42 | }
43 | };
44 |
45 | var refreshCache = function refreshCache() {
46 | // Timestamp should be generated *before* the actual outbound call to ensure
47 | // we don't mark the cache as being fresher than it actually is.
48 | var now = Math.floor(new Date().getTime() / 1000);
49 |
50 | // Pass a callback that only runs when the new rules are
51 | // successfully fetched.
52 | ServiceConnector.fetchSamplingRules(function(err, newRules) {
53 | if (err) {
54 | logger.getLogger().warn('Failed to retrieve sampling rules from X-Ray service:', err);
55 | } else if (newRules.length !== 0) {
56 | ruleCache.loadRules(newRules);
57 | ruleCache.timestamp(now);
58 | logger.getLogger().info('Successfully refreshed centralized sampling rule cache.');
59 | }
60 | });
61 | };
62 |
63 | // A random jitter of up to 5 seconds is injected after every run to ensure
64 | // the calls eventually get evenly distributed over the 5 minute window.
65 | var getJitter = function getJitter() {
66 | return Math.random() * 5;
67 | };
68 |
69 | module.exports = RulePoller;
70 |
--------------------------------------------------------------------------------
/packages/core/lib/middleware/sampling/target_poller.js:
--------------------------------------------------------------------------------
1 | var rulePoller = require('./rule_poller');
2 | var serviceConnector = require('./service_connector');
3 | var ruleCache = require('./rule_cache');
4 | var logger = require('../../logger');
5 | var DEFAULT_INTERVAL = 10 * 1000; // 10 seconds on sampling targets fetch
6 |
7 |
8 | /**
9 | * The TargetPoller that periodically fetch sampling targets from X-Ray service
10 | * and load them into RuleCache.
11 | * @module TargetPoller
12 | */
13 | var TargetPoller = {
14 |
15 | interval: DEFAULT_INTERVAL,
16 |
17 | start: function start() {
18 | this.poller = setInterval(refreshWithFirewall, DEFAULT_INTERVAL + getJitter());
19 | this.poller.unref();
20 | },
21 | };
22 |
23 | var refreshWithFirewall = function refreshWithFirewall() {
24 | try {
25 | refresh();
26 | } catch (e) {
27 | logger.getLogger().warn('Encountered unexpected exception when fetching sampling targets: ' + e);
28 | }
29 | };
30 |
31 | var refresh = function refresh() {
32 | var candidates = getCandidates();
33 | if (candidates && candidates.length > 0) {
34 | serviceConnector.fetchTargets(candidates, function(err, targetsMapping, ruleFreshness) {
35 | if (err) {
36 | logger.getLogger().warn('Failed to retrieve sampling targets from X-Ray service:', err);
37 | return;
38 | }
39 |
40 | ruleCache.loadTargets(targetsMapping);
41 | if (ruleFreshness > ruleCache.getLastUpdated()) {
42 | logger.getLogger().info('Performing out-of-band sampling rule polling to fetch updated rules.');
43 | rulePoller.start();
44 | }
45 |
46 | logger.getLogger().info('Successfully reported rule statistics to get new sampling quota.');
47 | });
48 | }
49 | };
50 |
51 | // Don't report a rule statistics if any of the conditions is met:
52 | // 1. The report time hasn't come (some rules might have larger report intervals).
53 | // 2. The rule is never matched.
54 | var getCandidates = function getCandidates() {
55 | var rules = ruleCache.getRules();
56 |
57 | var candidates = [];
58 | rules.forEach(function(rule) {
59 | if (rule.everMatched() && rule.timeToReport()) {
60 | candidates.push(rule);
61 | }
62 | });
63 |
64 | return candidates;
65 | };
66 |
67 | // A random jitter of up to 0.1 seconds is injected after every run to ensure
68 | // the calls eventually get evenly distributed over the 10 second window.
69 | var getJitter = function getJitter() {
70 | return Math.random() / TargetPoller.interval;
71 | };
72 |
73 | module.exports = TargetPoller;
74 |
--------------------------------------------------------------------------------
/packages/core/lib/patchers/aws3_p.d.ts:
--------------------------------------------------------------------------------
1 | import { SegmentLike } from '../aws-xray';
2 | /**
3 | * Instruments AWS SDK V3 clients with X-Ray via middleware.
4 | *
5 | * @param client - AWS SDK V3 client to instrument
6 | * @param manualSegment - Parent segment or subsegment that is passed in for manual mode users
7 | * @returns - the client with the X-Ray instrumentation middleware added to its middleware stack
8 | */
9 | export declare function captureAWSClient(client: T, manualSegment?: SegmentLike): T
10 |
--------------------------------------------------------------------------------
/packages/core/lib/patchers/aws_p.d.ts:
--------------------------------------------------------------------------------
1 | /* The type accepted and returned from patching AWS clients is generic because using types defined
2 | * by the aws-sdk would require us to depend on it, which would make our bundle size too large.
3 | *
4 | * See: https://github.com/aws/aws-xray-sdk-node/pull/255
5 | */
6 |
7 | export function captureAWS(awssdk: T): T;
8 |
9 | export function captureAWSClient(service: T): T;
10 |
--------------------------------------------------------------------------------
/packages/core/lib/patchers/http_p.d.ts:
--------------------------------------------------------------------------------
1 | import * as http from 'http';
2 | import * as https from 'https';
3 | import { Subsegment } from '../aws-xray';
4 |
5 | type httpSubsegmentCallback = (subsegment: Subsegment, req: http.ClientRequest, res: http.IncomingMessage | null, error: Error) => void
6 |
7 | export function captureHTTPs(mod: T, downstreamXRayEnabled?: boolean, subsegmentCallback?: httpSubsegmentCallback): T;
8 |
9 | export function captureHTTPsGlobal(mod: typeof https | typeof http, downstreamXRayEnabled?: boolean, subsegmentCallback?: httpSubsegmentCallback): void;
10 |
--------------------------------------------------------------------------------
/packages/core/lib/patchers/promise_p.d.ts:
--------------------------------------------------------------------------------
1 | export const capturePromise: {
2 | (): void;
3 | patchThirdPartyPromise(Promise: any): void;
4 | };
5 |
--------------------------------------------------------------------------------
/packages/core/lib/patchers/promise_p.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module promise_p
3 | */
4 |
5 | /**
6 | * This module patches native Promise libraries provided by V8 engine
7 | * so all subsegments generated within Promise are attached to the correct parent.
8 | */
9 |
10 | const contextUtils = require('../context_utils');
11 |
12 | const originalThen = Symbol('original then');
13 | const originalCatch = Symbol('original catch');
14 |
15 | function patchPromise(Promise) {
16 | const then = Promise.prototype.then;
17 | if (!then[originalThen]) {
18 | Promise.prototype.then = function(onFulfilled, onRejected) {
19 | if (contextUtils.isAutomaticMode()
20 | && tryGetCurrentSegment()
21 | ) {
22 | const ns = contextUtils.getNamespace();
23 |
24 | onFulfilled = onFulfilled && ns.bind(onFulfilled);
25 | onRejected = onRejected && ns.bind(onRejected);
26 | }
27 |
28 | return then.call(this, onFulfilled, onRejected);
29 | };
30 | Promise.prototype.then[originalThen] = then;
31 | }
32 |
33 | const origCatch = Promise.prototype.catch;
34 | if (origCatch && !origCatch[originalCatch]) {
35 | Promise.prototype.catch = function (onRejected) {
36 | if (contextUtils.isAutomaticMode()
37 | && tryGetCurrentSegment()
38 | ) {
39 | const ns = contextUtils.getNamespace();
40 |
41 | onRejected = onRejected && ns.bind(onRejected);
42 | }
43 |
44 | return origCatch.call(this, onRejected);
45 | };
46 | Promise.prototype.catch[originalCatch] = origCatch;
47 | }
48 | }
49 |
50 | function unpatchPromise(Promise) {
51 | const then = Promise.prototype.then;
52 | if (then[originalThen]) {
53 | Promise.prototype.then = then[originalThen];
54 | }
55 | const origCatch = Promise.prototype.catch;
56 | if (origCatch && origCatch[originalCatch]) {
57 | Promise.prototype.catch = origCatch[originalCatch];
58 | }
59 | }
60 |
61 | function tryGetCurrentSegment() {
62 | try {
63 | return contextUtils.getSegment();
64 | } catch (e) {
65 | return undefined;
66 | }
67 | }
68 |
69 | function capturePromise() {
70 | patchPromise(Promise);
71 | }
72 |
73 | function uncapturePromise() {
74 | unpatchPromise(Promise);
75 | }
76 |
77 | capturePromise.patchThirdPartyPromise = patchPromise;
78 |
79 | module.exports.capturePromise = capturePromise;
80 | module.exports.uncapturePromise = uncapturePromise;
81 |
--------------------------------------------------------------------------------
/packages/core/lib/resources/default_sampling_rules.json:
--------------------------------------------------------------------------------
1 | {
2 | "default": {
3 | "fixed_target": 1,
4 | "rate": 0.05
5 | },
6 | "version": 2
7 | }
8 |
--------------------------------------------------------------------------------
/packages/core/lib/segment_emitter.d.ts:
--------------------------------------------------------------------------------
1 | import Segment = require('./segments/segment');
2 |
3 | export function format(segment: Segment): string;
4 |
5 | export function send(segment: Segment): void;
6 |
7 | export function setDaemonAddress(address: string): void;
8 |
9 | export function getIp(): string;
10 |
11 | export function getPort(): number;
12 |
13 | export function disableReusableSocket(): void;
14 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/attributes/aws.d.ts:
--------------------------------------------------------------------------------
1 | declare class Aws {
2 | constructor(res: any, serviceName: string);
3 |
4 | addData(data: any): void;
5 | }
6 |
7 | declare namespace Aws {
8 | function setAWSWhitelist(source: string | object): void;
9 |
10 | function appendAWSWhitelist(source: string | object): void;
11 | }
12 |
13 | export = Aws;
14 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/attributes/aws.js:
--------------------------------------------------------------------------------
1 | var CallCapturer = require('../../patchers/call_capturer.js');
2 |
3 | var capturer = new CallCapturer();
4 |
5 | /**
6 | * Represents a AWS client call. Automatically captures data from the supplied response object,
7 | * Data captured depends on the whitelisting file supplied.
8 | * The base whitelisting file can be found at /lib/resources/aws_whitelist.json.
9 | * @constructor
10 | * @param {any} res - The response object from the AWS call. Typed as any to avoid AWS SDK dependency. Otherwise would be AWS.Response.
11 | * @param {string} serviceName - The service name of the AWS client.
12 | * @see https://github.com/aws/aws-sdk-js/blob/master/lib/response.js
13 | */
14 |
15 | function Aws(res, serviceName) {
16 | this.init(res, serviceName);
17 | }
18 |
19 | Aws.prototype.init = function init(res, serviceName) {
20 | //TODO: account ID
21 | this.operation = formatOperation(res.request.operation) || '';
22 | if (res && res.request && res.request.httpRequest && res.request.httpRequest.region) {
23 | this.region = res.request.httpRequest.region;
24 | }
25 | if (res && res.requestId) {
26 | this.request_id = res.requestId;
27 | }
28 | this.retries = res.retryCount || 0;
29 |
30 | if (res.extendedRequestId && serviceName && serviceName.toLowerCase() === 's3') {
31 | this.id_2 = res.extendedRequestId;
32 | }
33 |
34 | if (serviceName) {
35 | this.addData(capturer.capture(serviceName.toLowerCase(), res));
36 | }
37 | };
38 |
39 | Aws.prototype.addData = function addData(data) {
40 | for (var attribute in data) {
41 | this[attribute] = data[attribute];
42 | }
43 | };
44 |
45 | /**
46 | * Overrides the default whitelisting file to specify what params to capture on each AWS Service call.
47 | * @param {string|Object} source - The path to the custom whitelist file, or a whitelist source JSON object.
48 | * @exports setAWSWhitelist
49 | */
50 |
51 | var setAWSWhitelist = function setAWSWhitelist(source) {
52 | if (!source || source instanceof String || !(typeof source === 'string' || (source instanceof Object))) {
53 | throw new Error('Please specify a path to the local whitelist file, or supply a whitelist source object.');
54 | }
55 |
56 | capturer = new CallCapturer(source);
57 | };
58 |
59 | /**
60 | * Appends to the default whitelisting file to specify what params to capture on each AWS Service call.
61 | * @param {string|Object} source - The path to the custom whitelist file, or a whitelist source JSON object.
62 | * @exports appendAWSWhitelist
63 | */
64 |
65 | var appendAWSWhitelist = function appendAWSWhitelist(source) {
66 | if (!source || source instanceof String || !(typeof source === 'string' || (source instanceof Object))) {
67 | throw new Error('Please specify a path to the local whitelist file, or supply a whitelist source object.');
68 | }
69 |
70 | capturer.append(source);
71 | };
72 |
73 | function formatOperation(operation) {
74 | if (!operation) {
75 | return;
76 | }
77 |
78 | return operation.charAt(0).toUpperCase() + operation.slice(1);
79 | }
80 |
81 | module.exports = Aws;
82 | module.exports.appendAWSWhitelist = appendAWSWhitelist;
83 | module.exports.setAWSWhitelist = setAWSWhitelist;
84 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/attributes/captured_exception.js:
--------------------------------------------------------------------------------
1 | const crypto = require('crypto');
2 |
3 | /**
4 | * Represents a captured exception.
5 | * @constructor
6 | * @param {Exception} err - The exception to capture.
7 | * @param {boolean} [remote] - Flag for whether the error was remote.
8 | */
9 |
10 | function CapturedException(err, remote) {
11 | this.init(err, remote);
12 | }
13 |
14 | CapturedException.prototype.init = function init(err, remote) {
15 | var e = (typeof err === 'string' || err instanceof String) ? { message: err, name: '' } : err;
16 |
17 | this.message = e.message;
18 | this.type = e.name;
19 | this.stack = [];
20 | this.remote = !!remote;
21 | this.id = crypto.randomBytes(8).toString('hex');
22 |
23 | if (e.stack) {
24 | var stack = e.stack.split('\n');
25 | stack.shift();
26 |
27 | stack.forEach((stackline) => {
28 | var line = stackline.trim().replace(/\(|\)/g, '');
29 | line = line.substring(line.indexOf(' ') + 1);
30 |
31 | var label = line.lastIndexOf(' ') >= 0 ? line.slice(0, line.lastIndexOf(' ')) : null;
32 | var path = Array.isArray(label) && !label.length ? line : line.slice(line.lastIndexOf(' ') + 1);
33 | path = path.split(':');
34 |
35 | var entry = {
36 | path: path[0],
37 | line: parseInt(path[1]),
38 | label: label || 'anonymous'
39 | };
40 |
41 | this.stack.push(entry);
42 | }, this);
43 | }
44 | };
45 |
46 | module.exports = CapturedException;
47 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/attributes/remote_request_data.js:
--------------------------------------------------------------------------------
1 | const { getHttpResponseData } = require('../segment_utils');
2 | var { stripQueryStringFromPath } = require('../../utils');
3 |
4 | /**
5 | * Represents an outgoing HTTP/HTTPS call.
6 | * @constructor
7 | * @param {http.ClientRequest|https.ClientRequest} req - The request object from the HTTP/HTTPS call.
8 | * @param {http.IncomingMessage|https.IncomingMessage} res - The response object from the HTTP/HTTPS call.
9 | * @param {boolean} downstreamXRayEnabled - when true, adds a "traced": true hint to generated subsegments such that the AWS X-Ray service expects a corresponding segment from the downstream service.
10 | */
11 |
12 | function RemoteRequestData(req, res, downstreamXRayEnabled) {
13 | this.init(req, res, downstreamXRayEnabled);
14 | }
15 |
16 | RemoteRequestData.prototype.init = function init(req, res, downstreamXRayEnabled) {
17 | this.request = {
18 | url: (req.agent && req.agent.protocol) ? (req.agent.protocol + '//' + (req.host || req.getHeader('host')) + stripQueryStringFromPath(req.path)) : '',
19 | method: req.method || '',
20 | };
21 |
22 | if (downstreamXRayEnabled) {
23 | this.request.traced = true;
24 | }
25 |
26 | if (res) {
27 | this.response = getHttpResponseData(res);
28 | }
29 | };
30 |
31 | module.exports = RemoteRequestData;
32 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/attributes/subsegment.d.ts:
--------------------------------------------------------------------------------
1 | import * as http from 'http';
2 | import { Segment, SegmentLike } from '../../aws-xray';
3 |
4 | declare class Subsegment {
5 | id: string;
6 | name: string;
7 | start_time: number;
8 | in_progress?: boolean;
9 | subsegments?: Array;
10 | parent: SegmentLike;
11 | segment: Segment;
12 | namespace?: string;
13 | notTraced: boolean;
14 |
15 | constructor(name: string);
16 |
17 | addNewSubsegment(name: string): Subsegment;
18 |
19 | addSubsegment(subsegment: Subsegment): void;
20 |
21 | addNewSubsegmentWithoutSampling(name: String): Subsegment;
22 |
23 | addSubsegmentWithoutSampling(subsegment: Subsegment): void;
24 |
25 | removeSubsegment(subsegment: Subsegment): void;
26 |
27 | addAttribute(name: string, data: any): void;
28 |
29 | addPrecursorId(id: string): void;
30 |
31 | addAnnotation(key: string, value: boolean | string | number): void;
32 |
33 | addMetadata(key: string, value: any, namespace?: string): void;
34 |
35 | addSqlData(sqlData: any): void;
36 |
37 | addError(err: Error | string, remote?: boolean): void;
38 |
39 | addRemoteRequestData(req: http.ClientRequest, res: http.IncomingMessage, downstreamXRayEnabled?: boolean): void;
40 |
41 | addFaultFlag(): void;
42 |
43 | addErrorFlag(): void;
44 |
45 | addThrottleFlag(): void;
46 |
47 | close(err?: Error | string | null, remote?: boolean): void;
48 |
49 | incrementCounter(additional?: number): void;
50 |
51 | decrementCounter(): void;
52 |
53 | isClosed(): boolean;
54 |
55 | flush(): void;
56 |
57 | streamSubsegments(): true | undefined;
58 |
59 | format(): string;
60 |
61 | toString(): string;
62 |
63 | toJSON(): { [key: string]: any };
64 |
65 | serialize(subsegment?: Subsegment): string;
66 | }
67 |
68 | export = Subsegment;
69 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/attributes/trace_id.d.ts:
--------------------------------------------------------------------------------
1 | declare class TraceID {
2 | version: number;
3 | timestamp: string;
4 | id: string;
5 |
6 | constructor(tsHex?: string, numberhex?: string);
7 |
8 | static Invalid(): TraceID;
9 |
10 | static FromString(rawId: string): TraceID;
11 |
12 | toString(): string;
13 | }
14 |
15 | export = TraceID;
16 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/attributes/trace_id.js:
--------------------------------------------------------------------------------
1 | var crypto = require('crypto');
2 | var logger = require('../../logger');
3 |
4 | /**
5 | * Class describing an AWS X-Ray trace ID.
6 | * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-traces
7 | */
8 | class TraceID {
9 | /**
10 | * Constructs a new trace ID using the current time.
11 | * @param {string} [tsHex] - time stamp to use for trace ID in hexadecimal format
12 | * @param {string} [numberhex] - string of hexadecimal characters for random portion of Trace ID
13 | * @constructor
14 | */
15 | constructor(tsHex, numberhex) {
16 | this.version = 1;
17 | this.timestamp = tsHex || Math.round(new Date().getTime() / 1000).toString(16);
18 | this.id = numberhex || crypto.randomBytes(12).toString('hex');
19 | }
20 |
21 | /**
22 | * @returns {TraceID} - a hardcoded trace ID using zeroed timestamp and random ID
23 | */
24 | static Invalid() {
25 | return new TraceID('00000000', '000000000000000000000000');
26 | }
27 |
28 | /**
29 | * Constructs a new trace ID from provided string. If no string is provided or the provided string is invalid,
30 | * log an error but a new trace ID still returned. This can be used as a trace ID string validator.
31 | * @param {string} [rawID] - string to create a Trace ID object from.
32 | */
33 | static FromString(rawID) {
34 | const DELIMITER = '-';
35 | var traceID = new TraceID();
36 | var version, timestamp;
37 |
38 | if (!rawID || typeof rawID !== 'string') {
39 | logger.getLogger().error('Empty or non-string trace ID provided');
40 | return traceID;
41 | }
42 |
43 | const parts = rawID.trim().split(DELIMITER);
44 | if (parts.length !== 3) {
45 | logger.getLogger().error('Unrecognized trace ID format');
46 | return traceID;
47 | }
48 |
49 | version = parseInt(parts[0]);
50 | if (isNaN(version) || version < 1) {
51 | logger.getLogger().error('Trace ID version must be positive integer');
52 | return traceID;
53 | }
54 |
55 | timestamp = parseInt(parts[1], 16).toString(16);
56 | if (timestamp === 'NaN') {
57 | logger.getLogger().error('Trace ID timestamp must be a hex-encoded value');
58 | return traceID;
59 | } else {
60 | timestamp = timestamp.padStart(8, '0');
61 | }
62 |
63 | traceID.version = version;
64 | traceID.timestamp = timestamp;
65 | traceID.id = parts[2];
66 |
67 | return traceID;
68 | }
69 |
70 | /**
71 | * Returns a string representation of the trace ID.
72 | * @returns {string} - stringified trace ID, e.g. 1-57fbe041-2c7ad569f5d6ff149137be86
73 | */
74 | toString() {
75 | return `${this.version.toString()}-${this.timestamp}-${this.id}`;
76 | }
77 | }
78 |
79 | module.exports = TraceID;
80 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/plugins/ec2_plugin.d.ts:
--------------------------------------------------------------------------------
1 | export interface EC2Metadata {
2 | ec2: {
3 | instance_id: string;
4 | availability_zone: string;
5 | };
6 | }
7 |
8 | export function getData(callback: (metadata?: EC2Metadata) => void): void;
9 |
10 | export const originName: string;
11 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/plugins/ec2_plugin.js:
--------------------------------------------------------------------------------
1 | var Plugin = require('./plugin');
2 | var logger = require('../../logger');
3 | var http = require('http');
4 |
5 | var EC2Plugin = {
6 | /**
7 | * A function to get the instance data from the EC2 metadata service.
8 | * @param {function} callback - The callback for the plugin loader.
9 | */
10 | getData: function(callback) {
11 | const METADATA_PATH = '/latest/dynamic/instance-identity/document';
12 |
13 | function populateMetadata(token) {
14 | const options = getOptions(
15 | METADATA_PATH,
16 | 'GET',
17 | token ? { 'X-aws-ec2-metadata-token': token } : {}
18 | );
19 |
20 | Plugin.getPluginMetadata(options, function(err, data) {
21 | if (err || !data) {
22 | logger.getLogger().error('Error loading EC2 plugin metadata: ', err ? err.toString() : 'Could not retrieve data from IMDS.');
23 | callback();
24 | return;
25 | }
26 |
27 | const metadata = {
28 | ec2: {
29 | instance_id: data.instanceId,
30 | availability_zone: data.availabilityZone,
31 | instance_size: data.instanceType,
32 | ami_id: data.imageId
33 | }
34 | };
35 | callback(metadata);
36 | });
37 | }
38 |
39 | /**
40 | * This kicks off a requet to get a token used for requests to IMDSv2. If the request for the token
41 | * fails, we fall back to IMDSv1. Otherwise, the token will be used for an IMDSv2 request.
42 | */
43 | getToken(function(token) {
44 | if (token === null) {
45 | logger.getLogger().debug('EC2Plugin failed to get token from IMDSv2. Falling back to IMDSv1.');
46 | }
47 |
48 | populateMetadata(token);
49 | });
50 | },
51 | originName: 'AWS::EC2::Instance'
52 | };
53 |
54 | /**
55 | * Asynchronously retrieves a token used in requests to EC2 instance metadata service.
56 | * @param {function} callback - callback to plugin
57 | */
58 | function getToken(callback) {
59 | const httpReq = http.__request ? http.__request : http.request;
60 | const TTL = 60; //seconds
61 | const TOKEN_PATH = '/latest/api/token';
62 | const options = getOptions(TOKEN_PATH, 'PUT', {
63 | 'X-aws-ec2-metadata-token-ttl-seconds': TTL
64 | });
65 |
66 | let req = httpReq(options, function(res) {
67 | let body = '';
68 |
69 | res.on('data', function(chunk) {
70 | body += chunk;
71 | });
72 |
73 | res.on('end', function() {
74 | if (this.statusCode === 200 || this.statusCode === 300) {
75 | callback(body);
76 | } else {
77 | callback(null);
78 | }
79 | });
80 | });
81 |
82 | req.on('error', function() {
83 | callback(null);
84 | });
85 |
86 | req.on('timeout', function() {
87 | req.abort();
88 | callback(null);
89 | });
90 |
91 | req.setTimeout(Plugin.METADATA_TIMEOUT);
92 | req.end();
93 | }
94 |
95 | function getOptions(path, method, headers) {
96 | if (!method) {
97 | method = 'GET';
98 | }
99 |
100 | if (!headers) {
101 | headers = {};
102 | }
103 |
104 | return {
105 | host: '169.254.169.254',
106 | path: path,
107 | method: method,
108 | headers: headers
109 | };
110 | }
111 |
112 | module.exports = EC2Plugin;
113 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/plugins/ecs_plugin.d.ts:
--------------------------------------------------------------------------------
1 | export interface ECSMetadata {
2 | ecs: {
3 | container: string;
4 | };
5 | }
6 |
7 | export function getData(callback: (metadata?: ECSMetadata) => void): void;
8 |
9 | export const originName: string;
10 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/plugins/ecs_plugin.js:
--------------------------------------------------------------------------------
1 | var os = require('os');
2 |
3 | var ECSPlugin = {
4 | /**
5 | * A function to get the instance data from the ECS instance.
6 | * @param {function} callback - The callback for the plugin loader.
7 | */
8 | getData: function(callback) {
9 | callback({ ecs: { container: os.hostname() }});
10 | },
11 | originName: 'AWS::ECS::Container'
12 | };
13 |
14 | module.exports = ECSPlugin;
15 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/plugins/elastic_beanstalk_plugin.d.ts:
--------------------------------------------------------------------------------
1 | export interface ElasticBeanstalkMetadata {
2 | elastic_beanstalk: {
3 | environment: string;
4 | version_label: string;
5 | deployment_id: number;
6 | };
7 | }
8 |
9 | export function getData(callback: (metadata?: ElasticBeanstalkMetadata) => void): void;
10 |
11 | export const originName: string;
12 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/plugins/elastic_beanstalk_plugin.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | var logger = require('../../logger');
4 |
5 | var ENV_CONFIG_LOCATION = '/var/elasticbeanstalk/xray/environment.conf';
6 |
7 | var ElasticBeanstalkPlugin = {
8 | /**
9 | * A function to get data from the Elastic Beanstalk environment configuration file.
10 | * @param {function} callback - The callback for the plugin loader.
11 | */
12 | getData: function(callback) {
13 | fs.readFile(ENV_CONFIG_LOCATION, 'utf8', function(err, rawData) {
14 | if (err) {
15 | logger.getLogger().error('Error loading Elastic Beanstalk plugin:', err.stack);
16 | callback();
17 | } else {
18 | var data = JSON.parse(rawData);
19 |
20 | var metadata = {
21 | elastic_beanstalk: {
22 | environment: data.environment_name,
23 | version_label: data.version_label,
24 | deployment_id: data.deployment_id
25 | }
26 | };
27 |
28 | callback(metadata);
29 | }
30 | });
31 | },
32 | originName: 'AWS::ElasticBeanstalk::Environment'
33 | };
34 |
35 | module.exports = ElasticBeanstalkPlugin;
36 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/plugins/plugin.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 |
3 | var Plugin = {
4 | METADATA_TIMEOUT: 1000, // Millis
5 |
6 | /**
7 | * Asynchronously retrieves metadata from on-instance endpoint with an HTTP request using retries for
8 | * requests that time out.
9 | * @param {object} options - The HTTP options to make the request with
10 | * @param {function} callback - callback to plugin
11 | */
12 | getPluginMetadata: function(options, callback) {
13 | const METADATA_RETRY_TIMEOUT = 250; // Millis
14 | const METADATA_RETRIES = 5;
15 |
16 | var retries = METADATA_RETRIES;
17 |
18 | var getMetadata = function() {
19 | var httpReq = http.__request ? http.__request : http.request;
20 |
21 | var req = httpReq(options, function(res) {
22 | var body = '';
23 |
24 | res.on('data', function(chunk) {
25 | body += chunk;
26 | });
27 |
28 | res.on('end', function() {
29 | if (this.statusCode === 200 || this.statusCode === 300) {
30 | try {
31 | body = JSON.parse(body);
32 | } catch (e) {
33 | callback(e);
34 | return;
35 | }
36 |
37 | callback(null, body);
38 | } else if (retries > 0 && Math.floor(this.statusCode / 100) === 5) {
39 | retries--;
40 | setTimeout(getMetadata, METADATA_RETRY_TIMEOUT);
41 | } else {
42 | callback(new Error(`Failed to retrieve metadata with options: ${options}`));
43 | }
44 | });
45 | });
46 |
47 | req.on('error', function(err) {
48 | callback(err);
49 | });
50 |
51 | req.on('timeout', function() {
52 | req.abort();
53 | });
54 |
55 | req.setTimeout(Plugin.METADATA_TIMEOUT);
56 | req.end();
57 | };
58 |
59 | getMetadata();
60 | }
61 | };
62 |
63 | module.exports = Plugin;
64 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/segment.d.ts:
--------------------------------------------------------------------------------
1 | import Subsegment = require('./attributes/subsegment');
2 | import IncomingRequestData = require('../middleware/incoming_request_data');
3 |
4 | declare class Segment {
5 | id: string;
6 | name: string;
7 | start_time: number;
8 | end_time?: number;
9 | in_progress?: boolean;
10 | trace_id: string;
11 | parent_id?: string;
12 | origin?: string;
13 | subsegments?: Array;
14 | notTraced?: boolean;
15 |
16 | additionalTraceData?: object
17 |
18 | constructor(name: string, rootId?: string | null, parentId?: string | null);
19 |
20 | addIncomingRequestData(data: IncomingRequestData): void;
21 |
22 | addAnnotation(key: string, value: boolean | string | number): void;
23 |
24 | setUser(user: string): void;
25 |
26 | addMetadata(key: string, value: any, namespace?: string): void;
27 |
28 | setSDKData(data: object): void;
29 |
30 | setMatchedSamplingRule(ruleName: string): void;
31 |
32 | setServiceData(data: any): void;
33 |
34 | addPluginData(data: object): void;
35 |
36 | addNewSubsegment(name: string): Subsegment;
37 |
38 | addSubsegment(subsegment: Subsegment): void;
39 |
40 | addSubsegmentWithoutSampling(subsegment: Subsegment): void;
41 |
42 | addNewSubsegmentWithoutSampling(name: string): Subsegment
43 |
44 | removeSubsegment(subsegment: Subsegment): void;
45 |
46 | addError(err: Error | string, remote?: boolean): void;
47 |
48 | addFaultFlag(): void;
49 |
50 | addErrorFlag(): void;
51 |
52 | addThrottleFlag(): void;
53 |
54 | isClosed(): boolean;
55 |
56 | incrementCounter(additional?: number): void;
57 |
58 | decrementCounter(): void;
59 |
60 | close(err?: Error | string | null, remote?: boolean): void;
61 |
62 | flush(): void;
63 |
64 | format(): string;
65 |
66 | toString(): string;
67 |
68 | serialize(segment?: Segment): string;
69 | }
70 |
71 | export = Segment;
72 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/segment_utils.d.ts:
--------------------------------------------------------------------------------
1 | import * as http from 'http';
2 |
3 | export const streamingThreshold: number;
4 |
5 | export function getCurrentTime(): number;
6 |
7 | export function setOrigin(origin: string): void;
8 |
9 | export function setPluginData(pluginData: object): void;
10 |
11 | export function setSDKData(sdkData: object): void;
12 |
13 | export function setServiceData(serviceData: any): void;
14 |
15 | export function setStreamingThreshold(threshold: number): void;
16 |
17 | export function getStreamingThreshold(): number;
18 |
19 | export function getHttpResponseData(res: http.ServerResponse): object;
20 |
21 | export function getJsonStringifyReplacer(): (key: string, value: any) => any;
22 |
--------------------------------------------------------------------------------
/packages/core/lib/segments/segment_utils.js:
--------------------------------------------------------------------------------
1 | const { safeParseInt } = require('../utils');
2 | var logger = require('../logger');
3 |
4 | var DEFAULT_STREAMING_THRESHOLD = 100;
5 |
6 | var utils = {
7 | streamingThreshold: DEFAULT_STREAMING_THRESHOLD,
8 |
9 | getCurrentTime: function getCurrentTime() {
10 | return Date.now() / 1000;
11 | },
12 |
13 | setOrigin: function setOrigin(origin) {
14 | this.origin = origin;
15 | },
16 |
17 | setPluginData: function setPluginData(pluginData) {
18 | this.pluginData = pluginData;
19 | },
20 |
21 | setSDKData: function setSDKData(sdkData) {
22 | this.sdkData = sdkData;
23 | },
24 |
25 | setServiceData: function setServiceData(serviceData) {
26 | this.serviceData = serviceData;
27 | },
28 |
29 | /**
30 | * Overrides the default streaming threshold (100).
31 | * The threshold represents the maximum number of subsegments on a single segment before
32 | * the SDK beings to send the completed subsegments out of band of the main segment.
33 | * Reduce this threshold if you see the 'Segment too large to send' error.
34 | * @param {number} threshold - The new threshold to use.
35 | * @memberof AWSXRay
36 | */
37 |
38 | setStreamingThreshold: function setStreamingThreshold(threshold) {
39 | if (isFinite(threshold) && threshold >= 0) {
40 | utils.streamingThreshold = threshold;
41 | logger.getLogger().debug('Subsegment streaming threshold set to: ' + threshold);
42 | } else {
43 | logger.getLogger().error('Invalid threshold: ' + threshold + '. Must be a whole number >= 0.');
44 | }
45 | },
46 |
47 | getStreamingThreshold: function getStreamingThreshold() {
48 | return utils.streamingThreshold;
49 | },
50 |
51 | /**
52 | * Parses an HTTP response object to return an X-Ray compliant HTTP response object.
53 | * @param {http.ServerResponse} res
54 | * @returns {Object} - X-Ray response object to be added to (sub)segment
55 | */
56 | getHttpResponseData: (res) => {
57 | const ret = {};
58 | if (!res) {
59 | return ret;
60 | }
61 |
62 | const status = safeParseInt(res.statusCode);
63 | if (status !== 0) {
64 | ret.status = status;
65 | }
66 | if (res.headers && res.headers['content-length']) {
67 | ret.content_length = safeParseInt(res.headers['content-length']);
68 | }
69 | return ret;
70 | },
71 |
72 | getJsonStringifyReplacer: () => (_, value) => {
73 | if (typeof value === 'bigint') {
74 | return value.toString();
75 | }
76 |
77 | return value;
78 | }
79 | };
80 |
81 | module.exports = utils;
82 |
--------------------------------------------------------------------------------
/packages/core/lib/utils.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-vars */
2 | import Segment = require('./segments/segment');
3 |
4 | export function getCauseTypeFromHttpStatus(status: number | string): 'error' | 'fault' | undefined;
5 |
6 | export function stripQueryStringFromPath(path: string): string;
7 |
8 | export function wildcardMatch(pattern: string, text: string): boolean;
9 |
10 | export namespace LambdaUtils {
11 | function validTraceData(xAmznTraceId?: string): boolean;
12 |
13 | function populateTraceData(segment: Segment, xAmznTraceId: string): boolean;
14 | }
15 |
16 | export function processTraceData(traceData?: string): { [key: string]: string };
17 |
18 | export function objectWithoutProperties(
19 | obj: T,
20 | keys: K[],
21 | preservePrototype?: boolean
22 | ): Omit;
23 |
24 | export function safeParseInt(val: number | string): number;
25 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-core",
3 | "version": "3.10.3",
4 | "description": "AWS X-Ray SDK for Javascript",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Sandra McMullen ",
8 | "William Armiros ",
9 | "Moritz Onken "
10 | ],
11 | "files": [
12 | "dist/lib/**/*",
13 | "LICENSE",
14 | "README.md"
15 | ],
16 | "main": "dist/lib/index.js",
17 | "types": "dist/lib/index.d.ts",
18 | "engines": {
19 | "node": ">= 14.x"
20 | },
21 | "directories": {
22 | "test": "test"
23 | },
24 | "//": "@types/cls-hooked is exposed in API so must be in dependencies, not devDependencies",
25 | "dependencies": {
26 | "@aws-sdk/types": "^3.4.1",
27 | "@smithy/service-error-classification": "^2.0.4",
28 | "@types/cls-hooked": "^4.3.3",
29 | "atomic-batcher": "^1.0.2",
30 | "cls-hooked": "^4.2.2",
31 | "semver": "^7.5.3"
32 | },
33 | "scripts": {
34 | "prepare": "npm run compile",
35 | "compile": "tsc && npm run copy-lib && npm run copy-test",
36 | "copy-lib": "find lib -type f \\( -name '*.d.ts' -o -name '*.json' \\) | xargs -I % ../../scripts/cp-with-structure.sh % dist",
37 | "copy-test": "find test -name '*.json' | xargs -I % ../../scripts/cp-with-structure.sh % dist",
38 | "lint": "eslint .",
39 | "lint:fix": "eslint . --fix",
40 | "test": "npm run compile && mocha --recursive ./dist/test/ -R spec && tsd && mocha --recursive ./dist/test_async/ -R spec",
41 | "test-d": "tsd",
42 | "test-async": "npm run compile && mocha --recursive ./dist/test_async/ -R spec",
43 | "clean": "rm -rf dist && rm -rf node_modules",
44 | "testcov": "nyc npm run test",
45 | "reportcov": "nyc report --reporter=text-lcov > coverage.lcov"
46 | },
47 | "keywords": [
48 | "amazon",
49 | "api",
50 | "aws",
51 | "core",
52 | "xray",
53 | "x-ray",
54 | "x ray"
55 | ],
56 | "license": "Apache-2.0",
57 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/packages/core"
58 | }
--------------------------------------------------------------------------------
/packages/core/test/integration/segment_maintained_across_shared_promise.test.js:
--------------------------------------------------------------------------------
1 | if (!global.Promise) {
2 | process.exit(0);
3 | }
4 |
5 | var assert = require('chai').assert;
6 | var http = require('http');
7 |
8 | var AWSXRay = require('../../lib');
9 | var Segment = AWSXRay.Segment;
10 |
11 | AWSXRay.capturePromise();
12 | AWSXRay.enableAutomaticMode();
13 |
14 | var sharedPromise = null;
15 |
16 | var server = http
17 | .createServer(function(req, res) {
18 | var ns = AWSXRay.getNamespace();
19 | ns.bindEmitter(req);
20 | ns.bindEmitter(res);
21 |
22 | ns.run(function () {
23 | var segment = new Segment('foo');
24 |
25 | AWSXRay.setSegment(segment);
26 |
27 | if (!sharedPromise) {
28 | sharedPromise = Promise.resolve();
29 | }
30 |
31 | sharedPromise.then(function() {
32 | var retrievedSegment = AWSXRay.getSegment();
33 | res.end();
34 |
35 | // setTimeout so the assertion isn't caught by the promise
36 | setTimeout(function() {
37 | assert.equal(segment.id, retrievedSegment.id);
38 | // Cancel the patch because it doesn't affect other tests
39 | require('../../lib/patchers/promise_p').uncapturePromise();
40 | });
41 | });
42 | });
43 | }).listen(8080, '0.0.0.0', function() {
44 | var address = server.address();
45 |
46 | var count = 0;
47 | function cb(err) {
48 | if (err) {
49 | throw err;
50 | }
51 |
52 | if (++count === 2) {
53 | server.close();
54 | }
55 | }
56 | sendRequest(address, cb);
57 | sendRequest(address, cb);
58 | });
59 |
60 | function sendRequest(address, cb) {
61 | http
62 | .request({
63 | hostname: address.address,
64 | port: address.port,
65 | path: '/'
66 | })
67 | .on('response', function(res) {
68 | res.on('end', cb).resume();
69 | })
70 | .on('error', cb)
71 | .end();
72 | }
73 |
--------------------------------------------------------------------------------
/packages/core/test/resources/custom_sampling.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": [
3 | {
4 | "description": "Root",
5 | "http_method": "GET",
6 | "service_name": "localhost:*",
7 | "url_path": "/",
8 | "fixed_target": 0,
9 | "rate": 0
10 | },
11 | {
12 | "http_method": "GET",
13 | "service_name": "*",
14 | "url_path": "/getSQS",
15 | "fixed_target": 10,
16 | "rate": 0.05
17 | },
18 | {
19 | "http_method": "GET",
20 | "service_name": "*.foo.com",
21 | "url_path": "/signin/*",
22 | "fixed_target": 10,
23 | "rate": 0.05
24 | }
25 | ],
26 | "default": {
27 | "fixed_target": 10,
28 | "rate": 0.05
29 | },
30 | "version": 1
31 | }
--------------------------------------------------------------------------------
/packages/core/test/resources/custom_whitelist.json:
--------------------------------------------------------------------------------
1 | {
2 | "services": {
3 | "s3": {
4 | "operations": {
5 | "getObject": {
6 | "request_parameters": [
7 | "Bucket",
8 | "Key"
9 | ]
10 | }
11 | }
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/packages/core/test/unit/aws-xray.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('chai').assert;
2 | var chai = require('chai');
3 | var sinon = require('sinon');
4 | var sinonChai = require('sinon-chai');
5 |
6 | var segmentUtils = require('../../lib/segments/segment_utils');
7 |
8 | chai.should();
9 | chai.use(sinonChai);
10 |
11 | describe('AWSXRay', function() {
12 | var AWSXRay;
13 |
14 | describe('on load', function() {
15 | var sandbox, setSDKDataStub, setServiceDataStub;
16 |
17 | beforeEach(function() {
18 | sandbox = sinon.createSandbox();
19 |
20 | setSDKDataStub = sandbox.stub(segmentUtils, 'setSDKData');
21 | setServiceDataStub = sandbox.stub(segmentUtils, 'setServiceData');
22 | });
23 |
24 | afterEach(function() {
25 | sandbox.restore();
26 | });
27 |
28 | it('should set the segmentUtils version and SDK version', function() {
29 | // This test requires both index.js and aws-xray.js are first time required.
30 | // We should always clear the require cache for these two files so this test
31 | // could run independently.
32 | const indexPath = '../../lib/index';
33 | const xrayPath = '../../lib/aws-xray';
34 | delete require.cache[require.resolve(indexPath)];
35 | delete require.cache[require.resolve(xrayPath)];
36 |
37 | AWSXRay = require(indexPath);
38 |
39 | setSDKDataStub.should.have.been.calledWithExactly(sinon.match.object);
40 | setServiceDataStub.should.have.been.calledWithExactly(sinon.match.object);
41 |
42 | assert.property(setSDKDataStub.firstCall.args[0], 'sdk');
43 | assert.property(setSDKDataStub.firstCall.args[0], 'sdk_version');
44 | assert.notStrictEqual(
45 | setSDKDataStub.firstCall.args[0].sdk_version,
46 | 'unknown',
47 | 'Expected sdk_version to not be unknown'
48 | );
49 | assert.property(setSDKDataStub.firstCall.args[0], 'package');
50 | assert.notStrictEqual(
51 | setSDKDataStub.firstCall.args[0].package,
52 | 'unknown',
53 | 'Expected package to not be unknown'
54 | );
55 |
56 | assert.property(setServiceDataStub.firstCall.args[0], 'runtime');
57 | assert.property(setServiceDataStub.firstCall.args[0], 'runtime_version');
58 | assert.property(setServiceDataStub.firstCall.args[0], 'name');
59 | assert.property(setServiceDataStub.firstCall.args[0], 'version');
60 | });
61 | });
62 |
63 | describe('#config', function() {
64 | var sandbox, setOriginStub, setPluginDataStub;
65 |
66 | beforeEach(function() {
67 | sandbox = sinon.createSandbox();
68 |
69 | setPluginDataStub = sandbox.stub(segmentUtils, 'setPluginData');
70 | setOriginStub = sandbox.stub(segmentUtils, 'setOrigin');
71 | });
72 |
73 | afterEach(function() {
74 | sandbox.restore();
75 | });
76 |
77 | it('should load the given plugins and set the data on segmentUtils', function(done) {
78 | var data = { client: 'data' };
79 | var pluginStub = {
80 | getData: function(callback) {
81 | callback(data);
82 | }
83 | };
84 | AWSXRay.config([pluginStub]);
85 |
86 | setTimeout(function() {
87 | setPluginDataStub.should.have.been.calledWithExactly(data);
88 | done();
89 | }, 50);
90 | });
91 |
92 | it('should set segmentUtils origin to beanstalk if beanstalk plugin was loaded', function(done) {
93 | var pluginStub = {
94 | getData: function(callback) {
95 | callback('data');
96 | },
97 | originName: 'AWS::ElasticBeanstalk::Environment'
98 | };
99 | AWSXRay.config([pluginStub]);
100 |
101 | setTimeout(function() {
102 | setOriginStub.should.have.been.calledWithExactly('AWS::ElasticBeanstalk::Environment');
103 | done();
104 | }, 50);
105 | });
106 | });
107 | });
108 |
--------------------------------------------------------------------------------
/packages/core/test/unit/env/sqs_message_helper.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('chai').assert;
2 | var chai = require('chai');
3 | var sinonChai = require('sinon-chai');
4 |
5 | import SqsMessageHelper from '../../../lib/env/sqs_message_helper';
6 |
7 | chai.should();
8 | chai.use(sinonChai);
9 |
10 | describe('#SqsMessageHelper', function () {
11 |
12 | // sample records from https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html
13 | const sampleSqsMessageEvent = {
14 | 'Records': [
15 | {
16 | 'messageId': '059f36b4-87a3-44ab-83d2-661975830a7d',
17 | 'receiptHandle': 'AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...',
18 | 'body': 'Test message.',
19 | 'attributes': {
20 | 'ApproximateReceiveCount': '1',
21 | 'SentTimestamp': '1545082649183',
22 | 'SenderId': 'AIDAIENQZJOLO23YVJ4VO',
23 | 'ApproximateFirstReceiveTimestamp': '1545082649185',
24 | 'AWSTraceHeader':'Root=1-632BB806-bd862e3fe1be46a994272793;Sampled=1'
25 | },
26 | 'messageAttributes': {},
27 | 'md5OfBody': 'e4e68fb7bd0e697a0ae8f1bb342846b3',
28 | 'eventSource': 'aws:sqs',
29 | 'eventSourceARN': 'arn:aws:sqs:us-east-2:123456789012:my-queue',
30 | 'awsRegion': 'us-east-2'
31 | },
32 | {
33 | 'messageId': '2e1424d4-f796-459a-8184-9c92662be6da',
34 | 'receiptHandle': 'AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...',
35 | 'body': 'Test message.',
36 | 'attributes': {
37 | 'ApproximateReceiveCount': '1',
38 | 'SentTimestamp': '1545082650636',
39 | 'SenderId': 'AIDAIENQZJOLO23YVJ4VO',
40 | 'ApproximateFirstReceiveTimestamp': '1545082650649',
41 | 'AWSTraceHeader':'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=0'
42 | },
43 | 'messageAttributes': {},
44 | 'md5OfBody': 'e4e68fb7bd0e697a0ae8f1bb342846b3',
45 | 'eventSource': 'aws:sqs',
46 | 'eventSourceARN': 'arn:aws:sqs:us-east-2:123456789012:my-queue',
47 | 'awsRegion': 'us-east-2'
48 | },
49 | {
50 | 'messageId': '2e1424d4-f796-459a-8184-9c92662be6da',
51 | 'receiptHandle': 'AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...',
52 | 'body': 'Test message.',
53 | 'attributes': {
54 | 'ApproximateReceiveCount': '1',
55 | 'SentTimestamp': '1545082650636',
56 | 'SenderId': 'AIDAIENQZJOLO23YVJ4VO',
57 | 'ApproximateFirstReceiveTimestamp': '1545082650649',
58 | 'AWSTraceHeader':'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8'
59 | },
60 | 'messageAttributes': {},
61 | 'md5OfBody': 'e4e68fb7bd0e697a0ae8f1bb342846b3',
62 | 'eventSource': 'aws:sqs',
63 | 'eventSourceARN': 'arn:aws:sqs:us-east-2:123456789012:my-queue',
64 | 'awsRegion': 'us-east-2'
65 | }
66 | ]
67 | };
68 |
69 | describe('SqsMessageHelper isSampled', function() {
70 |
71 | it('should return true when AWSTraceHeader has Sampled=1', function() {
72 | assert.equal(SqsMessageHelper.isSampled(sampleSqsMessageEvent.Records[0]), true);
73 | });
74 |
75 | it('should return false when AWSTraceHeader has Sampled=0', function() {
76 | assert.equal(SqsMessageHelper.isSampled(sampleSqsMessageEvent.Records[1]), false);
77 | });
78 |
79 | it('should return false when AWSTraceHeader has no Sampled flag', function() {
80 | assert.equal(SqsMessageHelper.isSampled(sampleSqsMessageEvent.Records[2]), false);
81 | });
82 |
83 | });
84 | });
85 |
--------------------------------------------------------------------------------
/packages/core/test/unit/sampling/local_reservoir.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('chai').assert;
2 | var expect = require('chai').expect;
3 | var sinon = require('sinon');
4 |
5 | var LocalReservoir = require('../../../lib/middleware/sampling/local_reservoir');
6 |
7 | describe('LocalReservoir', function() {
8 | describe('#constructor', function() {
9 | it('should return a new Sampler with fixed target and rate set', function() {
10 | var localReservoir = new LocalReservoir(5, 0.5);
11 |
12 | assert(!isNaN(localReservoir.fixedTarget), 'Expected fixed target to be a number.');
13 | assert(!isNaN(localReservoir.fallbackRate), 'Expected rate to be a number.');
14 | });
15 |
16 | it('should throw an exception if fixed target is a float or a negative number', function() {
17 | expect(function() {
18 | new LocalReservoir(123.45, 0.5);
19 | }).to.throw(Error, '"fixed_target" must be a non-negative integer.');
20 | expect(function() {
21 | new LocalReservoir(-123, 0.5);
22 | }).to.throw(Error, '"fixed_target" must be a non-negative integer.');
23 | });
24 |
25 | it('should throw an exception if rate is not a number between 0 and 1', function() {
26 | expect(function() {
27 | new LocalReservoir(5, 123);
28 | }).to.throw(Error, '"rate" must be a number between 0 and 1 inclusive.');
29 | expect(function() {
30 | new LocalReservoir(5, -0.5);
31 | }).to.throw(Error, '"rate" must be a number between 0 and 1 inclusive.');
32 | });
33 | });
34 |
35 | describe('#isSampled', function() {
36 | var sandbox, localReservoir;
37 | var fixedTarget = 5;
38 |
39 | before(function() {
40 | sandbox = sinon.createSandbox();
41 | sandbox.stub(Math, 'round').returns(1);
42 | });
43 |
44 | beforeEach(function() {
45 | localReservoir = new LocalReservoir(fixedTarget, 0);
46 | });
47 |
48 | after(function() {
49 | sandbox.restore();
50 | });
51 |
52 | it('should return true up to the fixed target set.', function() {
53 | for (var i = 0; i < fixedTarget; i++) {
54 | assert.isTrue(localReservoir.isSampled());
55 | }
56 |
57 | assert.isFalse(localReservoir.isSampled());
58 | });
59 |
60 | it('should call Math.random and use the rate set if the fixed target has already been reached.', function() {
61 | localReservoir.thisSecond = 1;
62 | localReservoir.usedThisSecond = 5;
63 | var randomStub = sandbox.stub(Math, 'random').returns(1);
64 |
65 | localReservoir.isSampled();
66 | randomStub.should.have.been.calledOnce;
67 | });
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/packages/core/test/unit/segments/attributes/captured_exception.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('chai').assert;
2 | var CapturedException = require('../../../../lib/segments/attributes/captured_exception');
3 |
4 | describe('CapturedException', function() {
5 | describe('#constructor', function() {
6 | it('should create a CapturedException for a String', function() {
7 | var err = 'Error here!';
8 | var captured = new CapturedException(err);
9 |
10 | assert.equal(captured.message, err);
11 | assert.equal(captured.type, '');
12 | assert.equal(captured.id.length, 16);
13 | assert.deepEqual(captured.stack, []);
14 | });
15 |
16 | it('should create a CapturedException for an Error', function() {
17 | var err = new Error('Error here!');
18 | var captured = new CapturedException(err);
19 |
20 | assert.equal(captured.message, err.message);
21 | assert.equal(captured.type, err.name);
22 | assert.equal(captured.id.length, 16);
23 | assert.isArray(captured.stack);
24 | });
25 |
26 | it('should create a CapturedException for an Error with no stack trace', function() {
27 | var err = { message: 'Error here!', name: 'Error'};
28 | var captured = new CapturedException(err);
29 |
30 | assert.deepEqual(captured.stack, []);
31 | });
32 |
33 | it('should create a CapturedException for an Error with a parsed stack trace', function() {
34 | var err = new Error('Test error');
35 | err.stack = ('Test error\n at /path/to/file.js:200:15\n ' +
36 | 'at myTestFunction /path/to/another/file.js:20:30\n ' +
37 | 'at myTest [as _myTests] (test.js:10:5)');
38 |
39 | var stack = [
40 | {
41 | path: '/path/to/file.js',
42 | line: 200,
43 | label: 'anonymous'
44 | },
45 | {
46 | path: '/path/to/another/file.js',
47 | line: 20,
48 | label: 'myTestFunction'
49 | },
50 | {
51 | path: 'test.js',
52 | line: 10,
53 | label: 'myTest [as _myTests]'
54 | }
55 | ];
56 |
57 | var captured = new CapturedException(err);
58 | assert.deepEqual(captured.stack, stack);
59 | });
60 |
61 | it('should create a CapturedException with remote false by default', function() {
62 | var err = { message: 'Error here!', name: 'Error'};
63 | var captured = new CapturedException(err);
64 |
65 | assert.equal(captured.remote, false);
66 | });
67 |
68 | it('should create a CapturedException with remote true when set', function() {
69 | var err = { message: 'Error here!', name: 'Error'};
70 | var captured = new CapturedException(err, true);
71 |
72 | assert.equal(captured.remote, true);
73 | });
74 | });
75 | });
76 |
--------------------------------------------------------------------------------
/packages/core/test/unit/segments/attributes/remote_request_data.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('chai').assert;
2 | var chai = require('chai');
3 |
4 | var RemoteRequestData = require('../../../../lib/segments/attributes/remote_request_data');
5 |
6 | chai.should();
7 |
8 | describe('RemoteRequestData', function() {
9 | const defaultRequest = {
10 | agent: {
11 | protocol: 'https:'
12 | },
13 | getHeader: (key) => {
14 | if (key === 'host') {
15 | return 'host.com';
16 | }
17 | return undefined;
18 | },
19 | path: '/path/to/resource'
20 | };
21 |
22 | const defaultResponse = {
23 | statusCode: 200,
24 | headers: {
25 | 'content-length': 10,
26 | }
27 | };
28 |
29 | var request = defaultRequest;
30 | var response = defaultResponse;
31 |
32 | this.beforeEach(function () {
33 | request = defaultRequest;
34 | response = defaultResponse;
35 | });
36 |
37 | describe('#constructor', function() {
38 | it('should mask out query string in path', function() {
39 | const requestWithPathQueryString = Object.assign(request, { path: '/path/to/resource?qs=qs' });
40 |
41 | assert.propertyVal(
42 | new RemoteRequestData(requestWithPathQueryString, response, true).request,
43 | 'url',
44 | 'https://host.com/path/to/resource'
45 | );
46 | });
47 | it('should return empty url if request agent is missing', function() {
48 | const requestWithoutAgent = {};
49 |
50 | assert.propertyVal(
51 | new RemoteRequestData(requestWithoutAgent, response, true).request,
52 | 'url',
53 | ''
54 | );
55 | });
56 | it('should use the host from the request object over headers', () => {
57 | const requestWithHost = Object.assign(request, { host: 'different-site.com' });
58 |
59 | assert.propertyVal(
60 | new RemoteRequestData(requestWithHost, response, true).request,
61 | 'url',
62 | 'https://different-site.com/path/to/resource'
63 | );
64 | });
65 | });
66 | });
67 |
--------------------------------------------------------------------------------
/packages/core/test/unit/segments/attributes/trace_id.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('chai').assert;
2 | var TraceID = require('../../../../lib/segments/attributes/trace_id');
3 |
4 | function validateTraceID(traceID) {
5 | const hexRegex = /^[0-9a-fA-F]+$/;
6 | assert.isNumber(traceID.version);
7 | assert.isTrue(hexRegex.test(traceID.timestamp));
8 | assert.isTrue(hexRegex.test(traceID.id));
9 | }
10 |
11 | describe('TraceID', function() {
12 | it('should construct a valid trace ID', function() {
13 | var traceId = new TraceID();
14 | validateTraceID(traceId);
15 | });
16 |
17 | it('should have created a valid trace ID from given string', function() {
18 | const traceStr = '1-57fbe041-2c7ad569f5d6ff149137be86';
19 | var traceId = TraceID.FromString(traceStr);
20 | assert.equal(traceId.version, 1);
21 | assert.equal(traceId.timestamp, '57fbe041');
22 | assert.equal(traceId.id, '2c7ad569f5d6ff149137be86');
23 | });
24 |
25 | it('should return a valid trace ID given undefined', function() {
26 | var traceId = TraceID.FromString(undefined);
27 | validateTraceID(traceId);
28 | });
29 |
30 | it('should return a valid trace ID when given malformed string', function() {
31 | const traceStr = 'FAKE-TRACE';
32 | var traceId = TraceID.FromString(traceStr);
33 | validateTraceID(traceId);
34 | });
35 |
36 | it('should return a valid trace ID when given partially malformed string', function() {
37 | const traceStr = '1-XYZ-2c7ad569f5d6ff149137be86';
38 | var traceId = TraceID.FromString(traceStr);
39 | validateTraceID(traceId);
40 | });
41 |
42 | it('should keep given trace ID the same between fromString and toString', function() {
43 | const traceStr = '1-57fbe041-2c7ad569f5d6ff149137be86';
44 | var traceId = TraceID.FromString(traceStr);
45 | assert.equal(traceId.toString(), traceStr);
46 | });
47 |
48 | it('should keep leading 0\'s for trace ID from given string', function() {
49 | const traceStr = '1-00fbe041-2c7ad569f5d6ff149137be86';
50 | var traceId = TraceID.FromString(traceStr);
51 | assert.equal(traceId.version, 1);
52 | assert.equal(traceId.timestamp, '00fbe041');
53 | assert.equal(traceId.id, '2c7ad569f5d6ff149137be86');
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/packages/core/test/unit/segments/plugins/ecs_plugin.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 | var chai = require('chai');
3 | var sinon = require('sinon');
4 | var sinonChai = require('sinon-chai');
5 |
6 | chai.use(sinonChai);
7 |
8 | var ECSPlugin = require('../../../../lib/segments/plugins/ecs_plugin');
9 |
10 | describe('ECSPlugin', function() {
11 | var sandbox;
12 |
13 | beforeEach(function() {
14 | sandbox = sinon.createSandbox();
15 | });
16 |
17 | afterEach(function() {
18 | sandbox.restore();
19 | });
20 |
21 | it('should return an object holding ECS metadata', function(done) {
22 | ECSPlugin.getData(function(data) {
23 | expect(data.ecs.container).not.to.be.empty;
24 | done();
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/packages/core/test/unit/segments/plugins/elastic_beanstalk_plugin.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 | var fs = require('fs');
3 | var chai = require('chai');
4 | var sinon = require('sinon');
5 | var sinonChai = require('sinon-chai');
6 |
7 | chai.use(sinonChai);
8 |
9 | var ElasticBeanstalkPlugin = require('../../../../lib/segments/plugins/elastic_beanstalk_plugin');
10 |
11 | describe('ElasticBeanstalkPlugin', function() {
12 | var err = new Error('Cannot load file.');
13 | var data = {
14 | deployment_id: 'deployment_id',
15 | version_label: 'version_label',
16 | environment_name: 'my_env'
17 | };
18 |
19 | var readStub, sandbox;
20 |
21 | beforeEach(function() {
22 | sandbox = sinon.createSandbox();
23 | });
24 |
25 | afterEach(function() {
26 | sandbox.restore();
27 | });
28 |
29 | it('should return an object holding Beanstalk metadata if it read data', function(done) {
30 | readStub = sandbox.stub(fs, 'readFile').yields(null, data);
31 | sandbox.stub(JSON, 'parse').returns(data);
32 |
33 | ElasticBeanstalkPlugin.getData(function(data) {
34 | readStub.should.have.been.calledOnce;
35 | expect(data.elastic_beanstalk).not.to.be.empty;
36 | done();
37 | });
38 | });
39 |
40 | it('should return undefined if the read fails', function(done) {
41 | readStub = sandbox.stub(fs, 'readFile').yields(err, null);
42 |
43 | ElasticBeanstalkPlugin.getData(function(data) {
44 | readStub.should.have.been.calledOnce;
45 | expect(data).to.be.undefined;
46 | done();
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/packages/core/test/unit/segments/plugins/plugin.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 | var assert = require('chai').assert;
3 | var nock = require('nock');
4 |
5 | var Plugin = require('../../../../lib/segments/plugins/plugin');
6 |
7 | describe('Plugin', function() {
8 | const METADATA_HOST = 'http://localhost';
9 |
10 | describe('#getPluginMetadata', function() {
11 | var data = { data: 1234 };
12 | var getPluginMetadata = Plugin.getPluginMetadata;
13 | const METADATA_PATH = '/metadata';
14 | const OPTIONS = {
15 | host: 'localhost',
16 | path: '/metadata'
17 | };
18 |
19 | var getMetadata;
20 |
21 | it('should return metadata if 200 OK', function(done) {
22 | getMetadata = nock(METADATA_HOST)
23 | .get(METADATA_PATH)
24 | .reply(200, data);
25 |
26 | getPluginMetadata(OPTIONS, function(err, data) {
27 | expect(data.data).to.equal(1234);
28 | getMetadata.done();
29 | done();
30 | });
31 | });
32 |
33 | it('should retry on 5xx', function(done) {
34 | getMetadata = nock(METADATA_HOST)
35 | .get(METADATA_PATH)
36 | .times(3)
37 | .reply(500)
38 | .get(METADATA_PATH)
39 | .reply(200, data);
40 |
41 | getPluginMetadata(OPTIONS, function(err, data) {
42 | expect(data.data).to.equal(1234);
43 | getMetadata.done();
44 | done();
45 | });
46 | });
47 |
48 | it('should retry on 5xx 5 times then error out', function(done) {
49 | getMetadata = nock(METADATA_HOST)
50 | .get(METADATA_PATH)
51 | .times(3)
52 | .reply(500)
53 | .get(METADATA_PATH)
54 | .times(3)
55 | .reply(504); // Ensure retry on different 5xx codes
56 |
57 | getPluginMetadata(OPTIONS, function(err, data) {
58 | assert.isUndefined(data);
59 | getMetadata.done();
60 | done();
61 | });
62 | });
63 |
64 | it('should fast fail on 4xx status code', function(done) {
65 | getMetadata = nock(METADATA_HOST)
66 | .get(METADATA_PATH)
67 | .reply(400);
68 |
69 | getPluginMetadata(OPTIONS, function(err, data) {
70 | assert.isUndefined(data);
71 | getMetadata.done();
72 | done();
73 | });
74 | });
75 | });
76 | });
77 |
--------------------------------------------------------------------------------
/packages/core/test/unit/segments/segment_utils.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('chai').assert;
2 |
3 | var SegmentUtils = require('../../../lib/segments/segment_utils');
4 |
5 | describe('SegmentUtils', function() {
6 | afterEach(function() {
7 | SegmentUtils.setStreamingThreshold(100);
8 | });
9 |
10 | describe('#setStreamingThreshold', function() {
11 | it('should override the default streaming threshold', function() {
12 | SegmentUtils.setStreamingThreshold(10);
13 |
14 | assert.equal(SegmentUtils.streamingThreshold, 10);
15 | });
16 | });
17 |
18 | describe('#getHttpResponseData', () => {
19 | it('should populate attributes as integers', () => {
20 | const responseWithStrings = {statusCode: '200', headers: {'content-length': '42'}};
21 | const res = SegmentUtils.getHttpResponseData(responseWithStrings);
22 | assert.deepEqual(res, {
23 | 'content_length': 42,
24 | 'status': 200
25 | });
26 | });
27 |
28 | it('should omit missing properties', () => {
29 | const responseWithStatus = {statusCode: 200};
30 | const responseWithLength = {headers: {'content-length': 42}};
31 | const emptyResponse = {};
32 |
33 | const statusRes = SegmentUtils.getHttpResponseData(responseWithStatus);
34 | const lengthRes = SegmentUtils.getHttpResponseData(responseWithLength);
35 | const emptyRes = SegmentUtils.getHttpResponseData(emptyResponse);
36 |
37 | assert.deepEqual(statusRes, {
38 | 'status': 200
39 | });
40 | assert.deepEqual(lengthRes, {
41 | 'content_length': 42
42 | });
43 | assert.deepEqual(emptyRes, {});
44 | });
45 | });
46 |
47 | describe('#getJsonStringifyReplacer', () => {
48 | it('should stringify BigInts', () => {
49 | const obj = {foo: 1n, bar: BigInt(2)};
50 | const replacer = SegmentUtils.getJsonStringifyReplacer();
51 | const result = JSON.stringify(obj, replacer);
52 |
53 | assert.equal(result, '{"foo":"1","bar":"2"}');
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/core/test/unit/test_utils.js:
--------------------------------------------------------------------------------
1 | var EventEmitter = require('events');
2 | var util = require('util');
3 |
4 | var TestUtils = {};
5 |
6 | TestUtils.TestEmitter = function TestEmitter() {
7 | EventEmitter.call(this);
8 | };
9 |
10 | util.inherits(TestUtils.TestEmitter, EventEmitter);
11 |
12 | TestUtils.onEvent = function onEvent(event, fcn) {
13 | this.emitter.on(event, fcn.bind(this));
14 | return this;
15 | };
16 |
17 | TestUtils.randomString = function randomString(length) {
18 | var text = '';
19 | var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.#-_$%^&@!';
20 | for (var i = 0; i < length; i++) {
21 | text += possible.charAt(Math.floor(Math.random() * possible.length));
22 | }
23 |
24 | return text;
25 | };
26 |
27 | module.exports = TestUtils;
28 |
--------------------------------------------------------------------------------
/packages/core/test_async/integration/segment_maintained_across_awaited.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('chai').assert;
2 | var http = require('http');
3 | var AWSXRay = require('../../lib');
4 | var Segment = AWSXRay.Segment;
5 |
6 | AWSXRay.enableAutomaticMode();
7 |
8 | describe('Integration', function() {
9 | describe('#async', function() {
10 | it('should maintain segment in async functions', function(done) {
11 | var sharedPromise = null;
12 |
13 | var requestCount = 0;
14 | var server = http
15 | .createServer(function(req, res) {
16 | var ns = AWSXRay.getNamespace();
17 | ns.bindEmitter(req);
18 | ns.bindEmitter(res);
19 |
20 | ns.run(function () {
21 | var segment = new Segment('root');
22 |
23 | AWSXRay.setSegment(segment);
24 |
25 | if (!sharedPromise) {
26 | sharedPromise = Promise.resolve();
27 | }
28 |
29 | // execute an async task
30 | sharedPromise.then(async () => {
31 |
32 | await sleep(0);
33 |
34 | var retrievedSegment = AWSXRay.getSegment();
35 | res.end();
36 |
37 | // setTimeout so the assertion isn't caught by the promise
38 | setTimeout(function() {
39 | assert.equal(segment.id, retrievedSegment.id);
40 | if (++requestCount === 2) {
41 | done();
42 | }
43 | });
44 | });
45 | });
46 | }).listen(8080, '0.0.0.0', function() {
47 |
48 | var address = server.address();
49 |
50 | var count = 0;
51 | function cb(err) {
52 | if (err) {
53 | throw err;
54 | }
55 |
56 | if (++count === 2) {
57 | server.close();
58 | }
59 | }
60 | sendRequest(address, cb);
61 | sendRequest(address, cb);
62 | });
63 | });
64 | });
65 | });
66 |
67 | function sendRequest(address, cb) {
68 | http
69 | .request({
70 | hostname: address.address,
71 | port: address.port,
72 | path: '/'
73 | })
74 | .on('response', function(res) {
75 | res.on('end', cb).resume();
76 | })
77 | .on('error', cb)
78 | .end();
79 | }
80 |
81 | function sleep(ms) {
82 | return new Promise((resolve) => {
83 | setTimeout(resolve, ms);
84 | });
85 | }
86 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es2019",
5 | "strict": true,
6 | "strictNullChecks": true,
7 | "declaration": false,
8 | "esModuleInterop": true,
9 | "skipLibCheck": true,
10 | "allowJs": true,
11 | "checkJs": false,
12 | "outDir": "dist",
13 | },
14 | "include": ["lib", "test", "test_async"],
15 | "exclude": ["dist", "node_modules"]
16 | }
17 |
--------------------------------------------------------------------------------
/packages/express/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/express/.npmignore:
--------------------------------------------------------------------------------
1 | .npmignore
2 | node_modules
3 | npm-debug.log
4 | docs
5 | AWSXRay.log
6 | Config
7 |
--------------------------------------------------------------------------------
/packages/express/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog for AWS X-Ray SDK Express for JavaScript
2 |
3 | **Beginning after v3.2.0, ChangeLog entries for this package are recorded in the [top-level CHANGELOG file](../../CHANGELOG.md).**
4 |
5 |
6 |
7 | ## 2.5.0
8 | * improvement: Added TypeScript definitions [PR #207](https://github.com/aws/aws-xray-sdk-node/pull/207)
9 |
10 | ## 2.3.4
11 | * improvement: Updated eslint dev dependency: [PR #145](https://github.com/aws/aws-xray-sdk-node/pull/145)
12 | * improvement: Updated .eslintrc.json to enable es6 and fixed eslint errors: [PR #146](https://github.com/aws/aws-xray-sdk-node/pull/146)
13 | * improvement: Updated nock,mocha,sinon dependencies to fix lodash version: [PR #153](https://github.com/aws/aws-xray-sdk-node/pull/153)
14 |
15 | ## 2.3.3
16 | * bugfix(express): express middleware closes segments when client request cancelled. [PR#128](https://github.com/aws/aws-xray-sdk-node/pull/128)
17 |
18 | ## 1.1.5
19 | * The X-Ray SDK for Node.js is now an open source project. You can follow the project and submit issues and pull requests on [GitHub](https://github.com/aws/aws-xray-sdk-node).
20 |
21 | ## 1.1.2
22 | * bugfix: Changed behavior on a http status code 429. Segment should have been marked as 'throttle' and 'error'.
23 |
24 | ## 1.1.1
25 | * feature: Added debug logs for opening and closing segments.
26 |
--------------------------------------------------------------------------------
/packages/express/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK Express for JavaScript
2 | Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/packages/express/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Requirements
3 |
4 | * AWS X-Ray SDK Core (aws-xray-sdk-core)
5 | * Express 4.14.0 or greater
6 |
7 | ## AWS X-Ray and Express
8 |
9 | The AWS X-Ray Express package automatically records information for incoming and outgoing
10 | requests and responses, via the middleware functions in this package. To configure sampling,
11 | dynamic naming, and more see the [set up section](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/core#setup).
12 |
13 | The AWS X-Ray SDK Core has two modes - `manual` and `automatic`.
14 | Automatic mode uses the `cls-hooked` package and automatically
15 | tracks the current segment and subsegment. This is the default mode.
16 | Manual mode requires that you pass around the segment reference.
17 |
18 | In automatic mode, you can get the current segment/subsegment at any time:
19 |
20 | var segment = AWSXRay.getSegment();
21 |
22 | In manual mode, you can get the base segment off of the request object:
23 |
24 | var segment = req.segment;
25 |
26 | ## Middleware Usage
27 |
28 | The X-Ray SDK provides two middlewares: `AWSXRay.express.openSegment()`
29 | and `AWSXRay.express.closeSegment()`. These two middlewares must be used together
30 | and wrap all of your defined routes that you'd like to trace.
31 | In automatic mode, the `openSegment` middleware *must* be the last middleware added
32 | before defining routes, and the `closeSegment` middleware *must* be the
33 | first middleware added after defining routes. Otherwise issues with the `cls-hooked`
34 | context may occur.
35 |
36 | ## Sample App
37 |
38 | To get started with a functional express application instrumented with the X-Ray SDK, check out our [sample app](https://github.com/aws-samples/aws-xray-sdk-node-sample).
39 |
40 | ## Automatic mode example
41 | For more automatic mode examples, see the
42 | [Automatic Mode Examples](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/core#Automatic-Mode-Examples).
43 |
44 | ### Capture all incoming requests to `/` and `/directory`
45 |
46 | ```js
47 | var AWSXRay = require('aws-xray-sdk-core');
48 | var xrayExpress = require('aws-xray-sdk-express');
49 | var app = express();
50 |
51 | //...
52 |
53 | app.use(xrayExpress.openSegment('defaultName'));
54 |
55 | app.get('/', function (req, res) {
56 | var segment = AWSXRay.getSegment();
57 | segment.addAnnotation('page', 'home');
58 |
59 | //...
60 |
61 | res.render('index');
62 | });
63 |
64 | app.get('/directory', function (req, res) {
65 | var segment = AWSXRay.getSegment();
66 | segment.addAnnotation('page', 'directory');
67 |
68 | //...
69 |
70 | res.render('directory');
71 | });
72 |
73 | app.use(xrayExpress.closeSegment());
74 | ```
75 |
76 | ## Manual mode examples
77 | For more manual mode examples, e.g. what to do with the segment inside your route logic,
78 | see the [Manual Mode Examples](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/core#Manual-Mode-Examples). Note that you don't have to manually start or close the segments since that is handled by the X-Ray middleware.
79 |
80 | ### Capture All incoming requests to `/`
81 |
82 | ```js
83 | var AWSXRay = require('aws-xray-sdk-core');
84 | var xrayExpress = require('aws-xray-sdk-express');
85 | var app = express();
86 |
87 | //...
88 |
89 | var AWSXRay = require('aws-xray-sdk');
90 |
91 | //Required at the start of your routes
92 | app.use(xrayExpress.openSegment('defaultName'));
93 |
94 | app.get('/', function (req, res) {
95 | var segment = req.segment;
96 |
97 | //...
98 |
99 | res.render('index');
100 | });
101 |
102 | app.use(xrayExpress.closeSegment()); //Required at the end of your routes / first in error handling routes
103 | ```
104 |
--------------------------------------------------------------------------------
/packages/express/lib/express_mw.d.ts:
--------------------------------------------------------------------------------
1 | import { ErrorRequestHandler, RequestHandler } from 'express';
2 |
3 | export function openSegment(defaultName: string): RequestHandler;
4 |
5 | export function closeSegment(): ErrorRequestHandler;
6 |
--------------------------------------------------------------------------------
/packages/express/lib/express_mw.js:
--------------------------------------------------------------------------------
1 | const AWSXRay = require('aws-xray-sdk-core');
2 |
3 | const mwUtils = AWSXRay.middleware;
4 |
5 | /**
6 | * Express middleware module.
7 | *
8 | * Exposes Express middleware functions to enable automated data capturing on a web service. To enable on a Node.js/Express application,
9 | * use 'app.use(AWSXRayExpress.openSegment())' before defining your routes. After your routes, before any extra error
10 | * handling middleware, use 'app.use(AWSXRayExpress.closeSegment())'.
11 | * Use AWSXRay.getSegment() to access the current sub/segment.
12 | * Otherwise, for manual mode, this appends the Segment object to the request object as req.segment.
13 | * @module express_mw
14 | */
15 | const expressMW = {
16 |
17 | /**
18 | * Use 'app.use(AWSXRayExpress.openSegment('defaultName'))' before defining your routes.
19 | * Use AWSXRay.getSegment() to access the current sub/segment.
20 | * Otherwise, for manual mode, this appends the Segment object to the request object as req.segment.
21 | * @param {string} defaultName - The default name for the segment.
22 | * @alias module:express_mw.openSegment
23 | * @returns {function}
24 | */
25 | openSegment: (defaultName) => {
26 | if (!defaultName || typeof defaultName !== 'string') {
27 | throw new Error('Default segment name was not supplied. Please provide a string.');
28 | }
29 |
30 | mwUtils.setDefaultName(defaultName);
31 |
32 | return (req, res, next) => {
33 | const segment = mwUtils.traceRequestResponseCycle(req, res);
34 |
35 | if (AWSXRay.isAutomaticMode()) {
36 | const ns = AWSXRay.getNamespace();
37 | ns.bindEmitter(req);
38 | ns.bindEmitter(res);
39 |
40 | ns.run(() => {
41 | AWSXRay.setSegment(segment);
42 | if (next) {
43 | next();
44 | }
45 | });
46 | } else {
47 | req.segment = segment;
48 | if (next) {
49 | next();
50 | }
51 | }
52 | };
53 | },
54 |
55 | /**
56 | * After your routes, before any extra error handling middleware, use 'app.use(AWSXRayExpress.closeSegment())'.
57 | * This is error-handling middleware, so it is called only when there is a server-side fault.
58 | * @alias module:express_mw.closeSegment
59 | * @returns {function}
60 | */
61 | closeSegment: () => {
62 | return (err, req, res, next) => {
63 | const segment = AWSXRay.resolveSegment(req.segment);
64 |
65 | if (segment && err) {
66 | segment.addError(err);
67 | AWSXRay.getLogger().debug('Added Express server fault to segment');
68 | }
69 |
70 | if (next) {
71 | next(err);
72 | }
73 | };
74 | }
75 | };
76 |
77 | module.exports = expressMW;
78 |
--------------------------------------------------------------------------------
/packages/express/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 |
3 | declare global {
4 | namespace Express {
5 | interface Request {
6 | segment?: AWSXRay.Segment;
7 | }
8 | }
9 | }
10 |
11 | export * from './express_mw';
12 |
--------------------------------------------------------------------------------
/packages/express/lib/index.js:
--------------------------------------------------------------------------------
1 | // Convenience file to require the SDK from the root of the repository
2 | module.exports = require('./express_mw');
3 |
--------------------------------------------------------------------------------
/packages/express/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-express",
3 | "version": "3.10.3",
4 | "description": "AWS X-Ray Middleware for Express (Javascript)",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Sandra McMullen "
8 | ],
9 | "main": "lib/index.js",
10 | "types": "lib/index.d.ts",
11 | "engines": {
12 | "node": ">= 14.x"
13 | },
14 | "directories": {
15 | "test": "test"
16 | },
17 | "dependencies": {
18 | "@types/express": "*"
19 | },
20 | "peerDependencies": {
21 | "aws-xray-sdk-core": "^3.10.3"
22 | },
23 | "scripts": {
24 | "test": "mocha --recursive ./test/ -R spec && tsd",
25 | "test-d": "tsd",
26 | "lint": "eslint .",
27 | "lint:fix": "eslint . --fix"
28 | },
29 | "keywords": [
30 | "amazon",
31 | "api",
32 | "aws",
33 | "express",
34 | "xray",
35 | "x-ray",
36 | "x ray"
37 | ],
38 | "license": "Apache-2.0",
39 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/packages/express"
40 | }
41 |
--------------------------------------------------------------------------------
/packages/express/test-d/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 | import express from 'express';
3 | import { expectType } from 'tsd';
4 | import * as xrayExpress from '../lib';
5 |
6 | const app = express();
7 |
8 | app.use(xrayExpress.openSegment('defaultName'));
9 |
10 | app.get('/', function(req, res) {
11 | expectType(req.segment);
12 | res.render('index');
13 | });
14 |
15 | app.use(xrayExpress.closeSegment());
16 |
--------------------------------------------------------------------------------
/packages/express/test/test_utils.js:
--------------------------------------------------------------------------------
1 | var EventEmitter = require('events');
2 | var util = require('util');
3 |
4 | var TestUtils = {};
5 |
6 | TestUtils.TestEmitter = function TestEmitter() {
7 | EventEmitter.call(this);
8 | };
9 |
10 | util.inherits(TestUtils.TestEmitter, EventEmitter);
11 |
12 | TestUtils.onEvent = function onEvent(event, fcn) {
13 | this.emitter.on(event, fcn.bind(this));
14 | return this;
15 | };
16 |
17 | module.exports = TestUtils;
18 |
--------------------------------------------------------------------------------
/packages/express/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/full_sdk/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/full_sdk/.npmignore:
--------------------------------------------------------------------------------
1 | .npmignore
2 | node_modules
3 | npm-debug.log
4 | docs
5 | AWSXRay.log
6 | Config
7 |
--------------------------------------------------------------------------------
/packages/full_sdk/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog for AWS X-Ray SDK for JavaScript
2 |
3 | The primary changelog for this SDK has moved to the [root of the repo](https://github.com/aws/aws-xray-sdk-node/blob/master/CHANGELOG.md).
4 |
--------------------------------------------------------------------------------
/packages/full_sdk/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK for JavaScript
2 | Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/packages/full_sdk/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Requirements
3 |
4 | * AWS SDK v2.7.15 or greater (if using `captureAWS` or `captureAWSClient`)
5 | * Express 4.14.0 or greater (if using Express and the associated X-Ray middleware)
6 | * MySQL 2.12.0 or greater (if using `captureMySQL`)
7 | * Postgres 6.1.0 or greater (if using `capturePostgres`)
8 |
9 | ## AWS X-Ray
10 |
11 | The AWS X-Ray SDK automatically records information for incoming and outgoing requests and responses (via middleware), as well as local data
12 | such as function calls, time, variables (via metadata and annotations), even EC2 instance data (via plugins).
13 |
14 | Although the AWS X-Ray SDK was originally intended to capture request/response data on a web app, the SDK provides functionality for use cases
15 | outside this as well. The SDK exposes the 'Segment' and 'Subsegment' objects to create your own capturing mechanisms, middleware, etc.
16 |
17 | This package includes the following AWS X-Ray packages.
18 |
19 | aws-xray-sdk-core
20 | aws-xray-sdk-express
21 | aws-xray-sdk-postgres
22 | aws-xray-sdk-mysql
23 |
24 | ## Setup
25 |
26 | The core package contains the base SDK functionality. Please see the aws-xray-sdk-core [README.md](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/core/README.md) for more details.
27 |
28 | ### Support for web frameworks
29 |
30 | * [Express](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/express)
31 | * [Restify](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/restify)
32 |
--------------------------------------------------------------------------------
/packages/full_sdk/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as express from 'aws-xray-sdk-express';
2 | import { captureMySQL } from 'aws-xray-sdk-mysql';
3 | import { capturePostgres } from 'aws-xray-sdk-postgres';
4 |
5 | export * from 'aws-xray-sdk-core';
6 |
7 | export {
8 | express,
9 | captureMySQL,
10 | capturePostgres
11 | };
12 |
--------------------------------------------------------------------------------
/packages/full_sdk/lib/index.js:
--------------------------------------------------------------------------------
1 | // Convenience file to require the SDK from the root of the repository
2 | var AWSXRay = require('aws-xray-sdk-core');
3 | AWSXRay.express = require('aws-xray-sdk-express');
4 | AWSXRay.captureMySQL = require('aws-xray-sdk-mysql');
5 | AWSXRay.capturePostgres = require('aws-xray-sdk-postgres');
6 |
7 | // Import Data from package.json,
8 | // If the importing of package.json fails leave
9 | // pkginfo as an empty object
10 | var pkginfo = {};
11 | try {
12 | pkginfo = require('../package.json');
13 | } catch (err) {
14 | AWSXRay.getLogger().debug('Failed to load SDK data:', err);
15 | }
16 |
17 | var UNKNOWN = 'unknown';
18 |
19 | (function () {
20 | var sdkData = AWSXRay.SegmentUtils.sdkData || { sdk: 'X-Ray for Node.js' };
21 | sdkData.sdk_version = pkginfo.version ? pkginfo.version : UNKNOWN;
22 | sdkData.package = pkginfo.name ? pkginfo.name : UNKNOWN;
23 | AWSXRay.SegmentUtils.setSDKData(sdkData);
24 | })();
25 |
26 | module.exports = AWSXRay;
27 |
--------------------------------------------------------------------------------
/packages/full_sdk/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk",
3 | "version": "3.10.3",
4 | "description": "AWS X-Ray SDK for Javascript",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Sandra McMullen ",
8 | "William Armiros "
9 | ],
10 | "main": "lib/index.js",
11 | "types": "lib/index.d.ts",
12 | "engines": {
13 | "node": ">= 14.x"
14 | },
15 | "dependencies": {
16 | "aws-xray-sdk-core": "file:../core",
17 | "aws-xray-sdk-express": "file:../express",
18 | "aws-xray-sdk-mysql": "file:../mysql",
19 | "aws-xray-sdk-postgres": "file:../postgres"
20 | },
21 | "scripts": {
22 | "test": "tsd",
23 | "test-d": "tsd",
24 | "lint": "eslint .",
25 | "lint:fix": "eslint . --fix"
26 | },
27 | "keywords": [
28 | "amazon",
29 | "api",
30 | "aws",
31 | "xray",
32 | "x-ray",
33 | "x ray"
34 | ],
35 | "license": "Apache-2.0",
36 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/packages/full_sdk"
37 | }
38 |
--------------------------------------------------------------------------------
/packages/full_sdk/test-d/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import * as express from 'aws-xray-sdk-express';
2 | import { captureMySQL } from 'aws-xray-sdk-mysql';
3 | import { capturePostgres } from 'aws-xray-sdk-postgres';
4 | import { expectType } from 'tsd';
5 | import * as AWSXRay from '../lib';
6 |
7 | expectType(AWSXRay.express);
8 | expectType(AWSXRay.captureMySQL);
9 | expectType(AWSXRay.capturePostgres);
10 |
--------------------------------------------------------------------------------
/packages/full_sdk/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/mysql/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/mysql/.npmignore:
--------------------------------------------------------------------------------
1 | .npmignore
2 | node_modules
3 | npm-debug.log
4 | docs
5 | AWSXRay.log
6 | Config
7 |
--------------------------------------------------------------------------------
/packages/mysql/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK MySQL for JavaScript
2 | Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/packages/mysql/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Requirements
3 |
4 | * AWS X-Ray SDK Core
5 | * MySQL 2.12.0 or greater
6 |
7 | ## AWS X-Ray and MySQL
8 |
9 | The AWS X-Ray MySQL package automatically records query information and request and
10 | response data. Simply patch the MySQL package via `captureMySQL` as shown below.
11 |
12 | The AWS X-Ray SDK Core has two modes - `manual` and `automatic`.
13 | Automatic mode uses the `cls-hooked` package and automatically
14 | tracks the current segment and subsegment. This is the default mode.
15 | Manual mode requires that you pass around the segment reference. See the examples below.
16 |
17 | ### Environment variables
18 |
19 | MYSQL_DATABASE_VERSION Sets additional data for the sql subsegment.
20 | MYSQL_DRIVER_VERSION Sets additional data for the sql subsegment.
21 |
22 | ### Lambda Example
23 |
24 | ```js
25 | var AWSXRay = require('aws-xray-sdk');
26 | var pg = AWSXRay.captureMySQL(require('mysql'));
27 |
28 | ...
29 |
30 | exports.handler = function (event, context, callback) {
31 | // Make MySQL queries normally
32 | }
33 | ```
34 |
35 | ## Automatic mode example
36 |
37 | ```js
38 | var AWSXRay = require('aws-xray-sdk-core');
39 | var captureMySQL = require('aws-xray-sdk-mysql');
40 |
41 | var mysql = captureMySQL(require('mysql'));
42 |
43 | var config = { ... };
44 |
45 | ...
46 |
47 | var connection = mysql.createConnection(config);
48 |
49 | connection.query('SELECT * FROM cats', function(err, rows) {
50 | //Automatically captures query information and errors (if any)
51 | });
52 |
53 | ...
54 |
55 | var pool = mysql.createPool(config);
56 |
57 | pool.query('SELECT * FROM cats', function(err, rows, fields) {
58 | //Automatically captures query information and errors (if any)
59 | }
60 | ```
61 |
62 | ## Manual mode example
63 |
64 | ```js
65 | var AWSXRay = require('aws-xray-sdk-core');
66 | var captureMySQL = require('aws-xray-sdk-mysql');
67 |
68 | var mysql = captureMySQL(require('mysql'));
69 |
70 | var config = { ... };
71 |
72 | ...
73 |
74 | var connection = mysql.createConnection(config);
75 |
76 | connection.query('SELECT * FROM cats', function(err, rows) {
77 | //Automatically captures query information and errors (if any)
78 | }, segment);
79 |
80 | ...
81 |
82 | var pool = mysql.createPool(config);
83 |
84 | pool.query('SELECT * FROM cats', function(err, rows, fields) {
85 | //Automatically captures query information and errors (if any)
86 | }, segment);
87 | ```
88 |
--------------------------------------------------------------------------------
/packages/mysql/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from './mysql_p';
2 |
--------------------------------------------------------------------------------
/packages/mysql/lib/index.js:
--------------------------------------------------------------------------------
1 | // Convenience file to require the SDK from the root of the repository
2 | module.exports = require('./mysql_p');
3 |
--------------------------------------------------------------------------------
/packages/mysql/lib/mysql_p.d.ts:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 | import * as MySQL from 'mysql';
3 |
4 | export function captureMySQL(mysql: typeof MySQL): captureMySQL.PatchedMySQL;
5 |
6 | declare namespace captureMySQL {
7 | interface PatchedQueryFunction {
8 | (query: MySQL.Query, segment?: AWSXRay.SegmentLike): MySQL.Query;
9 | (options: string | MySQL.QueryOptions, callback?: MySQL.queryCallback, segment?: AWSXRay.SegmentLike): MySQL.Query;
10 | (options: string, values: any, callback?: MySQL.queryCallback, segment?: AWSXRay.SegmentLike): MySQL.Query;
11 | }
12 |
13 | type PatchedConnection = {
14 | [K in keyof T]: K extends 'query'
15 | ? PatchedQueryFunction
16 | : T[K];
17 | };
18 |
19 | type PatchedPoolConnection = PatchedConnection;
20 |
21 | type PatchedPoolConnectionCallback = (err: MySQL.MysqlError, connection: PatchedPoolConnection) => void;
22 |
23 | interface PatchedPoolGetConnectionFunction {
24 | (callback: PatchedPoolConnectionCallback): void;
25 | }
26 |
27 | type PatchedPool = {
28 | [K in keyof T]: K extends 'query'
29 | ? PatchedQueryFunction
30 | : K extends 'getConnection'
31 | ? PatchedPoolGetConnectionFunction
32 | : T[K];
33 | };
34 |
35 | interface PatchedPoolClusterOfFunction {
36 | (pattern: string, selector?: string): PatchedPool;
37 | (pattern: undefined | null | false, selector: string): PatchedPool;
38 | }
39 |
40 | interface PatchedPoolClusterGetConnectionFunction {
41 | (callback: PatchedPoolConnectionCallback): void;
42 | (pattern: string, callback: PatchedPoolConnectionCallback): void;
43 | (pattern: string, selector: string, callback: PatchedPoolConnectionCallback): void;
44 | }
45 |
46 | type PatchedPoolCluster = {
47 | [K in keyof T]: K extends 'of'
48 | ? PatchedPoolClusterOfFunction
49 | : K extends 'getConnection'
50 | ? PatchedPoolClusterGetConnectionFunction
51 | : T[K]
52 | };
53 |
54 | interface PatchedCreateConnectionFunction {
55 | (connectionUri: string | MySQL.ConnectionConfig): PatchedConnection;
56 | }
57 |
58 | interface PatchedCreatePoolFunction {
59 | (config: MySQL.PoolConfig | string): PatchedPool;
60 | }
61 |
62 | interface PatchedCreatePoolClusterFunction {
63 | (config?: MySQL.PoolClusterConfig): PatchedPoolCluster;
64 | }
65 |
66 | type PatchedMySQL = {
67 | [K in keyof T]: K extends 'createConnection'
68 | ? PatchedCreateConnectionFunction
69 | : K extends 'createPool'
70 | ? PatchedCreatePoolFunction
71 | : K extends 'createPoolCluster'
72 | ? PatchedCreatePoolClusterFunction
73 | : T[K];
74 | };
75 | }
76 |
--------------------------------------------------------------------------------
/packages/mysql/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-mysql",
3 | "version": "3.10.3",
4 | "description": "AWS X-Ray Patcher for MySQL (Javascript)",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Sandra McMullen "
8 | ],
9 | "main": "lib/index.js",
10 | "types": "lib/index.d.ts",
11 | "engines": {
12 | "node": ">= 14.x"
13 | },
14 | "directories": {
15 | "test": "test"
16 | },
17 | "dependencies": {
18 | "@types/mysql": "*"
19 | },
20 | "peerDependencies": {
21 | "aws-xray-sdk-core": "^3.10.3"
22 | },
23 | "scripts": {
24 | "test": "mocha --recursive ./test/ -R spec && tsd",
25 | "test-d": "tsd",
26 | "lint": "eslint .",
27 | "lint:fix": "eslint . --fix"
28 | },
29 | "keywords": [
30 | "amazon",
31 | "api",
32 | "aws",
33 | "mysql",
34 | "xray",
35 | "x-ray",
36 | "x ray"
37 | ],
38 | "license": "Apache-2.0",
39 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/packages/mysql"
40 | }
41 |
--------------------------------------------------------------------------------
/packages/mysql/test-d/index.test-d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-vars */
2 | import * as AWSXRay from 'aws-xray-sdk-core';
3 | import * as MySQL from 'mysql';
4 | import { expectType } from 'tsd';
5 | import { captureMySQL } from '../lib';
6 |
7 | const segment = AWSXRay.getSegment();
8 |
9 | const mysql = captureMySQL(MySQL);
10 |
11 | const config = {};
12 |
13 | const connection: captureMySQL.PatchedConnection = mysql.createConnection(config);
14 | const pool: captureMySQL.PatchedPool = mysql.createPool(config);
15 | const poolCluster: captureMySQL.PatchedPoolCluster = mysql.createPoolCluster(config);
16 |
17 | const queryCallback: MySQL.queryCallback = function(err: MySQL.MysqlError | null, rows: any) {
18 | };
19 |
20 | const getConnectionCallback = function(err: MySQL.MysqlError, conn: captureMySQL.PatchedConnection) {
21 | };
22 |
23 | expectType(connection.query('SELECT * FROM cats', queryCallback));
24 | expectType(connection.query('SELECT * FROM cats', queryCallback, segment));
25 |
26 | expectType(pool.query('SELECT * FROM cats', queryCallback));
27 | expectType(pool.query('SELECT * FROM cats', queryCallback, segment));
28 | expectType(pool.getConnection(getConnectionCallback));
29 |
30 | expectType(poolCluster.getConnection(getConnectionCallback));
31 | expectType(poolCluster.getConnection('pattern', getConnectionCallback));
32 | expectType(poolCluster.getConnection('pattern', 'selector', getConnectionCallback));
33 |
34 | expectType(poolCluster.of('pattern'));
35 | expectType(poolCluster.of('pattern', 'selector'));
36 | expectType(poolCluster.of(null, 'selector'));
37 |
--------------------------------------------------------------------------------
/packages/mysql/test/test_utils.js:
--------------------------------------------------------------------------------
1 | var EventEmitter = require('events');
2 | var util = require('util');
3 |
4 | var TestUtils = {};
5 |
6 | TestUtils.TestEmitter = function TestEmitter() {
7 | EventEmitter.call(this);
8 | };
9 |
10 | util.inherits(TestUtils.TestEmitter, EventEmitter);
11 |
12 | TestUtils.onEvent = function onEvent(event, fcn) {
13 | this.emitter.on(event, fcn.bind(this));
14 | return this;
15 | };
16 |
17 | module.exports = TestUtils;
18 |
--------------------------------------------------------------------------------
/packages/mysql/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/postgres/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/postgres/.npmignore:
--------------------------------------------------------------------------------
1 | .npmignore
2 | node_modules
3 | npm-debug.log
4 | docs
5 | AWSXRay.log
6 | Config
7 |
--------------------------------------------------------------------------------
/packages/postgres/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK Postgres for JavaScript
2 | Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/packages/postgres/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Requirements
3 |
4 | * AWS X-Ray SDK Core
5 | * Postgres 6.1.0 or greater
6 |
7 | ## AWS X-Ray and Postgres
8 |
9 | The AWS X-Ray Postgres package automatically records query information and request
10 | and response data. Simply patch the Postgres package via `capturePostgres` as shown below.
11 |
12 | The AWS X-Ray SDK Core has two modes - `manual` and `automatic`.
13 | Automatic mode uses the `cls-hooked` package and automatically
14 | tracks the current segment and subsegment. This is the default mode.
15 | Manual mode requires that you pass around the segment reference. See the examples below.
16 |
17 | ### Environment variables
18 |
19 | POSTGRES_DATABASE_VERSION Sets additional data for the sql subsegment.
20 | POSTGRES_DRIVER_VERSION Sets additional data for the sql subsegment.
21 |
22 | ### Lambda Example
23 |
24 | ```js
25 | var AWSXRay = require('aws-xray-sdk');
26 | var pg = AWSXRay.capturePostgres(require('pg'));
27 |
28 | ...
29 |
30 | exports.handler = function (event, context, callback) {
31 | // Make postgres queries normally
32 | }
33 | ```
34 |
35 | ### Automatic mode example
36 |
37 | ```js
38 | var AWSXRay = require('aws-xray-sdk-core');
39 | var capturePostgres = require('aws-xray-sdk-postgres');
40 |
41 | var pg = capturePostgres(require('pg'));
42 |
43 | ...
44 |
45 | var client = new pg.Client();
46 |
47 | client.connect(function (err) {
48 | ...
49 |
50 | client.query({name: 'moop', text: 'SELECT $1::text as name'}, ['brianc'], function (err, result) {
51 | //Automatically captures query information and errors (if any)
52 | });
53 | });
54 |
55 | ...
56 |
57 | var pool = new pg.Pool(config);
58 | pool.connect(function(err, client, done) {
59 | if(err) {
60 | return console.error('error fetching client from pool', err);
61 | }
62 | var query = client.query('SELECT * FROM mytable', function(err, result) {
63 | //Automatically captures query information and errors (if any)
64 | });
65 | });
66 | ```
67 |
68 | ### Manual mode example
69 |
70 | ```js
71 | var AWSXRay = require('aws-xray-sdk-core');
72 | var capturePostgres = require('aws-xray-sdk-postgres');
73 |
74 | var pg = capturePostgres(require('pg'));
75 |
76 | ...
77 |
78 | var client = new pg.Client();
79 |
80 | client.connect(function (err) {
81 | ...
82 |
83 | client.query({name: 'moop', text: 'SELECT $1::text as name'}, ['mcmuls'], function (err, result) {
84 | //Automatically captures query information and errors (if any)
85 | });
86 | });
87 |
88 | ...
89 |
90 | var pool = new pg.Pool(config);
91 | pool.connect(function(err, client, done) {
92 | if(err) {
93 | return console.error('error fetching client from pool', err);
94 | }
95 | var query = client.query('SELECT * FROM mytable', function(err, result) {
96 | //Automatically captures query information and errors (if any)
97 | }, segment));
98 | };
99 | ```
--------------------------------------------------------------------------------
/packages/postgres/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from './postgres_p';
2 |
--------------------------------------------------------------------------------
/packages/postgres/lib/index.js:
--------------------------------------------------------------------------------
1 | // Convenience file to require the SDK from the root of the repository
2 | module.exports = require('./postgres_p');
3 |
--------------------------------------------------------------------------------
/packages/postgres/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-postgres",
3 | "version": "3.10.3",
4 | "description": "AWS X-Ray Patcher for Postgres (Javascript)",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Sandra McMullen "
8 | ],
9 | "main": "lib/index.js",
10 | "types": "lib/index.d.ts",
11 | "engines": {
12 | "node": ">= 14.x"
13 | },
14 | "directories": {
15 | "test": "test"
16 | },
17 | "dependencies": {
18 | "@types/pg": "*"
19 | },
20 | "peerDependencies": {
21 | "aws-xray-sdk-core": "^3.10.3"
22 | },
23 | "scripts": {
24 | "test": "mocha --recursive ./test/ -R spec && tsd",
25 | "test-d": "tsd",
26 | "lint": "eslint .",
27 | "lint:fix": "eslint . --fix"
28 | },
29 | "keywords": [
30 | "amazon",
31 | "api",
32 | "aws",
33 | "postgres",
34 | "xray",
35 | "x-ray",
36 | "x ray"
37 | ],
38 | "license": "Apache-2.0",
39 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/packages/postgres"
40 | }
41 |
--------------------------------------------------------------------------------
/packages/postgres/test-d/index.test-d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-vars */
2 | import * as AWSXRay from 'aws-xray-sdk-core';
3 | import * as PG from 'pg';
4 | import { expectType } from 'tsd';
5 | import { capturePostgres } from '../lib';
6 |
7 | const segment = AWSXRay.getSegment();
8 |
9 | const pg = capturePostgres(PG);
10 |
11 | const client: capturePostgres.PatchedClient = new pg.Client();
12 |
13 | client.connect((err: Error) => { });
14 |
15 | const pool = new pg.Pool();
16 |
17 | pool.connect().then((client: capturePostgres.PatchedPoolClient) => { });
18 | pool.connect((err: Error, client: capturePostgres.PatchedPoolClient, done: (release?: any) => void) => { });
19 |
20 | pool.on('error', (err: Error, client: capturePostgres.PatchedPoolClient) => { })
21 | .on('connect', (client: capturePostgres.PatchedPoolClient) => { })
22 | .on('acquire', (client: capturePostgres.PatchedPoolClient) => { })
23 | .on('remove', (client: capturePostgres.PatchedPoolClient) => { });
24 |
25 | function testQuery(client: capturePostgres.PatchedClient | capturePostgres.PatchedPoolClient): void {
26 | const queryCallback = (err: Error, result: PG.QueryResult) => void {
27 | };
28 |
29 | const queryArrayCallback = (err: Error, result: PG.QueryArrayResult) => void {
30 | };
31 |
32 | const queryStream = new PG.Query();
33 | expectType(client.query(queryStream));
34 | expectType(client.query(queryStream, segment));
35 |
36 | const queryArrayConfig: PG.QueryArrayConfig = {
37 | name: 'get-name',
38 | text: 'SELECT $1::text',
39 | values: ['brianc'],
40 | rowMode: 'array',
41 | };
42 | expectType>(client.query(queryArrayConfig));
43 | expectType>(client.query(queryArrayConfig, ['brianc']));
44 | expectType>(client.query(queryArrayConfig, ['brianc'], segment));
45 |
46 | expectType(client.query(queryArrayConfig, queryArrayCallback));
47 | expectType(client.query(queryArrayConfig, queryArrayCallback));
48 | expectType(client.query(queryArrayConfig, queryArrayCallback, segment));
49 |
50 | const queryConfig: PG.QueryConfig = {
51 | name: 'moop',
52 | text: 'SELECT $1::text as name',
53 | values: ['brianc']
54 | };
55 | expectType>(client.query(queryConfig));
56 | expectType>(client.query(queryConfig, ['brianc']));
57 | expectType>(client.query(queryConfig, ['brianc'], segment));
58 |
59 | expectType(client.query(queryConfig, queryCallback));
60 | expectType(client.query(queryConfig, queryCallback));
61 | expectType(client.query(queryConfig, queryCallback, segment));
62 |
63 | const queryText = 'select $1::text as name';
64 | expectType>(client.query(queryText));
65 | expectType>(client.query(queryText, ['brianc']));
66 | expectType>(client.query(queryText, ['brianc'], segment));
67 |
68 | expectType(client.query(queryText, queryCallback));
69 | expectType(client.query(queryText, ['brianc'], queryCallback));
70 | expectType(client.query(queryText, ['brianc'], queryCallback, segment));
71 | }
72 |
--------------------------------------------------------------------------------
/packages/postgres/test/test_utils.js:
--------------------------------------------------------------------------------
1 | var EventEmitter = require('events');
2 | var util = require('util');
3 |
4 | var TestUtils = {};
5 |
6 | TestUtils.TestEmitter = function TestEmitter() {
7 | EventEmitter.call(this);
8 | };
9 |
10 | util.inherits(TestUtils.TestEmitter, EventEmitter);
11 |
12 | TestUtils.onEvent = function onEvent(event, fcn) {
13 | this.emitter.on(event, fcn.bind(this));
14 | return this;
15 | };
16 |
17 | module.exports = TestUtils;
18 |
--------------------------------------------------------------------------------
/packages/postgres/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/restify/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/restify/.npmignore:
--------------------------------------------------------------------------------
1 | .npmignore
2 | node_modules
3 | npm-debug.log
4 | docs
5 | AWSXRay.log
6 | Config
7 |
--------------------------------------------------------------------------------
/packages/restify/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK Restify for JavaScript
2 | Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/packages/restify/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Requirements
3 |
4 | * AWS X-Ray SDK Core (aws-xray-sdk-core)
5 | * Restify 4.3.0 or greater
6 |
7 | ## AWS X-Ray and Restify
8 |
9 | The AWS X-Ray Restify package automatically records information for incoming and outgoing
10 | requests and responses, via the 'enable' function in this package. To configure sampling, dynamic naming, and more see the [set up section](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/core#setup).
11 |
12 | The AWS X-Ray SDK Core has two modes - `manual` and `automatic`.
13 | Automatic mode uses the `cls-hooked` package and automatically
14 | tracks the current segment and subsegment. This is the default mode.
15 | Manual mode requires that you pass around the segment reference.
16 |
17 | In automatic mode, you can get the current segment or subsegment at any time:
18 |
19 | var segment = AWSXRay.getSegment();
20 |
21 | In manual mode, you can get the base segment off of the request object:
22 |
23 | var segment = req.segment;
24 |
25 | //If the restify context plugin is being used, it is placed under 'XRaySegment'
26 | var segment = req.get('XRaySegment');
27 |
28 | ## Middleware usage
29 |
30 | The X-Ray SDK provides a single Restify middleware:
31 | `AWSXRayRestify.enable(, )`. This *must* be added as the last middleware before defining all routes you'd like to have traced, otherwise issues with the `cls-hooked` context may occur. Error capturing will be done automatically.
32 |
33 | ## Automatic mode examples
34 |
35 | For more automatic mode examples, see the [example code](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/core#Automatic-Mode-Examples).
36 |
37 | ```js
38 | var AWSXRayRestify = require('aws-xray-sdk-restify');
39 | var restify = require('restify');
40 | var server = restify.createServer();
41 |
42 | //...
43 |
44 | AWSXRayRestify.enable(server, 'defaultName');
45 |
46 | server.get('/', function (req, res) {
47 | var segment = AWSXRay.getSegment();
48 |
49 | //...
50 |
51 | res.send('hello');
52 | });
53 |
54 | //Error capturing is attached to the server's uncaughtException and after events (for both handled and unhandled errors)
55 | ```
56 |
57 | ## Manual mode examples
58 |
59 | For more manual mode examples, see [manual mode examples](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/core#Manual-Mode-Examples). The X-Ray SDK can be used identically inside Restify routes. Note that you don't have to manually start or close the segments since that is handled by the X-Ray middleware.
60 |
61 | ```js
62 | var AWSXRayRestify = require('aws-xray-sdk-restify');
63 | var restify = require('restify');
64 | var server = restify.createServer();
65 |
66 | //...
67 |
68 | AWSXRayRestify.enable(server, 'defaultName'); //Required at the start of your routes
69 |
70 | server.get('/', function (req, res) {
71 | var segment = req.segment;
72 |
73 | //...
74 |
75 | res.send('hello');
76 | });
77 |
78 | //Error capturing is attached to the server's uncaughtException and after events (for both handled and unhandled errors)
79 | ```
80 |
--------------------------------------------------------------------------------
/packages/restify/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 |
3 | declare module 'restify' {
4 | interface Request {
5 | segment?: AWSXRay.Segment;
6 | }
7 | }
8 |
9 | export * from './restify_mw';
10 |
--------------------------------------------------------------------------------
/packages/restify/lib/index.js:
--------------------------------------------------------------------------------
1 | // Convenience file to require the SDK from the root of the repository
2 | module.exports = require('./restify_mw');
3 |
--------------------------------------------------------------------------------
/packages/restify/lib/restify_mw.d.ts:
--------------------------------------------------------------------------------
1 | import * as restify from 'restify';
2 |
3 | export function enable(server: restify.Server, defaultName: string): void;
4 |
--------------------------------------------------------------------------------
/packages/restify/lib/restify_mw.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Enable X-Ray for Restify module.
3 | *
4 | * Exposes the 'enable' function to enable automated data capturing for a Restify web service. To enable on a Restify server,
5 | * use 'AWSXRayRestify.enable(, )' before defining your routes.
6 | * Use AWSXRay.getSegment() to access the current sub/segment.
7 | * If in manual mode, this appends the Segment object to the request object as req.segment, or in the case of using
8 | * the Restify context plugin, it will be set as 'XRaySegment'.
9 | * @module restify_mw
10 | */
11 |
12 | var AWSXRay = require('aws-xray-sdk-core');
13 |
14 | var mwUtils = AWSXRay.middleware;
15 |
16 | var restifyMW = {
17 |
18 | /**
19 | * Use 'AWSXRayRestify.enable(server, 'defaultName'))' before defining your routes.
20 | * Use AWSXRay.getSegment() to access the current sub/segment.
21 | * If in manual mode, this appends the Segment object to the request object as req.segment, or in the case of using
22 | * the Restify context plugin, it will be set as 'XRaySegment'.
23 | * @param {Server} server - The Restify server instance.
24 | * @param {string} defaultName - The default name for the segment.
25 | * @alias module:restify_mw.enable
26 | */
27 |
28 | enable: function enable(server, defaultName) {
29 | if (!server) {
30 | throw new Error('Restify server instance to enable was not supplied. Please provide a server.');
31 | }
32 |
33 | if (!defaultName || typeof defaultName !== 'string') {
34 | throw new Error('Default segment name was not supplied. Please provide a string.');
35 | }
36 |
37 | mwUtils.setDefaultName(defaultName);
38 | AWSXRay.getLogger().debug('Enabling AWS X-Ray for Restify.');
39 |
40 | var segment;
41 |
42 | server.use(function open(req, res, next) {
43 | segment = mwUtils.traceRequestResponseCycle(req, res);
44 |
45 | if (AWSXRay.isAutomaticMode()) {
46 | var ns = AWSXRay.getNamespace();
47 | ns.bindEmitter(req);
48 | ns.bindEmitter(res);
49 |
50 | ns.run(function() {
51 | AWSXRay.setSegment(segment);
52 |
53 | if (next) {
54 | next();
55 | }
56 | });
57 | } else {
58 | if (req.set) {
59 | req.set('XRaySegment', segment);
60 | } else {
61 | req.segment = segment;
62 | }
63 |
64 | if (next) {
65 | next();
66 | }
67 | }
68 | });
69 |
70 | server.on('uncaughtException', function uncaughtError(req, res, route, err) {
71 | if (segment && err) {
72 | segment.close(err);
73 | }
74 | });
75 |
76 | server.on('after', function handledError(req, res, route, err) {
77 | if (segment && err) {
78 | segment.addError(err);
79 | }
80 | });
81 | }
82 | };
83 |
84 | module.exports = restifyMW;
85 |
--------------------------------------------------------------------------------
/packages/restify/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-restify",
3 | "version": "3.10.3",
4 | "description": "Enables AWS X-Ray for Restify (Javascript)",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Sandra McMullen "
8 | ],
9 | "main": "lib/index.js",
10 | "types": "lib/index.d.ts",
11 | "engines": {
12 | "node": ">= 14.x"
13 | },
14 | "directories": {
15 | "test": "test"
16 | },
17 | "dependencies": {
18 | "@types/restify": "*"
19 | },
20 | "peerDependencies": {
21 | "aws-xray-sdk-core": "^3.10.3"
22 | },
23 | "scripts": {
24 | "test": "mocha --recursive ./test/ -R spec && tsd",
25 | "test-d": "tsd",
26 | "lint": "eslint .",
27 | "lint:fix": "eslint . --fix"
28 | },
29 | "keywords": [
30 | "amazon",
31 | "api",
32 | "aws",
33 | "restify",
34 | "xray",
35 | "x-ray",
36 | "x ray"
37 | ],
38 | "license": "Apache-2.0",
39 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/packages/restify"
40 | }
41 |
--------------------------------------------------------------------------------
/packages/restify/test-d/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 | import * as restify from 'restify';
3 | import { expectType } from 'tsd';
4 | import * as AWSXRayRestify from '../lib';
5 |
6 | const server = restify.createServer();
7 |
8 | AWSXRayRestify.enable(server, 'defaultName');
9 |
10 | server.get('/', function(req, res) {
11 | expectType(req.segment);
12 | res.send('hello');
13 | });
14 |
--------------------------------------------------------------------------------
/packages/restify/test/test_utils.js:
--------------------------------------------------------------------------------
1 | var EventEmitter = require('events');
2 | var util = require('util');
3 |
4 | var TestUtils = {};
5 |
6 | TestUtils.TestEmitter = function TestEmitter() {
7 | EventEmitter.call(this);
8 | };
9 |
10 | util.inherits(TestUtils.TestEmitter, EventEmitter);
11 |
12 | TestUtils.onEvent = function onEvent(event, fcn) {
13 | this.emitter.on(event, fcn.bind(this));
14 | return this;
15 | };
16 |
17 | module.exports = TestUtils;
18 |
--------------------------------------------------------------------------------
/packages/restify/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/test_express/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "mocha": true,
5 | "es6": true
6 | },
7 | "extends": "eslint:recommended",
8 | "rules": {
9 | "indent": [
10 | "error",
11 | 2
12 | ],
13 | "linebreak-style": [
14 | "error",
15 | "unix"
16 | ],
17 | "quotes": [
18 | "error",
19 | "single"
20 | ],
21 | "semi": [
22 | "error",
23 | "always"
24 | ],
25 | "eol-last": [
26 | "error",
27 | "always"
28 | ]
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/test_express/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test-aws-xray-sdk-express",
3 | "private": true,
4 | "version": "3.10.3",
5 | "description": "AWS X-Ray Middleware for Express (Javascript)",
6 | "author": "Amazon Web Services",
7 | "contributors": [
8 | "Sandra McMullen "
9 | ],
10 | "main": "lib/index.js",
11 | "engines": {
12 | "node": ">= 14.x"
13 | },
14 | "directories": {
15 | "test": "test"
16 | },
17 | "scripts": {
18 | "test": "mocha --recursive --exit ./test/ -R spec"
19 | },
20 | "keywords": [
21 | "amazon",
22 | "api",
23 | "aws",
24 | "express",
25 | "xray",
26 | "x-ray",
27 | "x ray"
28 | ],
29 | "license": "Apache-2.0",
30 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/packages/express"
31 | }
32 |
--------------------------------------------------------------------------------
/packages/test_express/test/shaky_stream.js:
--------------------------------------------------------------------------------
1 | var stream = require('stream');
2 | var util = require('util');
3 |
4 | var Readable = stream.Readable;
5 |
6 | var timeoutFn = typeof setTimeoutOrig === 'function' ? setTimeoutOrig : setTimeout;
7 |
8 | /**
9 | * ShakyStream will send data in 2 parts, pausing between parts.
10 | * @param {object} options
11 | * @param {number} options.pauseFor Length of time in ms to pause stream when reading.
12 | */
13 | function ShakyStream(options) {
14 | if (!(this instanceof ShakyStream)) {
15 | return new ShakyStream(options);
16 | }
17 | if (!options.highWaterMark) {
18 | options.highWaterMark = 1024 * 16;
19 | }
20 | this._shakyTime = options.pauseFor;
21 | this._didStart = false;
22 | this._isPaused = false;
23 |
24 | Readable.call(this, options);
25 | }
26 |
27 | util.inherits(ShakyStream, Readable);
28 |
29 | ShakyStream.prototype._read = function _read(_) {
30 | if (!this._didStart) {
31 | this._didStart = true;
32 | this.push(Buffer.from('{"Count":1,"Items":[{"id":{"S":"2016-12-11"},"dateUTC":{"N":"1481494545591"},'));
33 | }
34 | if (this._didStart && this._isPaused) {
35 | return;
36 | } else if (this._didStart) {
37 | this._isPaused = true;
38 | var self = this;
39 | timeoutFn(function() {
40 | self.push(Buffer.from('"javascript":{"M":{"foo":{"S":"bar"},"baz":{"S":"buz"}}}}],"ScannedCount":1}'));
41 | self.push(null);
42 | }, this._shakyTime);
43 | }
44 | };
45 |
46 | module.exports = {ShakyStream: ShakyStream};
47 |
--------------------------------------------------------------------------------
/scripts/cp-with-structure.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ## This script copies a file provided as the first parameter into the destination directory
4 | ## provided by the second parameter, while maintaining the directory structure of the source
5 | ## file. It is similar to 'cp --parents' on Linux, but usable cross-platform
6 | src=(${*: 1})
7 | dest=${*: -1:1}
8 | for filename in $src; do
9 | [ -e "$filename" ] || continue
10 | dirPath=$(dirname "${filename}")
11 | mkdir -p $dest/$dirPath
12 | cp -a $filename $dest/$dirPath
13 | done
14 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/.eslintignore:
--------------------------------------------------------------------------------
1 | index.*
2 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK Fastify for JavaScript
2 | Copyright 2012-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/README.md:
--------------------------------------------------------------------------------
1 | # fastify-xray
2 |
3 | A Fastify plugin to log requests and subsegments through AWSXray.
4 |
5 | ## Setup
6 |
7 | The plugin relies on the AWS credentials being set before being registered, or it will pull them from
8 | `~/.aws/credentials` as per the SDK default.
9 |
10 | For more details on using X-Ray, see the [docs](https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference)
11 |
12 | ## Usage
13 |
14 | Simply register as a normal Fastify plugin
15 |
16 | ```js
17 | const AWSXRay = require('aws-xray-sdk');
18 |
19 | await server.register(require('aws-xray-sdk-fastify'), {
20 | segmentName: 'test segment',
21 | captureAWS: true,
22 | plugins: [AWSXRay.plugins.ECSPlugin],
23 | });
24 | ```
25 |
26 | In automatic mode, you can access the X-Ray segment at any time via the AWSXRay SDK:
27 |
28 | ```js
29 | const AWSXRay = require('aws-xray-sdk-core');
30 |
31 | const segment = AWSXRay.getSegment();
32 | segment.addAnnotation('hitController', 'true');
33 | ```
34 |
35 | In manual mode, you can access the current X-Ray segment from the request object:
36 |
37 | ```js
38 | server.route({
39 | method: 'GET',
40 | path: '/items',
41 | handler: async (request, reply) => {
42 | const segment = request.segment;
43 | segment.addAnnotation('hitController', 'true');
44 |
45 | return {};
46 | },
47 | });
48 | ```
49 |
50 | ### Options
51 |
52 | - `segmentName` Segment name to use in place of default segment name generator (**required**)
53 | - `automaticMode` Specifies that X-Ray automatic mode is in use (default true)
54 | - `plugins` An array of AWS plugins to use (i.e. `[AWSXRay.plugins.EC2Plugin]`)
55 | - `captureAWS` Enables AWS X-Ray to capture AWS calls. This requires having `aws-sdk` installed as a peer dependency
56 | - `captureHTTP` Enables AWS X-Ray to capture all http calls
57 | - `capturePromises` Enables AWS X-Ray to capture all promises
58 | - `logger` Bind AWS X-Ray to compatible logging interface `({ trace, debug, info })`
59 |
60 | ## Sample App
61 |
62 | A naive Fastify server with X-Ray enabled is available in the "sample" directory.
63 | The sample can be started from the sdk_contrib/fastify directory with: `npm run sample`
64 |
65 | Once running, a "hello world" GET endpoint will be available at `http://localhost:3010/`
66 |
67 | The sample will run but throw errors connecting to X-Ray if a local X-Ray daemon is not running.
68 |
69 | For more information on running the XRay daemon locally:
70 | https://docs.aws.amazon.com/xray/latest/devguide/xray-daemon-local.html
71 |
72 | ## Thanks
73 |
74 | Based on the hard work @[AWS X-Ray Express Middleware](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/express) and heavily inspired by [X-Ray Hapi](https://github.com/aws/aws-xray-sdk-node/tree/master/sdk_contrib/hapi)
75 |
76 | ## Contributors
77 |
78 | - [Jorge Vargas](https://github.com/jorgevrgs)
79 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/lib/hooks/on-error.hook.js:
--------------------------------------------------------------------------------
1 | const AWSXRay = require('aws-xray-sdk-core');
2 | const { middleware: mwUtils } = AWSXRay;
3 |
4 | /** @type {import('fastify').onErrorHookHandler} */
5 | module.exports = function onErrorHook(request, reply, error, done) {
6 | const { segment } = request;
7 |
8 | if (segment) {
9 | segment.addError(error);
10 | mwUtils.middlewareLog(
11 | 'Fastify XRay segment encountered an error',
12 | request.url,
13 | segment
14 | );
15 | }
16 |
17 | done();
18 | };
19 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/lib/hooks/on-request.hook.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const AWSXRay = require('aws-xray-sdk-core');
3 | const { middleware: mwUtils } = AWSXRay;
4 |
5 | /** @type {import('fastify').onRequestHookHandler} */
6 | module.exports = function onRequestHook(request, reply, done) {
7 | const req = request.raw;
8 | const res = reply.raw;
9 |
10 | const segment = mwUtils.traceRequestResponseCycle(req, res);
11 |
12 | if (AWSXRay.isAutomaticMode()) {
13 | const ns = AWSXRay.getNamespace();
14 | ns.bindEmitter(req);
15 | ns.bindEmitter(res);
16 |
17 | ns.run(() => {
18 | AWSXRay.setSegment(segment);
19 | done();
20 | });
21 | } else {
22 | request.log.info('Manual mode, skipping segment');
23 |
24 | if (!request.segment) {
25 | request.segment = segment;
26 | } else {
27 | request.log.warn('Request already has a segment, skipping');
28 | }
29 |
30 | done();
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/lib/hooks/on-response.hook.js:
--------------------------------------------------------------------------------
1 | const AWSXRay = require('aws-xray-sdk-core');
2 | const { middleware: mwUtils } = AWSXRay;
3 |
4 | /** @type {import('fastify').onResponseHookHandler} */
5 | module.exports = function onResponseHook(request, reply, done) {
6 | try {
7 | const { segment } = request;
8 |
9 | if (!segment || segment.isClosed()) {
10 | return done();
11 | }
12 |
13 | if (reply.statusCode >= 400) {
14 | if (reply.statusCode === 429) {
15 | segment.addThrottleFlag();
16 | }
17 |
18 | const cause = AWSXRay.utils.getCauseTypeFromHttpStatus(reply.statusCode);
19 |
20 | if (cause) {
21 | segment[cause] = true;
22 | }
23 | }
24 |
25 | if (segment.http) {
26 | segment.http.close(reply.raw);
27 | }
28 |
29 | segment.close();
30 |
31 | mwUtils.middlewareLog(
32 | 'Closed Fastify XRay segment successfully',
33 | request.url,
34 | segment
35 | );
36 |
37 | done();
38 | } catch (error) {
39 | done(error);
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from './plugin';
2 | export { default } from './plugin';
3 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/lib/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./plugin');
2 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/lib/plugin.d.ts:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 | import { FastifyLoggerInstance } from 'fastify';
3 |
4 | declare module 'fastify' {
5 | interface FastifyRequest {
6 | segment?: AWSXRay.Segment;
7 | }
8 | }
9 |
10 | export interface XRayFastifyPluginOptions {
11 | segmentName?: string;
12 | captureAWS: boolean;
13 | captureHTTP: boolean;
14 | capturePromises: boolean;
15 | logger: FastifyLoggerInstance;
16 | automaticMode: boolean;
17 | plugins: AWSXRay.plugins.Plugin[];
18 | }
19 |
20 | declare const xRayFastifyPlugin: FastifyPluginAsync;
21 |
22 | export default xRayFastifyPlugin;
23 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/lib/plugin.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const fp = require('fastify-plugin').default;
3 | const configureAWSXRaySync = require('./private/configure-aws-x-ray-sync');
4 | const onRequestHook = require('./hooks/on-request.hook');
5 | const onResponseHook = require('./hooks/on-response.hook');
6 | const onErrorHook = require('./hooks/on-error.hook');
7 |
8 | /** @type {import('fastify').FastifyPluginAsync} */
9 | const xRayFastifyPlugin = fp(async (fastify, opts) => {
10 | configureAWSXRaySync(fastify, opts);
11 |
12 | fastify.decorateRequest('segment', null);
13 | fastify
14 | .addHook('onRequest', onRequestHook)
15 | .addHook('onResponse', onResponseHook)
16 | .addHook('onError', onErrorHook);
17 | });
18 |
19 | module.exports = xRayFastifyPlugin;
20 |
21 | exports.xRayFastifyPlugin = xRayFastifyPlugin;
22 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/lib/private/configure-aws-x-ray-sync.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const AWSXRay = require('aws-xray-sdk-core');
3 | const { middleware: mwUtils } = AWSXRay;
4 |
5 | /**
6 | *
7 | * @param {import('fastify').FastifyInstance} fastify
8 | * @param {*} opts
9 | */
10 | module.exports = function configureAWSXRaySync(fastify, opts) {
11 | const defaultOptions = {
12 | automaticMode: true,
13 | logger: fastify.log,
14 | };
15 |
16 | const localOptions = { ...defaultOptions, ...opts };
17 |
18 | if (localOptions.logger) {
19 | AWSXRay.setLogger(localOptions.logger);
20 | } else {
21 | AWSXRay.setLogger(fastify.log);
22 | }
23 |
24 | const segmentName = localOptions.segmentName;
25 |
26 | if (!segmentName) {
27 | throw new Error('segmentName is required');
28 | }
29 |
30 | mwUtils.setDefaultName(segmentName);
31 |
32 | if (localOptions.automaticMode) {
33 | AWSXRay.enableAutomaticMode();
34 | } else {
35 | AWSXRay.enableManualMode();
36 | }
37 |
38 | if (localOptions.plugins) {
39 | AWSXRay.config(localOptions.plugins);
40 | }
41 |
42 | if (localOptions.captureAWS) {
43 | AWSXRay.captureAWS(require('aws-sdk'));
44 | }
45 |
46 | if (localOptions.captureHTTP) {
47 | AWSXRay.captureHTTPsGlobal(require('http'), true);
48 | AWSXRay.captureHTTPsGlobal(require('https'), true);
49 | }
50 |
51 | if (localOptions.capturePromises) {
52 | AWSXRay.capturePromise();
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-fastify",
3 | "version": "3.10.3",
4 | "description": "AWS X-Ray plugin for Fastify",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Jorge Vargas "
8 | ],
9 | "main": "lib/index.js",
10 | "files": [
11 | "lib"
12 | ],
13 | "types": "lib/index.d.ts",
14 | "scripts": {
15 | "lint": "eslint .",
16 | "lint:fix": "eslint --fix .",
17 | "test": "mocha --recursive --exit ./test/ -R spec && tsd",
18 | "test-d": "tsd",
19 | "sample": "node sample"
20 | },
21 | "engines": {
22 | "node": ">= 14.x"
23 | },
24 | "peerDependencies": {
25 | "aws-xray-sdk-core": "^3.10.3",
26 | "fastify": "^4.0.1",
27 | "fastify-plugin": "^4.2.0"
28 | },
29 | "keywords": [
30 | "amazon",
31 | "api",
32 | "aws",
33 | "fastify",
34 | "xray",
35 | "x-ray",
36 | "x ray"
37 | ],
38 | "directories": {
39 | "lib": "lib"
40 | },
41 | "license": "Apache-2.0",
42 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/sdk_contrib/fastify"
43 | }
44 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/sample/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Fastify = require('fastify');
4 | const xrayPlugin = require('./xray-plugin');
5 |
6 | const server = Fastify();
7 |
8 | server.register(xrayPlugin.plugin, xrayPlugin.options);
9 | server.route({
10 | method: 'GET',
11 | path: '/',
12 | handler: async (_request, _reply) => {
13 | return 'Hello World!';
14 | },
15 | });
16 |
17 | server
18 | .listen(3000)
19 | .then(() => {
20 | console.log(`server listening on port 3000`);
21 | })
22 | .catch(console.error);
23 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/sample/xray-plugin.js:
--------------------------------------------------------------------------------
1 | const awsXray = require('aws-xray-sdk-core');
2 |
3 | module.exports = {
4 | plugin: require('../lib'),
5 | options: {
6 | segmentName: 'fastify-xray-sample',
7 | captureAWS: true,
8 | captureHTTP: true,
9 | capturePromises: true,
10 | plugins: [awsXray.plugins.ECSPlugin],
11 | logger: console,
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/test-d/index.test-d.ts.js:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 | import * as Fastify from 'fastify';
3 | import { expectType } from 'tsd';
4 |
5 | const server = Fastify();
6 | server.register(require('../lib'));
7 |
8 | server.route({
9 | method: 'GET',
10 | path: '/',
11 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
12 | handler: async (request, _reply) => {
13 | (expectType < AWSXRay.Segment) | (undefined > request.segment);
14 | return { data: 'ok' };
15 | },
16 | });
17 |
--------------------------------------------------------------------------------
/sdk_contrib/fastify/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "extends": [
6 | "plugin:mocha/recommended"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK Fetch for JavaScript
2 | Copyright 2012-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/README.md:
--------------------------------------------------------------------------------
1 | # fetch-xray
2 |
3 | A patcher for AWSXRay to support fetch, implemented via either the [node-fetch module](https://www.npmjs.com/package/node-fetch) or the built-in
4 | global fetch support starting with [NodeJS 18](https://nodejs.org/en/blog/announcements/v18-release-announce).
5 |
6 | ## Usage
7 |
8 | ```js
9 | const { captureFetchGlobal } = require('aws-xray-sdk-fetch');
10 |
11 | // To use globally defined fetch (available in NodeJS 18+)
12 | const fetch = captureFetchGlobal();
13 | const result = await fetch('https://foo.com');
14 |
15 | // To use node-fetch module
16 | const { captureFetchModule } = require('aws-xray-sdk-fetch');
17 | const fetchModule = require('node-fetch');
18 | const fetch = captureFetchModule(fetchModule); // Note, first parameter *must* be the node-fetch module
19 | const result = await fetch('https://foo.com');
20 | ```
21 |
22 | There are two optional parameters you can pass when calling `captureFetchGlobal` / `captureFetchModule`:
23 |
24 | * **downstreamXRayEnabled**: when True, adds a "traced:true" property to the subsegment so the AWS X-Ray service expects a corresponding segment from the downstream service (default = False)
25 | * **subsegmentCallback**: a callback that is called with the subsegment, the fetch request, the fetch response and any error issued, allowing custom annotations and metadata to be added
26 |
27 | TypeScript bindings for the capture functions are included.
28 |
29 | ## Testing
30 |
31 | Unit and integration tests can be run using `npm run test`. Typings file tess can be run using `npm run test-d`.
32 |
33 | ## Errata
34 |
35 | 1. This package CommonJS to conform with the rest of the AWSXRay codebase. As such, it is incompatible with node-fetch 3, which is ESM only. As such, it is written
36 | to be compatible with [node-fetch version 2](https://www.npmjs.com/package/node-fetch#CommonJS), which should still receive critical fixes. If you are using global
37 | fetch (available in NodeJS 18+) then this isn't an issue for you.
38 |
39 | 2. This package is designed working under the assumption that the NodeJS implementation of fetch is compatible with node-fetch, albeit with its own separate,
40 | built-in typings. If NodeJS takes fetch in a different direction (breaks compatibility) then that would most likely break this package. There is no indication that
41 | I could find that this will happen, but throwing it out there "just in case".
42 |
43 | ## Contributors
44 |
45 | - [Jason Terando](https://github.com/jasonterando)
46 | - [Bernd Fuhrmann](https://github.com/berndfuhrmann)
47 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/lib/fetch_p.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-vars */
2 | import AWSXRay from 'aws-xray-sdk-core';
3 | import * as fetchModule from 'node-fetch';
4 |
5 | type FetchModuleType = typeof fetchModule;
6 |
7 | type fetchModuleFetch = (url: URL | fetchModule.RequestInfo, init?: fetchModule.RequestInit | undefined) => Promise;
8 |
9 | export function captureFetchGlobal(
10 | downstreamXRayEnabled?: boolean,
11 | subsegmentCallback?: (subsegment: AWSXRay.Subsegment, req: Request, res: Response | null, error?: Error | undefined) => void):
12 | typeof globalThis.fetch;
13 |
14 | export function captureFetchModule(
15 | fetch: FetchModuleType,
16 | downstreamXRayEnabled?: boolean,
17 | subsegmentCallback?: (subsegment: AWSXRay.Subsegment, req: fetchModule.Request, res: fetchModule.Response | null, error?: Error | undefined) => void):
18 | (url: URL | fetchModule.RequestInfo, init?: fetchModule.RequestInit | undefined) => Promise;
19 |
20 | export function enableCapture(
21 | fetch: Fetch,
22 | request: Request,
23 | downstreamXRayEnabled?: boolean,
24 | subsegmentCallback?: (subsegment: AWSXRay.Subsegment, req: Request, res: Response | null, error?: Error | undefined) => void
25 | ): Fetch;
26 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/lib/subsegment_fetch.d.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'node-fetch';
2 |
3 | declare module 'aws-xray-sdk-core' {
4 | interface Subsegment {
5 | /**
6 | * Extends Subsegment to append remote request data to subsegment, similar to what
7 | * Subsegment.prototype.addRemoteRequestData does in core/lib/segments/attributes/subsegment.js
8 | * @param {Fetch Request} request
9 | * @param {Fetch Request or null|undefined} response
10 | * @param {boolean} downstreamXRayEnabled
11 | */
12 | addFetchRequestData(
13 | request: Request,
14 | response: Response,
15 | downstreamXRayEnabled: boolean): void;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/lib/subsegment_fetch.js:
--------------------------------------------------------------------------------
1 | const {Subsegment} = require('aws-xray-sdk-core');
2 |
3 | /**
4 | * Extends Subsegment to append remote request data to subsegment, similar to what
5 | * Subsegment.prototype.addRemoteRequestData does in core/lib/segments/attributes/subsegment.js
6 | * @param {Subsegment} subsegment
7 | * @param {Fetch Request} request
8 | * @param {Fetch Request or null|undefined} response
9 | * @param {boolean} downstreamXRayEnabled
10 | */
11 | Subsegment.prototype.addFetchRequestData = function addFetchRequestData(request, response, downstreamXRayEnabled) {
12 | this.http = {
13 | request: {
14 | url: request.url?.toString() ?? '',
15 | method: request.method ?? ''
16 | }
17 | };
18 |
19 | if (downstreamXRayEnabled) {
20 | this.traced = true;
21 | }
22 |
23 | if (response) {
24 | this.http.response = {
25 | status: response.status
26 | };
27 | if (response.headers) {
28 | const clength = response.headers.get('content-length');
29 | if (clength) {
30 | const v = parseInt(clength);
31 | if (! Number.isNaN(v)) {
32 | this.http.response.content_length = v;
33 | }
34 | }
35 | }
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-fetch",
3 | "version": "3.10.3",
4 | "description": "AWS X-Ray plugin for node-fetch",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Jason Terando "
8 | ],
9 | "main": "lib/fetch_p.js",
10 | "files": [
11 | "lib"
12 | ],
13 | "types": "lib/fetch_p.d.ts",
14 | "scripts": {
15 | "lint": "eslint .",
16 | "lint:fix": "eslint --fix .",
17 | "test": "mocha --recursive --exit ./test/ -R spec && tsd",
18 | "test-d": "tsd"
19 | },
20 | "engines": {
21 | "node": ">= 14.x"
22 | },
23 | "peerDependencies": {
24 | "aws-xray-sdk-core": "^3.10.3"
25 | },
26 | "keywords": [
27 | "amazon",
28 | "api",
29 | "aws",
30 | "fetch",
31 | "node-fetch",
32 | "xray",
33 | "x-ray",
34 | "x ray"
35 | ],
36 | "directories": {
37 | "lib": "lib"
38 | },
39 | "license": "Apache-2.0",
40 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/sdk_contrib/fetch",
41 | "devDependencies": {
42 | "@types/node-fetch": "^2.6.4",
43 | "chai-as-promised": "=7.1.1",
44 | "node-fetch": "^2.6.11",
45 | "ts-expect": "^1.3.0"
46 | },
47 | "dependencies": {
48 | "tsd": "^0.28.1"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/test-d/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import AWSXRay from 'aws-xray-sdk-core';
2 | import * as fetchModule from 'node-fetch';
3 | import { expectType } from 'ts-expect';
4 | import { captureFetchGlobal, captureFetchModule } from '../lib/fetch_p';
5 |
6 | type ModuleFetch = (url: URL | fetchModule.RequestInfo, init?: fetchModule.RequestInit | undefined) => Promise;
7 |
8 | if (globalThis.fetch !== undefined) {
9 | function fetchGlobalCallback(subsegment: AWSXRay.Subsegment, req: Request, res: Response | null, error?: Error | undefined) {
10 | console.log({ subsegment, req, res, error });
11 | }
12 |
13 | expectType(captureFetchGlobal());
14 | expectType(captureFetchGlobal(true));
15 | expectType(captureFetchGlobal(false));
16 | expectType(captureFetchGlobal(true, fetchGlobalCallback));
17 | expectType(captureFetchGlobal(false, fetchGlobalCallback));
18 | }
19 |
20 | function fetchModuleCallback(subsegment: AWSXRay.Subsegment, req: fetchModule.Request, res: fetchModule.Response | null, error?: Error | undefined) {
21 | console.log({ subsegment, req, res, error });
22 | }
23 |
24 | expectType(captureFetchModule(fetchModule));
25 | expectType(captureFetchModule(fetchModule, true));
26 | expectType(captureFetchModule(fetchModule, false));
27 | expectType(captureFetchModule(fetchModule, true, fetchModuleCallback));
28 | expectType(captureFetchModule(fetchModule, false, fetchModuleCallback));
29 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/test/global.js:
--------------------------------------------------------------------------------
1 | const AWSXray = require('aws-xray-sdk-core');
2 | const sinon = require('sinon');
3 |
4 | const sandbox = sinon.createSandbox();
5 | const spyLogWarn = sandbox.spy();
6 | const spyLogInfo = sandbox.spy();
7 |
8 | sandbox.stub(AWSXray, 'getLogger').returns({
9 | warn: spyLogWarn,
10 | info: spyLogInfo
11 | });
12 |
13 | const stubResolveSegment = sandbox.stub(AWSXray, 'resolveSegment');
14 | const stubResolveManualSegmentParams = sandbox.stub(AWSXray, 'resolveManualSegmentParams');
15 | const stubIsAutomaticMode = sandbox.stub(AWSXray, 'isAutomaticMode');
16 |
17 | afterEach(() => {
18 | sandbox.resetHistory();
19 | // We have to create and re-create this stub here because promise_p.test doesn't use sandboxes
20 | // stubIsAutomaticMode.returns(true);
21 | });
22 |
23 | module.exports.AWSXray = AWSXray;
24 | module.exports.sandbox = sandbox;
25 | module.exports.spyLogWarn = spyLogWarn;
26 | module.exports.spyLogInfo = spyLogInfo;
27 | module.exports.stubResolveSegment = stubResolveSegment;
28 | module.exports.stubResolveManualSegmentParams = stubResolveManualSegmentParams;
29 | module.exports.stubIsAutomaticMode = stubIsAutomaticMode;
30 |
--------------------------------------------------------------------------------
/sdk_contrib/fetch/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es2019",
5 | "strict": true,
6 | "strictNullChecks": true,
7 | "declaration": false,
8 | "esModuleInterop": true,
9 | "skipLibCheck": true,
10 | "allowJs": true,
11 | "checkJs": false,
12 | "outDir": "dist",
13 | },
14 | "include": ["lib", "test", "test_async"],
15 | "exclude": ["dist", "node_modules"]
16 | }
17 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/.eslintignore:
--------------------------------------------------------------------------------
1 | index.*
2 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK Hapi for JavaScript
2 | Copyright 2012-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/README.md:
--------------------------------------------------------------------------------
1 | # hapi-xray
2 | A HapiJS plugin to log requests and subsegments through AWSXray.
3 |
4 | ## Setup
5 |
6 | The plugin relies on the AWS credentials being set before being registered, or it will pull them from
7 | `~/.aws/credentials` as per the SDK default.
8 |
9 | For more details on using X-Ray, see the [docs](https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference)
10 |
11 | ## Usage
12 |
13 | Simply register as a normal Hapi plugin
14 |
15 | ```js
16 |
17 | const AWSXRay = require('aws-xray-sdk');
18 |
19 | await server.register({
20 | plugin: require('aws-xray-sdk-hapi'),
21 | options: {
22 | captureAWS: true,
23 | plugins: [AWSXRay.plugins.ECSPlugin]
24 | }
25 | });
26 | ```
27 |
28 | In automatic mode, you can access the X-Ray segment at any time via the AWSXRay SDK:
29 | ```js
30 | const AWSXRay = require('aws-xray-sdk-core');
31 |
32 | const segment = AWSXRay.getSegment();
33 | segment.addAnnotation('hitController', 'true');
34 | ```
35 |
36 | In manual mode, you can access the current X-Ray segment from the request object:
37 |
38 | ```js
39 | server.route({
40 | method: 'GET',
41 | path: '/items',
42 | handler: async (request, h) => {
43 | const segment = request.segment;
44 | segment.addAnnotation('hitController', 'true');
45 |
46 | return {};
47 | }
48 | });
49 | ```
50 |
51 | ### Options
52 | - `segmentName` Segment name to use in place of default segment name generator
53 | - `automaticMode` Specifies that X-Ray automatic mode is in use (default true)
54 | - `plugins` An array of AWS plugins to use (i.e. `[AWSXRay.plugins.EC2Plugin]`)
55 | - `captureAWS` Enables AWS X-Ray to capture AWS calls. This requires having `aws-sdk` installed as a peer dependency
56 | - `captureHTTP` Enables AWS X-Ray to capture all http calls
57 | - `capturePromises` Enables AWS X-Ray to capture all promises
58 | - `logger` Bind AWS X-Ray to compatible logging interface `({ trace, debug, info })`
59 |
60 | ## Sample App
61 | A naive hapi server with X-Ray enabled is available in the "sample" directory.
62 | The sample can be started from the sdk_contrib/hapi directory with: `npm run sample`
63 |
64 | Once running, a "hello world" GET endpoint will be available at `http://localhost:3010/`
65 |
66 | The sample will run but throw errors connecting to X-Ray if a local X-Ray daemon is not running.
67 |
68 | For more information on running the XRay daemon locally:
69 | https://docs.aws.amazon.com/xray/latest/devguide/xray-daemon-local.html
70 |
71 | ## Thanks
72 |
73 | Based on the hard work @[AWS X-Ray Express Middleware](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/express)
74 |
75 | ## Contributors
76 |
77 | - [Alex Coulcher](https://github.com/moonthug)
78 | - [Eric Swann](https://github.com/eric-swann-q2)
79 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 |
3 | declare global {
4 | namespace Hapi {
5 | interface Request {
6 | segment?: AWSXRay.Segment;
7 | }
8 | }
9 | }
10 |
11 | export * from './index';
12 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/lib/index.js:
--------------------------------------------------------------------------------
1 | const { register } = require('./plugin');
2 | const pjson = require('../package');
3 | /**
4 | *
5 | * NPM Module
6 | */
7 | module.exports = {
8 | /**
9 | *
10 | * HAPI Plugin
11 | */
12 | plugin: {
13 | name: 'hapi-xray',
14 | register,
15 | once: true,
16 | version: pjson.version
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/lib/plugin.js:
--------------------------------------------------------------------------------
1 | const xray = require('./xray');
2 |
3 | module.exports = {
4 | register: (server, options) => {
5 | xray.setup(options);
6 |
7 | server.ext({
8 | type: 'onRequest',
9 | method: xray.handleRequest
10 | });
11 |
12 | server.events.on(
13 | { name: 'request', channels: 'error' },
14 | (request, event) => {
15 | xray.handleError(request, event.error);
16 | }
17 | );
18 |
19 | server.events.on('response', xray.handleResponse);
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-hapi",
3 | "version": "3.10.3",
4 | "description": "AWS X-Ray plugin for Hapi.JS",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Alex Coulcher",
8 | "Eric Swann "
9 | ],
10 | "main": "lib/index.js",
11 | "files": [
12 | "lib"
13 | ],
14 | "types": "lib/index.d.ts",
15 | "scripts": {
16 | "lint": "eslint .",
17 | "lint:fix": "eslint --fix .",
18 | "test": "mocha --recursive --exit ./test/ -R spec && tsd",
19 | "test-d": "tsd",
20 | "sample": "node sample"
21 | },
22 | "engines": {
23 | "node": ">= 14.x"
24 | },
25 | "peerDependencies": {
26 | "@hapi/hapi": ">=18.x",
27 | "aws-xray-sdk-core": "^3.10.3"
28 | },
29 | "keywords": [
30 | "amazon",
31 | "api",
32 | "aws",
33 | "hapi",
34 | "xray",
35 | "x-ray",
36 | "x ray"
37 | ],
38 | "directories": {
39 | "lib": "lib"
40 | },
41 | "license": "Apache-2.0",
42 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/sdk_contrib/hapi"
43 | }
44 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/sample/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Hapi = require('@hapi/hapi');
4 | const xrayPlugin = require('./xray-plugin');
5 |
6 | const init = async () => {
7 | const server = Hapi.server({
8 | port: 3010,
9 | host: 'localhost',
10 | });
11 |
12 | server.route({
13 | method: 'GET',
14 | path: '/',
15 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
16 | handler: (request, h) => {
17 | return 'Hello World!';
18 | },
19 | });
20 |
21 | await server.register(xrayPlugin);
22 |
23 | await server.start();
24 | console.log('Server running on %s', server.info.uri);
25 | };
26 |
27 | process.on('unhandledRejection', (err) => {
28 | console.log(err);
29 | process.exit(1);
30 | });
31 |
32 | init();
33 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/sample/xray-plugin.js:
--------------------------------------------------------------------------------
1 | const awsXray = require('aws-xray-sdk-core');
2 | const hapiXray = require('../lib');
3 |
4 | module.exports = {
5 | plugin: hapiXray,
6 | options: {
7 | segmentName: 'hapi-xray-sample',
8 | captureAWS: true,
9 | captureHTTP: true,
10 | capturePromises: true,
11 | plugins: [awsXray.plugins.ECSPlugin],
12 | logger: console,
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/test-d/index.test-d.ts.js:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 | import * as hapi from '@hapi/hapi';
3 | import { expectType } from 'tsd';
4 | import * as hapiXray from '../lib';
5 |
6 | const server = new hapi.Server();
7 | hapiXray.plugin.register(server);
8 |
9 | server.route({
10 | method: 'GET',
11 | path: '/',
12 | handler: (request) => {
13 | (expectType < AWSXRay.Segment) | (undefined > request.segment);
14 | return { data: 'ok' };
15 | },
16 | });
17 |
--------------------------------------------------------------------------------
/sdk_contrib/hapi/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "extends": [
6 | "plugin:mocha/recommended"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/NOTICE.txt:
--------------------------------------------------------------------------------
1 | AWS X-Ray SDK Koa for JavaScript
2 | Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes software developed at
5 | Amazon Web Services, Inc. (http://aws.amazon.com/).
6 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Requirements
3 |
4 | AWS X-Ray SDK Core (aws-xray-sdk-core)
5 | Koa 2.x or greater
6 |
7 | ## AWS X-Ray and Koa
8 |
9 | The AWS X-Ray Koa package automatically records information for incoming and outgoing
10 | requests and responses, via the middleware functions in this package. To configure sampling,
11 | dynamic naming, and more see the [set up section](https://github.com/aws/aws-xray-sdk-node/tree/master/packages/core#setup).
12 |
13 | The AWS X-Ray SDK Core has two modes - `manual` and `automatic`.
14 | Automatic mode uses the `cls-hooked` package and automatically
15 | tracks the current segment and subsegment. This is the default mode.
16 | Manual mode requires that you pass around the segment reference.
17 |
18 | In automatic mode, you can get the current segment/subsegment at any time:
19 | var segment = AWSXRay.getSegment();
20 |
21 | In manual mode, you can get the base segment off of the context object:
22 | var segment = ctx.segment;
23 |
24 | ## Middleware Usage
25 |
26 | The Koa X-Ray SDK provides one middlewares: `xrayKoa.openSegment()`.
27 | This middleware will wrap all of the defined routes that you'd like to trace.
28 | In automatic mode, the `openSegment` middleware *must* be the last middleware added
29 | before defining routes, otherwise issues with the `cls-hooked`
30 | context may occur.
31 |
32 | ## Automatic mode examples
33 | ```js
34 | var AWSXRay = require('aws-xray-sdk-core');
35 | var xrayKoa = require('aws-xray-sdk-koa2');
36 | var app = new Koa();
37 |
38 | //...
39 |
40 | app.use(xrayKoa.openSegment('defaultName'));
41 |
42 | router.get('/myRoute', (ctx) => {
43 | const segment = AWSXRay.getSegment();
44 | //Do whatever
45 | });
46 | ```
47 |
48 | ## Manual mode examples
49 | ```js
50 | var AWSXRay = require('aws-xray-sdk-core');
51 | var xrayKoa = require('aws-xray-sdk-koa2');
52 | var app = new Koa();
53 |
54 | //...
55 |
56 | var AWSXRay = require('aws-xray-sdk');
57 |
58 | app.use(xrayKoa.openSegment('defaultName')); //Required at the start of your routes
59 |
60 | router.get('/myRoute', (ctx) => {
61 | const segment = ctx.segment;
62 | //Do whatever
63 | });
64 | ```
65 |
66 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 |
3 | declare global {
4 | namespace Koa {
5 | interface Request {
6 | segment?: AWSXRay.Segment;
7 | }
8 | }
9 | }
10 |
11 | export * from './koa_mw';
12 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/lib/index.js:
--------------------------------------------------------------------------------
1 | // Convenience file to require the SDK from the root of the repository
2 | module.exports = require('./koa_mw');
3 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/lib/koa_mw.d.ts:
--------------------------------------------------------------------------------
1 | import { Middleware } from 'koa';
2 |
3 | export function openSegment(defaultName: string): Middleware;
4 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/lib/koa_mw.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Koa middleware module.
3 | *
4 | * Exposes Koa middleware functions to enable automated data capturing on a web service.
5 | * To enable on a Node.js/Koa application, use 'app.use(AWSXRayKoa.openSegment())' before defining your routes.
6 | * Use AWSXRay.getSegment() to access the current sub/segment.
7 | * Otherwise, for manual mode this appends the Segment object to the context object as ctx.segment.
8 | * @module koa_mw
9 | */
10 |
11 | const AWSXRay = require('aws-xray-sdk-core');
12 |
13 | const mwUtils = AWSXRay.middleware;
14 | const IncomingRequestData = mwUtils.IncomingRequestData;
15 | const Segment = AWSXRay.Segment;
16 |
17 | const koaMW = {
18 | /**
19 | * Use 'app.use(AWSXRayKoa.openSegment())' before defining your routes.
20 | * Use AWSXRay.getSegment() to access the current sub/segment.
21 | * Otherwise, for manual mode, this appends the Segment object to the request object as ctx.segment.
22 | * @param {string} defaultName - The default name for the segment.
23 | * @alias module:koa_mw.openSegment
24 | * @returns {function}
25 | */
26 |
27 | openSegment: function openSegment(defaultName) {
28 | if (!defaultName || typeof defaultName !== 'string') {
29 | throw new Error(
30 | 'Default segment name was not supplied. Please provide a string.'
31 | );
32 | }
33 |
34 | mwUtils.setDefaultName(defaultName);
35 |
36 | return async (ctx, next) => {
37 | const amznTraceHeader = mwUtils.processHeaders(ctx);
38 | const name = mwUtils.resolveName(ctx.host);
39 | const segment = new Segment(
40 | name,
41 | amznTraceHeader.Root || amznTraceHeader.root,
42 | amznTraceHeader.Parent || amznTraceHeader.parent
43 | );
44 |
45 | mwUtils.resolveSampling(amznTraceHeader, segment, ctx);
46 | segment.addIncomingRequestData(new IncomingRequestData(ctx.req));
47 |
48 | mwUtils.middlewareLog('Starting koa segment', ctx.url, segment);
49 |
50 | if (AWSXRay.isAutomaticMode()) {
51 | const ns = AWSXRay.getNamespace();
52 | ns.bindEmitter(ctx.req);
53 | ns.bindEmitter(ctx.res);
54 |
55 | return ns.runAndReturn(async function () {
56 | let error;
57 | AWSXRay.setSegment(segment);
58 | ctx.segment = segment;
59 | try {
60 | if (next) {
61 | await next();
62 | }
63 | } catch (err) {
64 | error = err;
65 | }
66 | exports._processResponse(ctx, segment, error);
67 | });
68 | } else {
69 | let error;
70 | ctx.segment = segment;
71 | try {
72 | if (next) {
73 | await next();
74 | }
75 | } catch (err) {
76 | error = err;
77 | }
78 | exports._processResponse(ctx, segment, error);
79 | }
80 | };
81 | },
82 | };
83 |
84 | exports._processResponse = (ctx, segment, err) => {
85 | if (ctx.status >= 400) {
86 | if (ctx.status === 429) {
87 | segment.addThrottleFlag();
88 | }
89 | segment[AWSXRay.utils.getCauseTypeFromHttpStatus(ctx.status)] = true;
90 | }
91 |
92 | if (segment.http && ctx.res) {
93 | segment.http.close(ctx.res);
94 | }
95 | segment.close(err);
96 | const message = err ? 'Closed koa segment with error' : 'Closed koa segment successfully';
97 | mwUtils.middlewareLog(message, ctx.url, segment);
98 | if (err) {
99 | throw err;
100 | }
101 | };
102 |
103 | module.exports = koaMW;
104 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-xray-sdk-koa2",
3 | "version": "3.10.3",
4 | "description": "AWS X-Ray Middleware for koa (Javascript)",
5 | "author": "Amazon Web Services",
6 | "contributors": [
7 | "Sandra McMullen ",
8 | "Eric Swann "
9 | ],
10 | "main": "lib/index.js",
11 | "files": [
12 | "lib"
13 | ],
14 | "types": "lib/index.d.ts",
15 | "scripts": {
16 | "lint": "eslint .",
17 | "lint:fix": "eslint --fix .",
18 | "test": "mocha --recursive --exit ./test/ -R spec && tsd",
19 | "test-d": "tsd"
20 | },
21 | "engines": {
22 | "node": ">= 14.x"
23 | },
24 | "peerDependencies": {
25 | "aws-xray-sdk-core": "^3.10.3",
26 | "koa": "^2.0.0"
27 | },
28 | "keywords": [
29 | "amazon",
30 | "api",
31 | "aws",
32 | "koa",
33 | "xray",
34 | "x-ray",
35 | "x ray"
36 | ],
37 | "directories": {
38 | "lib": "lib"
39 | },
40 | "license": "Apache-2.0",
41 | "repository": "https://github.com/aws/aws-xray-sdk-node/tree/master/sdk_contrib/koa"
42 | }
43 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/test-d/index.test-d.ts.js:
--------------------------------------------------------------------------------
1 | import * as AWSXRay from 'aws-xray-sdk-core';
2 | import * as koa from 'koa';
3 | import { expectType } from 'tsd';
4 | import * as xrayKoa from '../lib';
5 |
6 | const app = new koa();
7 |
8 | app.use(xrayKoa.openSegment('defaultName'));
9 |
10 | app.get('/', function (ctx) {
11 | (expectType < AWSXRay.Segment) | (undefined > ctx.segment);
12 | ctx.res.render('index');
13 | });
14 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "extends": [
6 | "plugin:mocha/recommended"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/sdk_contrib/koa/test/test_utils.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('events');
2 | const util = require('util');
3 |
4 | const TestUtils = {};
5 |
6 | TestUtils.TestEmitter = function TestEmitter() {
7 | EventEmitter.call(this);
8 | };
9 |
10 | util.inherits(TestUtils.TestEmitter, EventEmitter);
11 |
12 | TestUtils.onEvent = function onEvent(event, fcn) {
13 | this.emitter.on(event, fcn.bind(this));
14 | return this;
15 | };
16 |
17 | module.exports = TestUtils;
18 |
--------------------------------------------------------------------------------
/smoke_test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test-smoke",
3 | "version": "1.0.0",
4 | "description": "Smoke Test for AWS XRay SDK",
5 | "main": "index.js",
6 | "engines": {
7 | "node": ">= 14.x"
8 | },
9 | "dependencies": {
10 | "aws-xray-sdk": "*",
11 | "chai": "^4.3.4",
12 | "mocha": "^8.0.1",
13 | "typescript": "4.2.4"
14 | },
15 | "scripts": {
16 | "test-smoke": "mocha ./test/ -R spec"
17 | },
18 | "license": "Apache-2.0"
19 | }
20 |
--------------------------------------------------------------------------------
/smoke_test/test/smoke.test.js:
--------------------------------------------------------------------------------
1 | const assert = require('chai').assert;
2 | const AWSXRay = require('aws-xray-sdk');
3 | const Segment = AWSXRay.Segment;
4 |
5 | describe('Smoke Test', () => {
6 | it('Segment', () => {
7 | const segment = new Segment('test');
8 | assert.equal(segment.name, 'test');
9 | });
10 | });
11 |
--------------------------------------------------------------------------------