├── .asf.yaml ├── .dockerignore ├── .editorconfig ├── .eslintrc.js ├── .file-headerrc ├── .github └── workflows │ ├── build.yaml │ ├── license.yaml │ └── test.yaml ├── .gitignore ├── .gitmodules ├── .husky └── pre-commit ├── .licenserc.yaml ├── .prettierrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── dist ├── LICENSE ├── NOTICE └── licenses │ ├── LICENSE-grpc_tools_node_protoc_ts.txt │ ├── LICENSE-on-finished.txt │ ├── LICENSE-protobuf-js.txt │ ├── LICENSE-semver.txt │ ├── LICENSE-tslib.txt │ ├── LICENSE-uuid.txt │ └── LICENSE-winston.txt ├── docs └── How-to-release.md ├── jest.config.js ├── jest.setup.js ├── package-lock.json ├── package.json ├── scripts └── protoc.sh ├── src ├── Log.ts ├── Tag.ts ├── agent │ └── protocol │ │ ├── Protocol.ts │ │ └── grpc │ │ ├── AuthInterceptor.ts │ │ ├── GrpcProtocol.ts │ │ ├── SegmentObjectAdapter.ts │ │ └── clients │ │ ├── Client.ts │ │ ├── HeartbeatClient.ts │ │ └── TraceReportClient.ts ├── annotations │ └── index.ts ├── aws │ ├── AWSLambdaGatewayAPIHTTP.ts │ ├── AWSLambdaGatewayAPIREST.ts │ ├── AWSLambdaTriggerPlugin.ts │ └── SDK2.ts ├── azure │ └── AzureHttpTriggerPlugin.ts ├── config │ └── AgentConfig.ts ├── core │ ├── OptionMethods.ts │ ├── PluginInstaller.ts │ └── SwPlugin.ts ├── egg │ └── index.ts ├── index.ts ├── lib │ └── EventEmitter.ts ├── logging │ └── index.ts ├── plugins │ ├── AMQPLibPlugin.ts │ ├── AWS2DynamoDBPlugin.ts │ ├── AWS2LambdaPlugin.ts │ ├── AWS2SNSPlugin.ts │ ├── AWS2SQSPlugin.ts │ ├── AxiosPlugin.ts │ ├── ExpressPlugin.ts │ ├── HttpPlugin.ts │ ├── IORedisPlugin.ts │ ├── MongoDBPlugin.ts │ ├── MongoosePlugin.ts │ ├── MySQL2Plugin.ts │ ├── MySQLPlugin.ts │ └── PgPlugin.ts ├── trace │ ├── Component.ts │ ├── ID.ts │ ├── NewID.ts │ ├── context │ │ ├── CarrierItem.ts │ │ ├── Context.ts │ │ ├── ContextCarrier.ts │ │ ├── ContextManager.ts │ │ ├── DummyContext.ts │ │ ├── Segment.ts │ │ ├── SegmentRef.ts │ │ └── SpanContext.ts │ └── span │ │ ├── DummySpan.ts │ │ ├── EntrySpan.ts │ │ ├── ExitSpan.ts │ │ ├── LocalSpan.ts │ │ └── Span.ts └── tsconfig.json ├── tests ├── build │ ├── Dockerfile │ ├── main.ts │ ├── package.json │ └── tsconfig.json ├── plugins │ ├── axios │ │ ├── client.ts │ │ ├── docker-compose.yml │ │ ├── expected.data.yaml │ │ ├── server.ts │ │ └── test.ts │ ├── common │ │ ├── Dockerfile.agent │ │ └── base-compose.yml │ ├── express │ │ ├── client.ts │ │ ├── docker-compose.yml │ │ ├── expected.data.yaml │ │ ├── server.ts │ │ └── test.ts │ ├── http │ │ ├── client.ts │ │ ├── docker-compose.yml │ │ ├── expected.data.yaml │ │ ├── server.ts │ │ └── test.ts │ ├── ioredis │ │ ├── client.ts │ │ ├── docker-compose.yml │ │ ├── expected.data.yaml │ │ ├── server.ts │ │ └── test.ts │ ├── mongodb │ │ ├── client.ts │ │ ├── docker-compose.yml │ │ ├── expected.data.yaml │ │ ├── init │ │ │ └── init.js │ │ ├── server.ts │ │ └── test.ts │ ├── mongoose │ │ ├── client.ts │ │ ├── docker-compose.yml │ │ ├── expected.data.yaml │ │ ├── init │ │ │ └── init.js │ │ ├── server.ts │ │ └── test.ts │ ├── mysql │ │ ├── client.ts │ │ ├── docker-compose.yml │ │ ├── expected.data.yaml │ │ ├── init │ │ │ └── init.sql │ │ ├── server.ts │ │ └── test.ts │ ├── mysql2 │ │ ├── client.ts │ │ ├── docker-compose.yml │ │ ├── expected.data.yaml │ │ ├── init │ │ │ └── init.sql │ │ ├── server.ts │ │ └── test.ts │ └── pg │ │ ├── client.ts │ │ ├── docker-compose.yml │ │ ├── expected.data.yaml │ │ ├── init │ │ └── init.sql │ │ ├── server.ts │ │ └── test.ts ├── tsconfig.json └── tslint.json └── tsconfig.json /.asf.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | github: 19 | description: The NodeJS agent for Apache SkyWalking 20 | homepage: https://skywalking.apache.org/ 21 | labels: 22 | - skywalking 23 | - observability 24 | - apm 25 | - distributed-tracing 26 | - nodejs 27 | - typescript 28 | - dapper 29 | dependabot_updates: false 30 | enabled_merge_buttons: 31 | squash: true 32 | merge: false 33 | rebase: false 34 | protected_branches: 35 | master: 36 | required_status_checks: 37 | strict: true 38 | 39 | required_pull_request_reviews: 40 | dismiss_stale_reviews: true 41 | required_approving_review_count: 1 42 | 43 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | node_modules/ 20 | lib/ 21 | .idea 22 | .git 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | root = true 19 | 20 | [*] 21 | charset = utf-8 22 | indent_style = space 23 | indent_size = 2 24 | end_of_line = lf 25 | insert_final_newline = true 26 | trim_trailing_whitespace = true 27 | quote_type = single 28 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["eslint:recommended", "plugin:prettier/recommended"], 3 | plugins: ["header"], 4 | parser: "@typescript-eslint/parser", 5 | parserOptions: { 6 | sourceType: "module" 7 | }, 8 | rules: { 9 | "header/header": ["error", ".file-headerrc"], 10 | "no-useless-escape": "off", 11 | "no-unused-vars": [ 12 | "error", 13 | // we are only using this rule to check for unused arguments since TS 14 | // catches unused variables but not args. 15 | { varsIgnorePattern: ".*", args: "none" } 16 | ] 17 | }, 18 | env: { 19 | node: true 20 | }, 21 | globals: { 22 | "NodeJS": true 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /.file-headerrc: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | name: Build 19 | 20 | on: 21 | pull_request: 22 | push: 23 | branches: 24 | - master 25 | 26 | jobs: 27 | Build: 28 | runs-on: ubuntu-latest 29 | timeout-minutes: 30 30 | strategy: 31 | matrix: 32 | node-version: [ 10, 12, 14, 16, 18 ] 33 | steps: 34 | - uses: actions/checkout@v4 35 | with: 36 | submodules: true 37 | 38 | - uses: actions/cache@v4 39 | with: 40 | path: ~/.npm 41 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 42 | restore-keys: | 43 | ${{ runner.os }}-node- 44 | 45 | - name: Set Up NodeJS ${{ matrix.node-version }} 46 | uses: actions/setup-node@v2-beta 47 | with: 48 | node-version: ${{ matrix.node-version }} 49 | 50 | - name: Build On Node@12 And Above(Current:Node@${{matrix.node-version}}) 51 | if: matrix.node-version >= 12 52 | run: | 53 | npm i 54 | npm run lint 55 | npm run build 56 | 57 | - name: Build On Node@10 58 | if: matrix.node-version == 10 59 | run: | 60 | npm i 61 | npm run build 62 | -------------------------------------------------------------------------------- /.github/workflows/license.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | name: License 19 | 20 | on: 21 | pull_request: 22 | push: 23 | branches: 24 | - master 25 | 26 | jobs: 27 | License: 28 | runs-on: ubuntu-latest 29 | timeout-minutes: 30 30 | steps: 31 | - uses: actions/checkout@v2 32 | 33 | - name: Check License Header 34 | uses: apache/skywalking-eyes/header@501a28d2fb4a9b962661987e50cf0219631b32ff 35 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | name: Test 19 | 20 | on: 21 | pull_request: 22 | push: 23 | branches: 24 | - master 25 | 26 | jobs: 27 | lint: 28 | name: Lint 29 | runs-on: ubuntu-latest 30 | timeout-minutes: 5 31 | steps: 32 | - uses: actions/checkout@v4 33 | with: 34 | submodules: true 35 | - uses: actions/cache@v4 36 | with: 37 | path: ~/.npm 38 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 39 | restore-keys: | 40 | ${{ runner.os }}-node- 41 | - name: Set Up NodeJS ${{ matrix.node-version }} 42 | uses: actions/setup-node@v2-beta 43 | with: 44 | node-version: 14 45 | - name: Lint 46 | run: | 47 | npm i 48 | npm run lint 49 | 50 | build-matrix: 51 | runs-on: ubuntu-latest 52 | timeout-minutes: 5 53 | outputs: 54 | plugins: ${{ steps.plugins.outputs.plugins }} 55 | steps: 56 | - uses: actions/checkout@v2 57 | with: 58 | submodules: true 59 | - id: plugins 60 | run: echo "plugins=$(ls tests/plugins | grep -v common | jq --raw-input | jq --slurp --compact-output)" >> "$GITHUB_OUTPUT" 61 | 62 | TestPlugins: 63 | runs-on: ubuntu-latest 64 | timeout-minutes: 30 65 | needs: [ build-matrix ] 66 | strategy: 67 | matrix: 68 | node-version: [ 14, 16, 18, 20 ] 69 | plugin: ${{ fromJson(needs.build-matrix.outputs.plugins) }} 70 | env: 71 | SW_NODE_VERSION: ${{ matrix.node-version }} 72 | steps: 73 | - uses: actions/checkout@v4 74 | with: 75 | submodules: true 76 | 77 | - uses: actions/cache@v4 78 | with: 79 | path: ~/.npm 80 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 81 | restore-keys: | 82 | ${{ runner.os }}-node- 83 | 84 | - name: Set Up NodeJS ${{ matrix.node-version }} 85 | uses: actions/setup-node@v2-beta 86 | with: 87 | node-version: ${{ matrix.node-version }} 88 | 89 | - name: Test On Node@${{ matrix.node-version }} 90 | run: | 91 | npm i 92 | npm run test tests/plugins/${{ matrix.plugin }}/ 93 | 94 | TestLib: 95 | runs-on: ubuntu-latest 96 | timeout-minutes: 30 97 | steps: 98 | - uses: actions/checkout@v2 99 | with: 100 | submodules: true 101 | 102 | - name: Test Dist Lib 103 | run: | 104 | docker build . -f tests/build/Dockerfile -t skywalking-nodejs:${{ github.sha }} 105 | docker run --rm skywalking-nodejs:${{ github.sha }} 106 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | lib 4 | src/proto/ 5 | *.log 6 | tsconfig.tsbuildinfo 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "protocol"] 2 | path = protocol 3 | url = https://github.com/apache/skywalking-data-collect-protocol.git 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run lint-staged 5 | -------------------------------------------------------------------------------- /.licenserc.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | header: 20 | license: 21 | spdx-id: Apache-2.0 22 | copyright-owner: Apache Software Foundation 23 | 24 | paths-ignore: 25 | - 'dist' 26 | - 'licenses' 27 | - '**/*.md' 28 | - '**/*.json' 29 | - 'LICENSE' 30 | - 'NOTICE' 31 | - '.gitignore' 32 | - '.gitmodules' 33 | - '.prettierrc' 34 | - 'lib' 35 | - '.husky' 36 | - '.file-headerrc' 37 | - '.eslintrc.js' 38 | 39 | comment: on-failure 40 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "all", 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Change Logs 2 | 3 | Head to the [release page](https://github.com/apache/skywalking-nodejs/releases) for the detailed changes. 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Compiling and Building 2 | 3 | We use NodeJS 14 to build skywalking-nodejs project, if you don't have NodeJS 14 installed, 4 | you can choose a node version manager like [nvm](https://github.com/nvm-sh/nvm) to easily 5 | manage multiple node vesions, or you can start a Docker container and build this project inside 6 | the container. 7 | 8 | ```shell 9 | # Suppose you have the source codes in folder skywalking-nodejs 10 | docker run -it --rm -v $(pwd)/skywalking-nodejs:/workspace -w /workspace node:14 bash 11 | ``` 12 | 13 | Then run the following commands to build the project: 14 | 15 | ```shell 16 | npm install 17 | npm run build 18 | ``` 19 | 20 | Warnings can be ignored, but if you have any error that prevents you to continue, try 21 | `rm -rf node_modules/` and then rerun the commands above. 22 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Apache SkyWalking 2 | Copyright 2017-2021 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | -------------------------------------------------------------------------------- /dist/NOTICE: -------------------------------------------------------------------------------- 1 | Apache SkyWalking 2 | Copyright 2017-2020 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | -------------------------------------------------------------------------------- /dist/licenses/LICENSE-grpc_tools_node_protoc_ts.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jonathan Dai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /dist/licenses/LICENSE-on-finished.txt: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2013 Jonathan Ong 4 | Copyright (c) 2014 Douglas Christopher Wilson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | 'Software'), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /dist/licenses/LICENSE-protobuf-js.txt: -------------------------------------------------------------------------------- 1 | Copyright 2008 Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | Code generated by the Protocol Buffer compiler is owned by the owner 30 | of the input file used when generating it. This code is not 31 | standalone and requires a support library to be linked with it. This 32 | support library is itself covered by the above license. 33 | -------------------------------------------------------------------------------- /dist/licenses/LICENSE-semver.txt: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) Isaac Z. Schlueter and Contributors 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /dist/licenses/LICENSE-tslib.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Microsoft Corporation. 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 11 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /dist/licenses/LICENSE-uuid.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2010-2020 Robert Kieffer and other contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dist/licenses/LICENSE-winston.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Charlie Robbins 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | module.exports = { 21 | preset: 'ts-jest', 22 | testEnvironment: 'node', 23 | setupFilesAfterEnv: ['./jest.setup.js'], 24 | }; 25 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | const waitForExpect = require('wait-for-expect'); 21 | 22 | jest.setTimeout(30000000); 23 | 24 | waitForExpect.defaults.interval = 10000; // ms 25 | waitForExpect.defaults.timeout = 120000; // ms 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skywalking-backend-js", 3 | "version": "0.8.0", 4 | "description": "The NodeJS agent for Apache SkyWalking", 5 | "homepage": "skywalking.apache.org", 6 | "main": "lib/index.js", 7 | "typings": "lib/index.d.ts", 8 | "scripts": { 9 | "postinstall": "node -e \"if(require('fs').existsSync('.git')){process.exit(1)}\" || husky install", 10 | "prepare": "npm run generate-source", 11 | "generate-source": "scripts/protoc.sh", 12 | "build": "npm run clean && npm run prepare && tsc --build src && OUT_DIR=lib/proto/ scripts/protoc.sh", 13 | "lint": "eslint src/**/*.ts", 14 | "lint:fix": "prettier --write \"src/**/*.ts\"", 15 | "lint-staged": "lint-staged", 16 | "test": "DEBUG=testcontainers* jest", 17 | "format": "prettier --write \"src/**/*.ts\"", 18 | "clean": "(rm -rf src/proto || true) && (rm -rf src/proto || true) && (rm -rf lib || true)", 19 | "package-src": "touch skywalking-nodejs-src-$npm_package_version.tgz && tar -zcvf skywalking-nodejs-src-$npm_package_version.tgz --exclude bin --exclude .git --exclude .idea --exclude .DS_Store --exclude .github --exclude node_modules --exclude skywalking-nodejs-src-$npm_package_version.tgz .", 20 | "release-src": "npm run prepare && npm run package-src && gpg --batch --yes --armor --detach-sig skywalking-nodejs-src-$npm_package_version.tgz && shasum -a 512 skywalking-nodejs-src-$npm_package_version.tgz > skywalking-nodejs-src-$npm_package_version.tgz.sha512" 21 | }, 22 | "files": [ 23 | "lib/**/*" 24 | ], 25 | "keywords": [ 26 | "SkyWalking", 27 | "APM", 28 | "Dapper", 29 | "Tracing", 30 | "Agent", 31 | "Instrumentation" 32 | ], 33 | "author": "Apache SkyWalking Team", 34 | "license": "Apache-2.0", 35 | "repository": { 36 | "type": "git", 37 | "url": "https://github.com/apache/skywalking-nodejs" 38 | }, 39 | "bugs": { 40 | "url": "https://github.com/apache/skywalking/issues", 41 | "email": "dev@skywalking.apache.org" 42 | }, 43 | "devDependencies": { 44 | "@types/express": "^4.17.9", 45 | "@types/google-protobuf": "^3.7.2", 46 | "@types/ioredis": "^4.26.4", 47 | "@types/jest": "^26.0.15", 48 | "@types/node": "^14.0.11", 49 | "@types/semver": "^7.2.0", 50 | "@types/uuid": "^8.0.0", 51 | "@typescript-eslint/parser": "^5.10.1", 52 | "amqplib": "^0.7.0", 53 | "axios": "^0.21.0", 54 | "eslint": "^8.8.0", 55 | "eslint-config-prettier": "^8.3.0", 56 | "eslint-plugin-header": "^3.1.1", 57 | "eslint-plugin-prettier": "^4.0.0", 58 | "express": "^4.17.1", 59 | "grpc-tools": "^1.11.1", 60 | "grpc_tools_node_protoc_ts": "^5.3.0", 61 | "ioredis": "^4.27.2", 62 | "jest": "^26.6.3", 63 | "lint-staged": "^12.3.2", 64 | "mongodb": "^3.6.4", 65 | "mongoose": "^5.12.2", 66 | "mysql": "^2.18.1", 67 | "mysql2": "^2.2.5", 68 | "pg": "^8.5.1", 69 | "prettier": "^2.5.1", 70 | "testcontainers": "^10.3.2", 71 | "ts-jest": "^26.4.4", 72 | "ts-node": "^8.10.2", 73 | "tsconfig-paths": "^3.9.0", 74 | "typescript": "^3.9.5", 75 | "wait-for-expect": "^3.0.2" 76 | }, 77 | "dependencies": { 78 | "@grpc/grpc-js": "^1.6.7", 79 | "google-protobuf": "^3.14.0", 80 | "husky": "^7.0.4", 81 | "semver": "^7.3.2", 82 | "tslib": "^2.0.3", 83 | "uuid": "^8.1.0", 84 | "winston": "^3.2.1" 85 | }, 86 | "lint-staged": { 87 | "*.ts": [ 88 | "eslint", 89 | "prettier --write" 90 | ] 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /scripts/protoc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ROOT_DIR=$(cd "$(dirname "$0")"/..; pwd) 20 | OUT_DIR="$ROOT_DIR"/${OUT_DIR:-src/proto/} 21 | 22 | mkdir -p $OUT_DIR || true 23 | 24 | cd "${ROOT_DIR}"/protocol || exit 25 | 26 | PROTOC_GEN_TS_PATH="${ROOT_DIR}/node_modules/.bin/protoc-gen-ts" 27 | PROTOC="${ROOT_DIR}/node_modules/.bin/grpc_tools_node_protoc" 28 | 29 | ${PROTOC} \ 30 | --js_out=import_style=commonjs,binary:$OUT_DIR \ 31 | --grpc_out=grpc_js:$OUT_DIR \ 32 | **/*.proto 33 | 34 | ${PROTOC} \ 35 | --plugin=protoc-gen-ts="${PROTOC_GEN_TS_PATH}" \ 36 | --ts_out=grpc_js:$OUT_DIR \ 37 | **/*.proto 38 | 39 | -------------------------------------------------------------------------------- /src/Log.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | export interface LogItem { 21 | key: string; 22 | val: string; 23 | } 24 | 25 | export default interface Log { 26 | timestamp: number; 27 | items: LogItem[]; 28 | } 29 | -------------------------------------------------------------------------------- /src/Tag.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | export interface Tag { 21 | readonly key: string; 22 | readonly overridable: boolean; 23 | val: string; 24 | } 25 | 26 | export default { 27 | coldStartKey: 'coldStart', 28 | httpStatusCodeKey: 'http.status_code', // TODO: maybe find a better place to put these? 29 | httpStatusMsgKey: 'http.status.msg', 30 | httpURLKey: 'http.url', 31 | httpMethodKey: 'http.method', 32 | dbTypeKey: 'db.type', 33 | dbInstanceKey: 'db.instance', 34 | dbStatementKey: 'db.statement', 35 | dbSqlParametersKey: 'db.sql.parameters', 36 | dbMongoParametersKey: 'db.mongo.parameters', 37 | mqBrokerKey: 'mq.broker', 38 | mqTopicKey: 'mq.topic', 39 | mqQueueKey: 'mq.queue', 40 | arnKey: 'arn', 41 | 42 | coldStart(val: boolean = true): Tag { 43 | return { 44 | key: this.coldStartKey, 45 | overridable: true, 46 | val: `${val}`, 47 | } as Tag; 48 | }, 49 | httpStatusCode(val: string | number | undefined): Tag { 50 | return { 51 | key: this.httpStatusCodeKey, 52 | overridable: true, 53 | val: `${val}`, 54 | } as Tag; 55 | }, 56 | httpStatusMsg(val: string | undefined): Tag { 57 | return { 58 | key: this.httpStatusMsgKey, 59 | overridable: true, 60 | val: `${val}`, 61 | } as Tag; 62 | }, 63 | httpURL(val: string | undefined): Tag { 64 | return { 65 | key: this.httpURLKey, 66 | overridable: true, 67 | val: `${val}`, 68 | } as Tag; 69 | }, 70 | httpMethod(val: string | undefined): Tag { 71 | return { 72 | key: this.httpMethodKey, 73 | overridable: true, 74 | val: `${val}`, 75 | } as Tag; 76 | }, 77 | dbType(val: string | undefined): Tag { 78 | return { 79 | key: this.dbTypeKey, 80 | overridable: true, 81 | val: `${val}`, 82 | } as Tag; 83 | }, 84 | dbInstance(val: string | undefined): Tag { 85 | return { 86 | key: this.dbInstanceKey, 87 | overridable: true, 88 | val: `${val}`, 89 | } as Tag; 90 | }, 91 | dbStatement(val: string | undefined): Tag { 92 | return { 93 | key: this.dbStatementKey, 94 | overridable: true, 95 | val: `${val}`, 96 | } as Tag; 97 | }, 98 | dbSqlParameters(val: string | undefined): Tag { 99 | return { 100 | key: this.dbSqlParametersKey, 101 | overridable: false, 102 | val: `${val}`, 103 | } as Tag; 104 | }, 105 | dbMongoParameters(val: string | undefined): Tag { 106 | return { 107 | key: this.dbMongoParametersKey, 108 | overridable: false, 109 | val: `${val}`, 110 | } as Tag; 111 | }, 112 | mqBroker(val: string | undefined): Tag { 113 | return { 114 | key: this.mqBrokerKey, 115 | overridable: true, 116 | val: `${val}`, 117 | } as Tag; 118 | }, 119 | mqTopic(val: string | undefined): Tag { 120 | return { 121 | key: this.mqTopicKey, 122 | overridable: true, 123 | val: `${val}`, 124 | } as Tag; 125 | }, 126 | mqQueue(val: string | undefined): Tag { 127 | return { 128 | key: this.mqQueueKey, 129 | overridable: true, 130 | val: `${val}`, 131 | } as Tag; 132 | }, 133 | arn(val: string | undefined): Tag { 134 | return { 135 | key: this.arnKey, 136 | overridable: true, 137 | val: `${val}`, 138 | } as Tag; 139 | }, 140 | }; 141 | -------------------------------------------------------------------------------- /src/agent/protocol/Protocol.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /** 21 | * The transport protocol between the agent and the backend (OAP). 22 | */ 23 | export default interface Protocol { 24 | isConnected: boolean; 25 | 26 | heartbeat(): this; 27 | 28 | report(): this; 29 | 30 | flush(): Promise | null; 31 | } 32 | -------------------------------------------------------------------------------- /src/agent/protocol/grpc/AuthInterceptor.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as grpc from '@grpc/grpc-js'; 21 | import config from '../../../config/AgentConfig'; 22 | 23 | export default function AuthInterceptor() { 24 | const mata = new grpc.Metadata(); 25 | if (config.authorization) { 26 | mata.add('Authentication', config.authorization); 27 | } 28 | return mata; 29 | } 30 | -------------------------------------------------------------------------------- /src/agent/protocol/grpc/GrpcProtocol.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import Protocol from '../../../agent/protocol/Protocol'; 21 | import HeartbeatClient from '../../../agent/protocol/grpc/clients/HeartbeatClient'; 22 | import TraceReportClient from '../../../agent/protocol/grpc/clients/TraceReportClient'; 23 | 24 | export default class GrpcProtocol implements Protocol { 25 | private readonly heartbeatClient: HeartbeatClient; 26 | private readonly traceReportClient: TraceReportClient; 27 | 28 | constructor() { 29 | this.heartbeatClient = new HeartbeatClient(); 30 | this.traceReportClient = new TraceReportClient(); 31 | } 32 | 33 | get isConnected(): boolean { 34 | return this.heartbeatClient.isConnected && this.traceReportClient.isConnected; 35 | } 36 | 37 | heartbeat(): this { 38 | this.heartbeatClient.start(); 39 | return this; 40 | } 41 | 42 | report(): this { 43 | this.traceReportClient.start(); 44 | return this; 45 | } 46 | 47 | flush(): Promise | null { 48 | return this.traceReportClient.flush(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/agent/protocol/grpc/SegmentObjectAdapter.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import config from '../../../config/AgentConfig'; 21 | import { KeyStringValuePair } from '../../../proto/common/Common_pb'; 22 | import Segment from '../../../trace/context/Segment'; 23 | import { Log, RefType, SegmentObject, SegmentReference, SpanObject } from '../../../proto/language-agent/Tracing_pb'; 24 | 25 | /** 26 | * An adapter that adapts {@link Segment} objects to gRPC object {@link SegmentObject}. 27 | */ 28 | export default class SegmentObjectAdapter extends SegmentObject { 29 | constructor(segment: Segment) { 30 | super(); 31 | super 32 | .setService(config.serviceName) 33 | .setServiceinstance(config.serviceInstance) 34 | .setTraceid(segment.relatedTraces[0].toString()) 35 | .setTracesegmentid(segment.segmentId.toString()) 36 | .setSpansList( 37 | segment.spans.map((span) => 38 | new SpanObject() 39 | .setSpanid(span.id) 40 | .setParentspanid(span.parentId) 41 | .setStarttime(span.startTime) 42 | .setEndtime(span.endTime) 43 | .setOperationname(span.operation) 44 | .setPeer(span.peer) 45 | .setSpantype(span.type) 46 | .setSpanlayer(span.layer) 47 | .setComponentid(span.component.id) 48 | .setIserror(span.errored) 49 | .setLogsList( 50 | span.logs.map((log) => 51 | new Log() 52 | .setTime(log.timestamp) 53 | .setDataList( 54 | log.items.map((logItem) => new KeyStringValuePair().setKey(logItem.key).setValue(logItem.val)), 55 | ), 56 | ), 57 | ) 58 | .setTagsList(span.tags.map((tag) => new KeyStringValuePair().setKey(tag.key).setValue(tag.val))) 59 | .setRefsList( 60 | span.refs.map((ref) => 61 | new SegmentReference() 62 | .setReftype(RefType.CROSSPROCESS) 63 | .setTraceid(ref.traceId.toString()) 64 | .setParenttracesegmentid(ref.segmentId.toString()) 65 | .setParentspanid(ref.spanId) 66 | .setParentservice(ref.service) 67 | .setParentserviceinstance(ref.serviceInstance) 68 | .setNetworkaddressusedatpeer(ref.clientAddress), 69 | ), 70 | ), 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/agent/protocol/grpc/clients/Client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | export default interface Client { 21 | readonly isConnected: boolean; 22 | 23 | start(): void; 24 | 25 | flush(): Promise | null; 26 | } 27 | -------------------------------------------------------------------------------- /src/agent/protocol/grpc/clients/HeartbeatClient.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as grpc from '@grpc/grpc-js'; 21 | import { connectivityState } from '@grpc/grpc-js'; 22 | 23 | import * as packageInfo from '../../../../../package.json'; 24 | import { createLogger } from '../../../../logging'; 25 | import Client from './Client'; 26 | import { ManagementServiceClient } from '../../../../proto/management/Management_grpc_pb'; 27 | import AuthInterceptor from '../AuthInterceptor'; 28 | import { InstancePingPkg, InstanceProperties } from '../../../../proto/management/Management_pb'; 29 | import config from '../../../../config/AgentConfig'; 30 | import { KeyStringValuePair } from '../../../../proto/common/Common_pb'; 31 | import * as os from 'os'; 32 | 33 | const logger = createLogger(__filename); 34 | 35 | export default class HeartbeatClient implements Client { 36 | private readonly managementServiceClient: ManagementServiceClient; 37 | private heartbeatTimer?: NodeJS.Timeout; 38 | 39 | constructor() { 40 | this.managementServiceClient = new ManagementServiceClient( 41 | config.collectorAddress, 42 | config.secure ? grpc.credentials.createSsl() : grpc.credentials.createInsecure(), 43 | ); 44 | } 45 | 46 | get isConnected(): boolean { 47 | return this.managementServiceClient.getChannel().getConnectivityState(true) === connectivityState.READY; 48 | } 49 | 50 | start() { 51 | if (this.heartbeatTimer) { 52 | logger.warn(` 53 | The heartbeat timer has already been scheduled, 54 | this may be a potential bug, please consider reporting 55 | this to ${packageInfo.bugs.url} 56 | `); 57 | return; 58 | } 59 | 60 | const keepAlivePkg = new InstancePingPkg() 61 | .setService(config.serviceName) 62 | .setServiceinstance(config.serviceInstance); 63 | 64 | const instanceProperties = new InstanceProperties() 65 | .setService(config.serviceName) 66 | .setServiceinstance(config.serviceInstance) 67 | .setPropertiesList([ 68 | new KeyStringValuePair().setKey('language').setValue('NodeJS'), 69 | new KeyStringValuePair().setKey('OS Name').setValue(os.platform()), 70 | new KeyStringValuePair().setKey('hostname').setValue(os.hostname()), 71 | new KeyStringValuePair().setKey('Process No.').setValue(`${process.pid}`), 72 | ]); 73 | 74 | this.heartbeatTimer = setInterval(() => { 75 | this.managementServiceClient.reportInstanceProperties(instanceProperties, AuthInterceptor(), (error, _) => { 76 | if (error) { 77 | logger.error('Failed to send heartbeat', error); 78 | } 79 | }); 80 | this.managementServiceClient.keepAlive(keepAlivePkg, AuthInterceptor(), (error, _) => { 81 | if (error) { 82 | logger.error('Failed to send heartbeat', error); 83 | } 84 | }); 85 | }, 20000).unref(); 86 | } 87 | 88 | flush(): Promise | null { 89 | logger.warn('HeartbeatClient does not need flush().'); 90 | return null; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/agent/protocol/grpc/clients/TraceReportClient.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import config from '../../../../config/AgentConfig'; 21 | import * as grpc from '@grpc/grpc-js'; 22 | import { connectivityState } from '@grpc/grpc-js'; 23 | import { createLogger } from '../../../../logging'; 24 | import Client from './Client'; 25 | import { TraceSegmentReportServiceClient } from '../../../../proto/language-agent/Tracing_grpc_pb'; 26 | import AuthInterceptor from '../AuthInterceptor'; 27 | import SegmentObjectAdapter from '../SegmentObjectAdapter'; 28 | import { emitter } from '../../../../lib/EventEmitter'; 29 | import Segment from '../../../../trace/context/Segment'; 30 | 31 | const logger = createLogger(__filename); 32 | 33 | export default class TraceReportClient implements Client { 34 | private readonly reporterClient: TraceSegmentReportServiceClient; 35 | private readonly buffer: Segment[] = []; 36 | private timeout?: NodeJS.Timeout; 37 | 38 | constructor() { 39 | this.reporterClient = new TraceSegmentReportServiceClient( 40 | config.collectorAddress, 41 | config.secure ? grpc.credentials.createSsl() : grpc.credentials.createInsecure(), 42 | ); 43 | emitter.on('segment-finished', (segment) => { 44 | this.buffer.push(segment); 45 | this.timeout?.ref(); 46 | }); 47 | } 48 | 49 | get isConnected(): boolean { 50 | return this.reporterClient?.getChannel().getConnectivityState(true) === connectivityState.READY; 51 | } 52 | 53 | private reportFunction(callback?: any) { 54 | emitter.emit('segments-sent'); // reset limiter in SpanContext 55 | 56 | try { 57 | if (this.buffer.length === 0) { 58 | if (callback) callback(); 59 | 60 | return; 61 | } 62 | 63 | const stream = this.reporterClient.collect( 64 | AuthInterceptor(), 65 | { deadline: Date.now() + config.traceTimeout }, 66 | (error, _) => { 67 | if (error) { 68 | logger.error('Failed to report trace data', error); 69 | } 70 | 71 | if (callback) callback(); 72 | }, 73 | ); 74 | 75 | try { 76 | for (const segment of this.buffer) { 77 | if (segment) { 78 | if (logger._isDebugEnabled) { 79 | logger.debug('Sending segment ', { segment }); 80 | } 81 | 82 | stream.write(new SegmentObjectAdapter(segment)); 83 | } 84 | } 85 | } finally { 86 | this.buffer.length = 0; 87 | } 88 | 89 | stream.end(); 90 | } finally { 91 | this.timeout = setTimeout(this.reportFunction.bind(this), 1000).unref(); 92 | } 93 | } 94 | 95 | start() { 96 | this.timeout = setTimeout(this.reportFunction.bind(this), 1000).unref(); 97 | } 98 | 99 | flush(): Promise | null { 100 | // This function explicitly returns null instead of a resolved Promise in case of nothing to flush so that in this 101 | // case passing control back to the event loop can be avoided. Even a resolved Promise will run other things in 102 | // the event loop when it is awaited and before it continues. 103 | 104 | return this.buffer.length === 0 105 | ? null 106 | : new Promise((resolve) => { 107 | this.reportFunction(resolve); 108 | }); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/annotations/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import ContextManager from '../trace/context/ContextManager'; 21 | 22 | export function trace(op?: string) { 23 | return (target: any, key: string, descriptor?: PropertyDescriptor) => { 24 | if (descriptor === undefined) { 25 | return; 26 | } 27 | 28 | const original = descriptor.value; 29 | 30 | descriptor.value = function (...args: any[]) { 31 | const span = ContextManager.current.newLocalSpan(op || key); 32 | 33 | span.start(); 34 | 35 | const result = original.apply(this, args); 36 | 37 | span.stop(); 38 | 39 | return result; 40 | }; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /src/aws/AWSLambdaGatewayAPIHTTP.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import { URL } from 'url'; 21 | import ContextManager from '../trace/context/ContextManager'; 22 | import { Component } from '../trace/Component'; 23 | import Tag from '../Tag'; 24 | import { SpanLayer } from '../proto/language-agent/Tracing_pb'; 25 | import { ContextCarrier } from '../trace/context/ContextCarrier'; 26 | import Span from '../trace/span/Span'; 27 | import DummySpan from '../trace/span/DummySpan'; 28 | import { ignoreHttpMethodCheck } from '../config/AgentConfig'; 29 | import { AWSLambdaTriggerPlugin } from './AWSLambdaTriggerPlugin'; 30 | 31 | class AWSLambdaGatewayAPIHTTP extends AWSLambdaTriggerPlugin { 32 | start(event: any, context: any): [Span, any] { 33 | const headers = event.headers; 34 | const reqCtx = event.requestContext; 35 | const http = reqCtx?.http; 36 | const method = http?.method; 37 | const proto = http?.protocol ? http.protocol.split('/')[0].toLowerCase() : headers?.['x-forwarded-proto']; 38 | const port = headers?.['x-forwarded-port'] || ''; 39 | const host = headers?.host ?? (reqCtx?.domainName || ''); 40 | const hostport = host ? (port ? `${host}:${port}` : host) : port; 41 | const operation = http?.path ?? event.rawPath ?? (context.functionName ? `/${context.functionName}` : '/'); 42 | 43 | const query = event.rawQueryString 44 | ? `?${event.rawQueryString}` 45 | : event.queryStringParameters 46 | ? '?' + 47 | Object.entries(event.queryStringParameters) 48 | .map(([k, v]) => `${k}=${v}`) 49 | .join('&') 50 | : ''; 51 | 52 | const carrier = headers && ContextCarrier.from(headers); 53 | 54 | const span = 55 | method && ignoreHttpMethodCheck(method) 56 | ? DummySpan.create() 57 | : ContextManager.current.newEntrySpan(operation, carrier); 58 | 59 | span.component = Component.AWSLAMBDA_GATEWAYAPIHTTP; 60 | span.peer = http?.sourceIp ?? headers?.['x-forwarded-for'] ?? 'Unknown'; 61 | 62 | if (method) span.tag(Tag.httpMethod(method)); 63 | 64 | if (hostport && proto) span.tag(Tag.httpURL(new URL(`${proto}://${hostport}${operation}${query}`).toString())); 65 | 66 | span.start(); 67 | 68 | return [span, event]; 69 | } 70 | 71 | stop(span: Span, err: Error | null, res: any): void { 72 | const statusCode = res?.statusCode || (typeof res === 'number' ? res : err ? 500 : null); 73 | 74 | if (statusCode) { 75 | if (statusCode >= 400) span.errored = true; 76 | 77 | span.tag(Tag.httpStatusCode(statusCode)); 78 | } 79 | 80 | span.stop(); 81 | } 82 | } 83 | 84 | export default new AWSLambdaGatewayAPIHTTP(); 85 | -------------------------------------------------------------------------------- /src/aws/AWSLambdaGatewayAPIREST.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import { URL } from 'url'; 21 | import ContextManager from '../trace/context/ContextManager'; 22 | import { Component } from '../trace/Component'; 23 | import Tag from '../Tag'; 24 | import { SpanLayer } from '../proto/language-agent/Tracing_pb'; 25 | import { ContextCarrier } from '../trace/context/ContextCarrier'; 26 | import Span from '../trace/span/Span'; 27 | import DummySpan from '../trace/span/DummySpan'; 28 | import { ignoreHttpMethodCheck } from '../config/AgentConfig'; 29 | import { AWSLambdaTriggerPlugin } from './AWSLambdaTriggerPlugin'; 30 | 31 | class AWSLambdaGatewayAPIREST extends AWSLambdaTriggerPlugin { 32 | start(event: any, context: any): [Span, any] { 33 | const headers = event.headers; 34 | const reqCtx = event.requestContext; 35 | const method = reqCtx?.httpMethod ?? event.httpMethod; 36 | const proto = reqCtx?.protocol ? reqCtx.protocol.split('/')[0].toLowerCase() : headers?.['X-Forwarded-Proto']; 37 | const port = headers?.['X-Forwarded-Port'] || ''; 38 | const host = headers?.Host ?? (reqCtx?.domainName || ''); 39 | const hostport = host ? (port ? `${host}:${port}` : host) : port; 40 | const operation = reqCtx?.path ?? event.path ?? (context.functionName ? `/${context.functionName}` : '/'); 41 | 42 | const query = event.multiValueQueryStringParameters 43 | ? '?' + 44 | Object.entries(event.multiValueQueryStringParameters) 45 | .map(([k, vs]: any[]) => vs.map((v: String) => `${k}=${v}`).join('&')) 46 | .join('&') 47 | : event.queryStringParameters 48 | ? '?' + 49 | Object.entries(event.queryStringParameters) 50 | .map(([k, v]) => `${k}=${v}`) 51 | .join('&') 52 | : ''; 53 | 54 | const carrier = headers && ContextCarrier.from(headers); 55 | 56 | const span = 57 | method && ignoreHttpMethodCheck(method) 58 | ? DummySpan.create() 59 | : ContextManager.current.newEntrySpan(operation, carrier); 60 | 61 | span.component = Component.AWSLAMBDA_GATEWAYAPIREST; 62 | span.peer = reqCtx?.identity?.sourceIp ?? headers?.['X-Forwarded-For'] ?? 'Unknown'; 63 | 64 | if (method) span.tag(Tag.httpMethod(method)); 65 | 66 | if (hostport && proto) span.tag(Tag.httpURL(new URL(`${proto}://${hostport}${operation}${query}`).toString())); 67 | 68 | span.start(); 69 | 70 | return [span, event]; 71 | } 72 | 73 | stop(span: Span, err: Error | null, res: any): void { 74 | const statusCode = res?.statusCode || (typeof res === 'number' ? res : err ? 500 : null); 75 | 76 | if (statusCode) { 77 | if (statusCode >= 400) span.errored = true; 78 | 79 | span.tag(Tag.httpStatusCode(statusCode)); 80 | } 81 | 82 | span.stop(); 83 | } 84 | } 85 | 86 | export default new AWSLambdaGatewayAPIREST(); 87 | -------------------------------------------------------------------------------- /src/azure/AzureHttpTriggerPlugin.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import { URL } from 'url'; 21 | import ContextManager from '../trace/context/ContextManager'; 22 | import { Component } from '../trace/Component'; 23 | import Tag from '../Tag'; 24 | import { SpanLayer } from '../proto/language-agent/Tracing_pb'; 25 | import { ContextCarrier } from '../trace/context/ContextCarrier'; 26 | import DummySpan from '../trace/span/DummySpan'; 27 | import { ignoreHttpMethodCheck } from '../config/AgentConfig'; 28 | 29 | class AzureHttpTriggerPlugin { 30 | wrap(func: any) { 31 | return function (this: any, context: any) { 32 | let outRet = true; 33 | let outName: any; 34 | 35 | for (const def of context.bindingDefinitions || []) { 36 | if (def.type === 'http' && def.directioun === 'out') { 37 | outName = def.name; 38 | outRet = outName === '$return'; 39 | } 40 | } 41 | 42 | const req = context.req; 43 | const url = new URL(req.url); 44 | const operation = url.pathname.replace(/\?.*$/g, ''); 45 | const carrier = ContextCarrier.from(req.headers); 46 | 47 | const span: any = ignoreHttpMethodCheck(req.method) 48 | ? DummySpan.create() 49 | : ContextManager.current.newEntrySpan(operation, carrier); 50 | 51 | span.layer = SpanLayer.HTTP; 52 | span.component = Component.AZURE_HTTPTRIGGER; 53 | span.peer = (req.headers['x-forwarded-for'] || '???').split(',').shift(); 54 | 55 | span.tag(Tag.httpMethod(req.method)); 56 | span.tag(Tag.httpURL(url.origin + url.pathname)); 57 | 58 | span.start(); 59 | 60 | let stop = (_ret?: any) => { 61 | stop = (v: any) => v; 62 | 63 | const res = outRet ? _ret : context.bindings[outName] || context.res; 64 | const status = span.errored ? 500 : !res ? 0 : res.status || 200; 65 | 66 | if (status) { 67 | span.tag(Tag.httpStatusCode(status)); 68 | 69 | if (status >= 400) span.errored = true; 70 | } 71 | 72 | span.stop(); 73 | 74 | return _ret; 75 | }; 76 | 77 | const done = context.done; 78 | let did = false; 79 | 80 | context.done = function (err: any, _ret: any) { 81 | if (!did) { 82 | if (err) span.error(err); 83 | 84 | if (arguments.length >= 2) arguments[1] = stop(_ret); 85 | else stop(); 86 | 87 | did = true; 88 | } 89 | 90 | return done.apply(this, arguments); 91 | }; 92 | 93 | let ret; 94 | 95 | try { 96 | ret = func.apply(this, arguments); 97 | } catch (err) { 98 | span.error(err); 99 | stop(); 100 | 101 | throw err; 102 | } 103 | 104 | if (ret && typeof ret.then === 'function') { 105 | // generic Promise check 106 | ret = ret.then( 107 | (_ret: any) => { 108 | return stop(_ret); 109 | }, 110 | 111 | (err: any) => { 112 | span.error(err); 113 | stop(); 114 | 115 | return Promise.reject(err); 116 | }, 117 | ); 118 | } 119 | 120 | span.async(); 121 | 122 | return ret; 123 | }; 124 | } 125 | } 126 | 127 | // noinspection JSUnusedGlobalSymbols 128 | export default new AzureHttpTriggerPlugin(); 129 | -------------------------------------------------------------------------------- /src/core/OptionMethods.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import PluginInstaller from './PluginInstaller'; 21 | 22 | export default interface OptionMethods { 23 | getVersion?(installer: PluginInstaller): string; 24 | } 25 | -------------------------------------------------------------------------------- /src/core/SwPlugin.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import PluginInstaller from './PluginInstaller'; 21 | import Span from '../trace/span/Span'; 22 | import OptionMethods from './OptionMethods'; 23 | 24 | export default interface SwPlugin extends OptionMethods { 25 | readonly module: string; 26 | readonly versions: string; 27 | readonly isBuiltIn?: boolean; 28 | 29 | install(installer: PluginInstaller): void; 30 | } 31 | 32 | export const wrapEmit = (span: Span, ee: any, doError: boolean = true, stop: any = NaN) => { 33 | // stop = NaN because NaN !== NaN 34 | const stopIsFunc = typeof stop === 'function'; 35 | const _emit = ee.emit; 36 | 37 | Object.defineProperty(ee, 'emit', { 38 | configurable: true, 39 | writable: true, 40 | value: function (this: any): any { 41 | const event = arguments[0]; 42 | 43 | span.resync(); 44 | 45 | try { 46 | if (doError && event === 'error') span.error(arguments[1]); 47 | 48 | return _emit.apply(this, arguments); 49 | } catch (err) { 50 | span.error(err); 51 | 52 | throw err; 53 | } finally { 54 | if (stopIsFunc ? stop(event) : event === stop) span.stop(); 55 | else span.async(); 56 | } 57 | }, 58 | }); 59 | }; 60 | 61 | export const wrapCallback = (span: Span, callback: any, idxError: any = false) => { 62 | return function (this: any) { 63 | if (idxError !== false && arguments[idxError]) span.error(arguments[idxError]); 64 | 65 | span.stop(); 66 | 67 | return callback.apply(this, arguments); 68 | }; 69 | }; 70 | 71 | export const wrapPromise = (span: Span, promise: any) => { 72 | return promise.then( 73 | (res: any) => { 74 | span.stop(); 75 | 76 | return res; 77 | }, 78 | 79 | (err: any) => { 80 | span.error(err); 81 | span.stop(); 82 | 83 | return Promise.reject(err); 84 | }, 85 | ); 86 | }; 87 | -------------------------------------------------------------------------------- /src/egg/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import agent from '../index'; 21 | 22 | agent.start({}); 23 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import config, { AgentConfig, finalizeConfig } from './config/AgentConfig'; 21 | import Protocol from './agent/protocol/Protocol'; 22 | import GrpcProtocol from './agent/protocol/grpc/GrpcProtocol'; 23 | import { createLogger } from './logging'; 24 | import PluginInstaller from './core/PluginInstaller'; 25 | import SpanContext from './trace/context/SpanContext'; 26 | 27 | const logger = createLogger(__filename); 28 | 29 | class Agent { 30 | private started = false; 31 | private protocol: Protocol | null = null; 32 | 33 | start(options: AgentConfig = {}): void { 34 | if (process.env.SW_DISABLE === 'true') { 35 | logger.info('SkyWalking agent is disabled by `SW_DISABLE=true`'); 36 | return; 37 | } 38 | 39 | if (this.started) { 40 | logger.warn('SkyWalking agent started more than once, subsequent options to start ignored.'); 41 | return; 42 | } 43 | 44 | Object.assign(config, options); 45 | finalizeConfig(config); 46 | 47 | logger.debug('Starting SkyWalking agent'); 48 | 49 | new PluginInstaller().install(); 50 | 51 | this.protocol = new GrpcProtocol().heartbeat().report(); 52 | this.started = true; 53 | } 54 | 55 | flush(): Promise | null { 56 | if (this.protocol === null) { 57 | logger.warn('Trying to flush() SkyWalking agent which is not started.'); 58 | return null; 59 | } 60 | 61 | const spanContextFlush = SpanContext.flush(); // if there are spans which haven't finished then wait for them 62 | const protocol = this.protocol; 63 | 64 | if (!spanContextFlush) return protocol.flush(); 65 | 66 | return new Promise((resolve) => { 67 | spanContextFlush.then(() => { 68 | const protocolFlush = protocol.flush(); 69 | 70 | if (!protocolFlush) resolve(null); 71 | else protocolFlush.then(() => resolve(null)); 72 | }); 73 | }); 74 | } 75 | } 76 | 77 | export default new Agent(); 78 | export { default as config } from './config/AgentConfig'; 79 | export { default as ContextManager } from './trace/context/ContextManager'; 80 | export { default as AzureHttpTriggerPlugin } from './azure/AzureHttpTriggerPlugin'; 81 | export { default as AWSLambdaTriggerPlugin } from './aws/AWSLambdaTriggerPlugin'; 82 | export { default as AWSLambdaGatewayAPIHTTP } from './aws/AWSLambdaGatewayAPIHTTP'; 83 | export { default as AWSLambdaGatewayAPIREST } from './aws/AWSLambdaGatewayAPIREST'; 84 | -------------------------------------------------------------------------------- /src/lib/EventEmitter.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /* tslint:disable:unified-signatures */ 21 | 22 | import { EventEmitter } from 'events'; 23 | import Segment from '../trace/context/Segment'; 24 | 25 | declare interface SkyWalkingEventEmitter { 26 | on(event: 'segment-finished', listener: (segment: Segment) => void): this; 27 | on(event: 'segments-sent', listener: () => void): this; 28 | } 29 | 30 | class SkyWalkingEventEmitterImpl extends EventEmitter {} 31 | 32 | export const emitter: SkyWalkingEventEmitterImpl = new SkyWalkingEventEmitterImpl(); 33 | -------------------------------------------------------------------------------- /src/logging/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as winston from 'winston'; 21 | import { Logger } from 'winston'; 22 | 23 | type LoggerLevelAware = Logger & { 24 | _isDebugEnabled: boolean; 25 | _isInfoEnabled: boolean; 26 | }; 27 | 28 | export function createLogger(name: string): LoggerLevelAware { 29 | const loggingLevel = (process.env.SW_AGENT_LOGGING_LEVEL || 'error').toLowerCase(); 30 | 31 | const logger = winston.createLogger({ 32 | level: loggingLevel, 33 | format: winston.format.json(), 34 | defaultMeta: { 35 | file: name, 36 | }, 37 | }); 38 | 39 | if (process.env.NODE_ENV !== 'production' || process.env.SW_LOGGING_TARGET === 'console') { 40 | logger.add( 41 | new winston.transports.Console({ 42 | format: winston.format.prettyPrint(), 43 | }), 44 | ); 45 | } else { 46 | logger.add( 47 | new winston.transports.File({ 48 | filename: 'skywalking.log', 49 | }), 50 | ); 51 | } 52 | 53 | const loggerLevel = logger.levels[logger.level]; 54 | const _isDebugEnabled = loggerLevel >= logger.levels.debug; 55 | const _isInfoEnabled = loggerLevel >= logger.levels.info; 56 | 57 | Object.assign(logger, { 58 | _isDebugEnabled, 59 | _isInfoEnabled, 60 | }); 61 | 62 | const nop = (): void => { 63 | /* a cookie for the linter */ 64 | }; 65 | 66 | if (loggerLevel < logger.levels.debug) 67 | // we do this because logger still seems to stringify anything sent to it even if it is below the logging level, costing performance 68 | (logger as any).debug = nop; 69 | 70 | if (loggerLevel < logger.levels.info) (logger as any).info = nop; 71 | 72 | if (loggerLevel < logger.levels.warn) (logger as any).warn = nop; 73 | 74 | return logger as LoggerLevelAware; 75 | } 76 | -------------------------------------------------------------------------------- /src/plugins/AWS2DynamoDBPlugin.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import SwPlugin from '../core/SwPlugin'; 21 | import ContextManager from '../trace/context/ContextManager'; 22 | import { Component } from '../trace/Component'; 23 | import Tag from '../Tag'; 24 | import { SpanLayer } from '../proto/language-agent/Tracing_pb'; 25 | import PluginInstaller from '../core/PluginInstaller'; 26 | import { getAWS, execute } from '../aws/SDK2'; 27 | 28 | class AWS2DynamoDBPlugin implements SwPlugin { 29 | readonly module = 'aws-sdk'; 30 | readonly versions = '2.*'; 31 | 32 | install(installer: PluginInstaller): void { 33 | const AWS = getAWS(installer); 34 | const DocumentClient = AWS.DynamoDB.DocumentClient; 35 | 36 | function instrument(name: string): void { 37 | const _func = DocumentClient.prototype[name]; 38 | 39 | DocumentClient.prototype[name] = function (params: any, callback?: any): any { 40 | const span = ContextManager.current.newExitSpan(`AWS/DynamoDB/${name}`, Component.AWS_DYNAMODB, Component.HTTP); 41 | 42 | span.component = Component.AWS_DYNAMODB; 43 | span.layer = SpanLayer.DATABASE; 44 | // span.peer = `${this.service.endpoint.host ?? ''}:${this.service.endpoint.port ?? ''}`; 45 | 46 | span.tag(Tag.dbType('DynamoDB')); 47 | span.tag(Tag.dbStatement(name)); 48 | 49 | return execute(span, this, _func, params, callback); 50 | }; 51 | } 52 | 53 | instrument('batchGet'); 54 | instrument('batchWrite'); 55 | instrument('delete'); 56 | instrument('get'); 57 | instrument('put'); 58 | instrument('query'); 59 | instrument('scan'); 60 | instrument('update'); 61 | instrument('transactGet'); 62 | instrument('transactWrite'); 63 | } 64 | } 65 | 66 | // noinspection JSUnusedGlobalSymbols 67 | export default new AWS2DynamoDBPlugin(); 68 | 69 | // // Example code for test maybe: 70 | // const AWS = require("aws-sdk"); 71 | 72 | // AWS.config.update({region: 'your-region'}); 73 | 74 | // const dynamo = new AWS.DynamoDB.DocumentClient(); 75 | 76 | // function callback(err, data) { 77 | // console.log('... callback err:', err); 78 | // console.log('... callback data:', data); 79 | // } 80 | 81 | // const data = {TableName: "table-name", Item: {id: 1, name: 'Bob'}}; 82 | 83 | // dynamo.put(data, callback); 84 | // // OR: 85 | // dynamo.put(data).send(callback); 86 | // // OR: 87 | // dynamo.put(data).promise() 88 | // .then(r => { console.log('... promise res:', r); }) 89 | // .catch(e => { console.log('... promise err:', e); }); 90 | -------------------------------------------------------------------------------- /src/plugins/ExpressPlugin.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import SwPlugin from '../core/SwPlugin'; 21 | import { ServerResponse } from 'http'; 22 | import ContextManager from '../trace/context/ContextManager'; 23 | import { Component } from '../trace/Component'; 24 | import Tag from '../Tag'; 25 | import { ContextCarrier } from '../trace/context/ContextCarrier'; 26 | import DummySpan from '../trace/span/DummySpan'; 27 | import { ignoreHttpMethodCheck } from '../config/AgentConfig'; 28 | import PluginInstaller from '../core/PluginInstaller'; 29 | import HttpPlugin from './HttpPlugin'; 30 | import { Request } from 'express'; 31 | 32 | class ExpressPlugin implements SwPlugin { 33 | readonly module = 'express'; 34 | readonly versions = '*'; 35 | 36 | install(installer: PluginInstaller): void { 37 | this.interceptServerRequest(installer); 38 | } 39 | 40 | private interceptServerRequest(installer: PluginInstaller) { 41 | const express = installer.require?.('express') ?? require('express'); 42 | const router = express.Router ?? installer.require?.('express/lib/router') ?? require('express/lib/router'); 43 | const _handle = router.handle; 44 | 45 | router.handle = function (req: Request, res: ServerResponse, next: any) { 46 | const carrier = ContextCarrier.from((req as any).headers || {}); 47 | const reqMethod = req.method ?? 'GET'; 48 | const operation = reqMethod + ':' + (req.originalUrl || req.url || '/').replace(/\?.*/g, ''); 49 | const span = ignoreHttpMethodCheck(reqMethod) 50 | ? DummySpan.create() 51 | : ContextManager.current.newEntrySpan(operation, carrier, [Component.HTTP_SERVER, Component.EXPRESS]); 52 | 53 | span.component = Component.EXPRESS; 54 | 55 | if (span.depth) 56 | // if we inherited from http then just change component ID and let http do the work 57 | return _handle.apply(this, arguments); 58 | 59 | return HttpPlugin.wrapHttpResponse(span, req, res, () => { 60 | // http plugin disabled, we use its mechanism anyway 61 | try { 62 | return _handle.call(this, req, res, (err: Error) => { 63 | span.error(err); 64 | next.call(this, err); 65 | }); 66 | } finally { 67 | // req.protocol is only possibly available after call to _handle() 68 | span.tag( 69 | Tag.httpURL( 70 | ((req as any).protocol ? (req as any).protocol + '://' : '') + (req.headers.host || '') + req.url, 71 | ), 72 | ); 73 | } 74 | }); 75 | }; 76 | } 77 | } 78 | 79 | // noinspection JSUnusedGlobalSymbols 80 | export default new ExpressPlugin(); 81 | -------------------------------------------------------------------------------- /src/plugins/IORedisPlugin.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import PluginInstaller from '../core/PluginInstaller'; 21 | import SwPlugin, { wrapPromise } from '../core/SwPlugin'; 22 | import { SpanLayer } from '../proto/language-agent/Tracing_pb'; 23 | import Tag from '../Tag'; 24 | import { Component } from '../trace/Component'; 25 | import ContextManager from '../trace/context/ContextManager'; 26 | 27 | class IORedisPlugin implements SwPlugin { 28 | readonly module = 'ioredis'; 29 | readonly versions = '*'; 30 | 31 | install(installer: PluginInstaller): void { 32 | const Redis = installer.require?.('ioredis') ?? require('ioredis'); 33 | 34 | this.interceptOperation(Redis, 'sendCommand'); 35 | } 36 | 37 | interceptOperation(Cls: any, operation: string): void { 38 | const _original = Cls.prototype[operation]; 39 | 40 | if (!_original) return; 41 | 42 | Cls.prototype[operation] = function (...args: any[]) { 43 | const command = args[0]; 44 | const host = `${this.options.host}:${this.options.port}`; 45 | const span = ContextManager.current.newExitSpan(`redis/${command?.name}`, Component.REDIS); 46 | 47 | span.start(); 48 | span.component = Component.REDIS; 49 | span.layer = SpanLayer.CACHE; 50 | span.peer = host; 51 | span.tag(Tag.dbType('Redis')); 52 | span.tag(Tag.dbInstance(`${this.condition?.select ?? host}`)); 53 | 54 | try { 55 | const ret = wrapPromise(span, _original.apply(this, args)); 56 | span.async(); 57 | return ret; 58 | } catch (err) { 59 | span.error(err); 60 | span.stop(); 61 | 62 | throw err; 63 | } 64 | }; 65 | } 66 | } 67 | 68 | export default new IORedisPlugin(); 69 | -------------------------------------------------------------------------------- /src/plugins/MySQLPlugin.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import SwPlugin, { wrapEmit, wrapCallback } from '../core/SwPlugin'; 21 | import ContextManager from '../trace/context/ContextManager'; 22 | import { Component } from '../trace/Component'; 23 | import Tag from '../Tag'; 24 | import { SpanLayer } from '../proto/language-agent/Tracing_pb'; 25 | import PluginInstaller from '../core/PluginInstaller'; 26 | import agentConfig from '../config/AgentConfig'; 27 | 28 | class MySQLPlugin implements SwPlugin { 29 | readonly module = 'mysql'; 30 | readonly versions = '*'; 31 | 32 | install(installer: PluginInstaller): void { 33 | const Connection = installer.require?.('mysql/lib/Connection') ?? require('mysql/lib/Connection'); 34 | const _query = Connection.prototype.query; 35 | 36 | Connection.prototype.query = function (sql: any, values: any, cb: any) { 37 | let query: any; 38 | 39 | const host = `${this.config.host}:${this.config.port}`; 40 | const span = ContextManager.current.newExitSpan('mysql/query', Component.MYSQL); 41 | 42 | span.start(); 43 | 44 | try { 45 | span.component = Component.MYSQL; 46 | span.layer = SpanLayer.DATABASE; 47 | span.peer = host; 48 | 49 | span.tag(Tag.dbType('Mysql')); 50 | span.tag(Tag.dbInstance(`${this.config.database}`)); 51 | 52 | let _sql: any; 53 | let _values: any; 54 | let streaming: any; 55 | 56 | if (typeof sql === 'function') { 57 | sql = wrapCallback(span, sql, 0); 58 | } else if (typeof sql === 'object') { 59 | _sql = sql.sql; 60 | 61 | if (typeof values === 'function') { 62 | values = wrapCallback(span, values, 0); 63 | _values = sql.values; 64 | } else if (values !== undefined) { 65 | _values = values; 66 | 67 | if (typeof cb === 'function') { 68 | cb = wrapCallback(span, cb, 0); 69 | } else { 70 | streaming = true; 71 | } 72 | } else { 73 | streaming = true; 74 | } 75 | } else { 76 | _sql = sql; 77 | 78 | if (typeof values === 'function') { 79 | values = wrapCallback(span, values, 0); 80 | } else if (values !== undefined) { 81 | _values = values; 82 | 83 | if (typeof cb === 'function') { 84 | cb = wrapCallback(span, cb, 0); 85 | } else { 86 | streaming = true; 87 | } 88 | } else { 89 | streaming = true; 90 | } 91 | } 92 | 93 | span.tag(Tag.dbStatement(`${_sql}`)); 94 | 95 | if (agentConfig.sqlTraceParameters && _values) { 96 | let vals = _values.map((v: any) => (v === undefined ? 'undefined' : JSON.stringify(v))).join(', '); 97 | 98 | if (vals.length > agentConfig.sqlParametersMaxLength) 99 | vals = vals.slice(0, agentConfig.sqlParametersMaxLength) + ' ...'; 100 | 101 | span.tag(Tag.dbSqlParameters(`[${vals}]`)); 102 | } 103 | 104 | query = _query.call(this, sql, values, cb); 105 | 106 | if (streaming) wrapEmit(span, query, true, 'end'); 107 | } catch (e) { 108 | span.error(e); 109 | span.stop(); 110 | 111 | throw e; 112 | } 113 | 114 | span.async(); 115 | 116 | return query; 117 | }; 118 | } 119 | } 120 | 121 | // noinspection JSUnusedGlobalSymbols 122 | export default new MySQLPlugin(); 123 | -------------------------------------------------------------------------------- /src/plugins/PgPlugin.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import SwPlugin, { wrapEmit, wrapCallback, wrapPromise } from '../core/SwPlugin'; 21 | import ContextManager from '../trace/context/ContextManager'; 22 | import { Component } from '../trace/Component'; 23 | import Tag from '../Tag'; 24 | import { SpanLayer } from '../proto/language-agent/Tracing_pb'; 25 | import PluginInstaller from '../core/PluginInstaller'; 26 | import agentConfig from '../config/AgentConfig'; 27 | 28 | class MySQLPlugin implements SwPlugin { 29 | readonly module = 'pg'; 30 | readonly versions = '*'; 31 | 32 | install(installer: PluginInstaller): void { 33 | const Client = installer.require?.('pg/lib/client') ?? require('pg/lib/client'); 34 | 35 | let Cursor: any; 36 | 37 | try { 38 | Cursor = installer.require?.('pg-cursor') ?? require('pg-cursor'); 39 | } catch { 40 | /* Linter food */ 41 | } 42 | 43 | const _query = Client.prototype.query; 44 | 45 | Client.prototype.query = function (config: any, values: any, callback: any) { 46 | let query: any; 47 | 48 | const host = `${this.host}:${this.port}`; 49 | const span = ContextManager.current.newExitSpan('pg/query', Component.POSTGRESQL); 50 | 51 | span.start(); 52 | 53 | try { 54 | span.component = Component.POSTGRESQL; 55 | span.layer = SpanLayer.DATABASE; 56 | span.peer = host; 57 | 58 | span.tag(Tag.dbType('PostgreSQL')); 59 | span.tag(Tag.dbInstance(`${this.connectionParameters.database}`)); 60 | 61 | let _sql: any; 62 | let _values: any; 63 | 64 | if (typeof config === 'string') _sql = config; 65 | else if (config !== null && config !== undefined) { 66 | _sql = config.text; 67 | _values = config.values; 68 | 69 | if (typeof config.callback === 'function') config.callback = wrapCallback(span, config.callback, 0); 70 | } 71 | 72 | if (typeof values === 'function') values = wrapCallback(span, values, 0); 73 | else if (_values !== undefined) _values = values; 74 | 75 | if (typeof callback === 'function') callback = wrapCallback(span, callback, 0); 76 | 77 | span.tag(Tag.dbStatement(`${_sql}`)); 78 | 79 | if (agentConfig.sqlTraceParameters && _values) { 80 | let vals = _values.map((v: any) => (v === undefined ? 'undefined' : JSON.stringify(v))).join(', '); 81 | 82 | if (vals.length > agentConfig.sqlParametersMaxLength) 83 | vals = vals.slice(0, agentConfig.sqlParametersMaxLength) + ' ...'; 84 | 85 | span.tag(Tag.dbSqlParameters(`[${vals}]`)); 86 | } 87 | 88 | query = _query.call(this, config, values, callback); 89 | 90 | if (query) { 91 | if (Cursor && query instanceof Cursor) wrapEmit(span, query, true, 'end'); 92 | else if (typeof query.then === 'function') 93 | // generic Promise check 94 | query = wrapPromise(span, query); 95 | // else we assume there was a callback 96 | } 97 | } catch (e) { 98 | span.error(e); 99 | span.stop(); 100 | 101 | throw e; 102 | } 103 | 104 | span.async(); 105 | 106 | return query; 107 | }; 108 | } 109 | } 110 | 111 | // noinspection JSUnusedGlobalSymbols 112 | export default new MySQLPlugin(); 113 | -------------------------------------------------------------------------------- /src/trace/Component.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | export class Component { 21 | static readonly UNKNOWN = new Component(0); 22 | static readonly HTTP = new Component(2); 23 | static readonly MYSQL = new Component(5); 24 | static readonly REDIS = new Component(7); 25 | static readonly MONGODB = new Component(9); 26 | static readonly POSTGRESQL = new Component(22); 27 | static readonly HTTP_SERVER = new Component(49); 28 | static readonly RABBITMQ_PRODUCER = new Component(52); 29 | static readonly RABBITMQ_CONSUMER = new Component(53); 30 | static readonly AZURE_HTTPTRIGGER = new Component(111); 31 | static readonly AWSLAMBDA_FUNCTION = new Component(124); 32 | static readonly AWSLAMBDA_GATEWAYAPIHTTP = new Component(125); 33 | static readonly AWSLAMBDA_GATEWAYAPIREST = new Component(126); 34 | static readonly AWS_DYNAMODB = new Component(138); 35 | static readonly AWS_SNS = new Component(139); 36 | static readonly AWS_SQS = new Component(140); 37 | static readonly EXPRESS = new Component(4002); 38 | static readonly AXIOS = new Component(4005); 39 | static readonly MONGOOSE = new Component(4006); 40 | 41 | constructor(public readonly id: number) {} 42 | } 43 | -------------------------------------------------------------------------------- /src/trace/ID.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import { v4 as uuid } from 'uuid'; 21 | 22 | export default class ID { 23 | constructor(public rawId: string = uuid().replace(/-/gi, '')) {} 24 | 25 | public toString = (): string => { 26 | return this.rawId; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/trace/NewID.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import ID from '../trace/ID'; 21 | 22 | export default class NewID extends ID {} 23 | -------------------------------------------------------------------------------- /src/trace/context/CarrierItem.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | export abstract class CarrierItem { 21 | abstract get value(): string; 22 | abstract set value(val: string); 23 | 24 | protected constructor(public key: string) {} 25 | } 26 | -------------------------------------------------------------------------------- /src/trace/context/Context.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import Span from '../../trace/span/Span'; 21 | import Segment from '../../trace/context/Segment'; 22 | import { Component } from '../Component'; 23 | import { ContextCarrier } from './ContextCarrier'; 24 | 25 | export default interface Context { 26 | segment: Segment; 27 | nSpans: number; 28 | finished: boolean; 29 | 30 | newLocalSpan(operation: string): Span; 31 | 32 | /* If 'inherit' is specified then if the span at the top of the stack is an Entry span of this component type then the 33 | span is reused instead of a new child span being created. This is intended for situations like an express handler 34 | inheriting an opened incoming http connection to present a single span. */ 35 | newEntrySpan(operation: string, carrier?: ContextCarrier, inherit?: Component | Component[]): Span; 36 | 37 | /* if 'inherit' is specified then the span returned is marked for inheritance by an Exit span component which is 38 | created later and calls this function with a matching 'component' value. For example Axios using an Http exit 39 | connection will be merged into a single exit span, see those plugins for how this is done. */ 40 | newExitSpan(operation: string, component: Component, inherit?: Component): Span; 41 | 42 | start(span: Span): Context; 43 | 44 | stop(span: Span): boolean; 45 | 46 | /* This should be called just before a span is passed to a different async context, like for example a callback from 47 | an asynchronous operation the code belonging to the span initiated. After this is called a span should only call 48 | .resync() or .stop(). See HttpPlugin.interceptClientRequest() in plugins/HttpPlugin.ts for example of usage. */ 49 | async(span: Span): void; 50 | 51 | /* This should be called upon entering the new async context for a span that has previously executed .async(), it 52 | should be the first thing the callback function belonging to the span does. */ 53 | resync(span: Span): void; 54 | 55 | /** 56 | * Returns the global trace id of the current trace, if there is no trace when invoking this method, 57 | * "N/A" is returned. 58 | */ 59 | traceId(): string; 60 | } 61 | -------------------------------------------------------------------------------- /src/trace/context/ContextCarrier.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import ID from '../../trace/ID'; 21 | import { CarrierItem } from './CarrierItem'; 22 | 23 | export class ContextCarrier extends CarrierItem { 24 | constructor( 25 | public traceId?: ID, 26 | public segmentId?: ID, 27 | public spanId?: number, 28 | public service?: string, 29 | public serviceInstance?: string, 30 | public endpoint?: string, 31 | public clientAddress?: string, 32 | public items: CarrierItem[] = [], 33 | ) { 34 | super('sw8'); 35 | this.items.push(this); 36 | } 37 | 38 | private encode = (s: string): string => { 39 | return Buffer.from(s).toString('base64'); 40 | }; 41 | 42 | private decode = (s: string): string => { 43 | return Buffer.from(s, 'base64').toString(); 44 | }; 45 | 46 | get value(): string { 47 | return this.isValid() 48 | ? [ 49 | '1', 50 | this.encode(this.traceId?.toString() || ''), 51 | this.encode(this.segmentId?.toString() || ''), 52 | this.spanId?.toString(), 53 | this.encode(this.service || ''), 54 | this.encode(this.serviceInstance || ''), 55 | this.encode(this.endpoint || ''), 56 | this.encode(this.clientAddress || ''), 57 | ].join('-') 58 | : ''; 59 | } 60 | 61 | set value(val) { 62 | if (!val) { 63 | return; 64 | } 65 | const parts = val.split('-'); 66 | if (parts.length != 8) { 67 | return; 68 | } 69 | this.traceId = new ID(this.decode(parts[1])); 70 | this.segmentId = new ID(this.decode(parts[2])); 71 | this.spanId = Number.parseInt(parts[3], 10); 72 | this.service = this.decode(parts[4]); 73 | this.serviceInstance = this.decode(parts[5]); 74 | this.endpoint = this.decode(parts[6]); 75 | this.clientAddress = this.decode(parts[7]); 76 | } 77 | 78 | isValid(): boolean { 79 | return Boolean( 80 | this.traceId?.rawId && 81 | this.segmentId?.rawId && 82 | this.spanId !== undefined && 83 | !isNaN(this.spanId) && 84 | this.service && 85 | this.endpoint && 86 | this.clientAddress !== undefined, 87 | ); 88 | } 89 | 90 | public static from(map: { [key: string]: string }): ContextCarrier | undefined { 91 | if (!Object.prototype.hasOwnProperty.call(map, 'sw8')) return; 92 | 93 | const carrier = new ContextCarrier(); 94 | 95 | carrier.items 96 | .filter((item) => Object.prototype.hasOwnProperty.call(map, item.key)) 97 | .forEach((item) => (item.value = map[item.key])); 98 | 99 | return carrier; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/trace/context/DummyContext.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import Context from '../../trace/context/Context'; 21 | import Span from '../../trace/span/Span'; 22 | import DummySpan from '../../trace/span/DummySpan'; 23 | import Segment from '../../trace/context/Segment'; 24 | import { Component } from '../Component'; 25 | import { ContextCarrier } from './ContextCarrier'; 26 | import ContextManager from './ContextManager'; 27 | 28 | export default class DummyContext implements Context { 29 | segment: Segment = new Segment(); 30 | nSpans = 0; 31 | finished = false; 32 | 33 | newEntrySpan(operation: string, carrier?: ContextCarrier, inherit?: Component | Component[]): Span { 34 | return DummySpan.create(this); 35 | } 36 | 37 | newExitSpan(operation: string, component: Component, inherit?: Component): Span { 38 | return DummySpan.create(this); 39 | } 40 | 41 | newLocalSpan(operation: string): Span { 42 | return DummySpan.create(this); 43 | } 44 | 45 | start(span: DummySpan): Context { 46 | const spans = ContextManager.spansDup(); 47 | 48 | if (!this.nSpans++) { 49 | ContextManager.checkCold(); // set cold to false 50 | 51 | if (spans.indexOf(span) === -1) spans.push(span); 52 | } 53 | 54 | return this; 55 | } 56 | 57 | stop(span: DummySpan): boolean { 58 | if (--this.nSpans) return false; 59 | 60 | ContextManager.clear(span); 61 | 62 | return true; 63 | } 64 | 65 | async(span: DummySpan) { 66 | ContextManager.clear(span); 67 | } 68 | 69 | resync(span: DummySpan) { 70 | ContextManager.restore(span); 71 | } 72 | 73 | traceId(): string { 74 | if (!this.segment.relatedTraces) { 75 | return 'N/A'; 76 | } 77 | return this.segment.relatedTraces[0].toString(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/trace/context/Segment.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import Span from '../../trace/span/Span'; 21 | import ID from '../../trace/ID'; 22 | import NewID from '../../trace/NewID'; 23 | import SegmentRef from '../../trace/context/SegmentRef'; 24 | 25 | export default class Segment { 26 | segmentId = new ID(); 27 | spans: Span[] = []; 28 | relatedTraces: ID[] = [new NewID()]; 29 | references: SegmentRef[] = []; 30 | 31 | archive(span: Span): void { 32 | this.spans.push(span); 33 | } 34 | 35 | relate(id: ID) { 36 | if (this.relatedTraces[0] instanceof NewID) { 37 | this.relatedTraces.shift(); 38 | } 39 | if (!this.relatedTraces.includes(id)) { 40 | this.relatedTraces.push(id); 41 | } 42 | } 43 | 44 | refer(ref: SegmentRef): this { 45 | if (!this.references.includes(ref)) { 46 | this.references.push(ref); 47 | } 48 | 49 | return this; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/trace/context/SegmentRef.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import ID from '../../trace/ID'; 21 | import { ContextCarrier } from './ContextCarrier'; 22 | 23 | export default class SegmentRef { 24 | private constructor( 25 | public refType: 'CrossProcess' | 'CrossThread' = 'CrossProcess', 26 | public traceId: ID, 27 | public segmentId: ID, 28 | public spanId: number, 29 | public service: string, 30 | public serviceInstance: string, 31 | public endpoint: string, 32 | public clientAddress: string, 33 | ) { 34 | this.traceId = traceId; 35 | this.segmentId = segmentId; 36 | this.spanId = spanId; 37 | this.service = service; 38 | this.serviceInstance = serviceInstance; 39 | this.endpoint = endpoint; 40 | this.clientAddress = clientAddress; 41 | } 42 | 43 | static fromCarrier(carrier: ContextCarrier): SegmentRef { 44 | return new SegmentRef( 45 | 'CrossProcess', 46 | carrier.traceId!, 47 | carrier.segmentId!, 48 | carrier.spanId!, 49 | carrier.service!, 50 | carrier.serviceInstance!, 51 | carrier.endpoint!, 52 | carrier.clientAddress!, 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/trace/span/DummySpan.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import Span from '../../trace/span/Span'; 21 | import { ContextCarrier } from '../context/ContextCarrier'; 22 | import Context from '../context/Context'; 23 | import { SpanType } from '../../proto/language-agent/Tracing_pb'; 24 | import DummyContext from '../context/DummyContext'; 25 | 26 | export default class DummySpan extends Span { 27 | static create(context?: Context): DummySpan { 28 | return new DummySpan({ 29 | context: context ?? new DummyContext(), 30 | operation: '', 31 | type: SpanType.LOCAL, 32 | }); 33 | } 34 | 35 | start(): any { 36 | if (!this.depth++) this.context.start(this); 37 | } 38 | 39 | stop(block?: any): void { 40 | if (!--this.depth) this.context.stop(this); 41 | } 42 | 43 | async(block?: any): void { 44 | this.context.async(this); 45 | } 46 | 47 | resync(): any { 48 | this.context.resync(this); 49 | } 50 | 51 | error(error: Error, statusOverride?: number): this { 52 | return this; 53 | } 54 | 55 | inject(): ContextCarrier { 56 | return new ContextCarrier(); 57 | } 58 | 59 | extract(carrier: ContextCarrier): this { 60 | return this; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/trace/span/EntrySpan.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import Span from '../../trace/span/Span'; 21 | import { SpanCtorOptions } from './Span'; 22 | import SegmentRef from '../../trace/context/SegmentRef'; 23 | import { SpanType } from '../../proto/language-agent/Tracing_pb'; 24 | import { ContextCarrier } from '../context/ContextCarrier'; 25 | 26 | export default class EntrySpan extends Span { 27 | constructor(options: SpanCtorOptions) { 28 | super( 29 | Object.assign(options, { 30 | type: SpanType.ENTRY, 31 | }), 32 | ); 33 | } 34 | 35 | extract(carrier: ContextCarrier): this { 36 | super.extract(carrier); 37 | 38 | const ref = SegmentRef.fromCarrier(carrier); 39 | 40 | this.refer(ref); 41 | 42 | return this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/trace/span/ExitSpan.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import Span from '../../trace/span/Span'; 21 | import { SpanCtorOptions } from './Span'; 22 | import config from '../../config/AgentConfig'; 23 | import { SpanType } from '../../proto/language-agent/Tracing_pb'; 24 | import { ContextCarrier } from '../context/ContextCarrier'; 25 | import ContextManager from '../context/ContextManager'; 26 | 27 | export default class ExitSpan extends Span { 28 | constructor(options: SpanCtorOptions) { 29 | super( 30 | Object.assign(options, { 31 | type: SpanType.EXIT, 32 | }), 33 | ); 34 | } 35 | 36 | inject(): ContextCarrier { 37 | return new ContextCarrier( 38 | this.context.segment.relatedTraces[0], 39 | this.context.segment.segmentId, 40 | this.id, 41 | config.serviceName, 42 | config.serviceInstance, 43 | this.operation, 44 | this.peer, 45 | [], 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/trace/span/LocalSpan.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import Span, { SpanCtorOptions } from '../../trace/span/Span'; 21 | import { SpanType } from '../../proto/language-agent/Tracing_pb'; 22 | 23 | export default class LocalSpan extends Span { 24 | constructor(options: SpanCtorOptions) { 25 | super( 26 | Object.assign(options, { 27 | type: SpanType.LOCAL, 28 | }), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "strict": true, 6 | "importHelpers": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "outDir": "../lib", 12 | "rootDir": ".", 13 | "baseUrl": ".", 14 | "resolveJsonModule": true, 15 | "declaration": true, 16 | "allowJs": true, 17 | "sourceMap": true 18 | }, 19 | "references": [ 20 | { 21 | "path": ".." 22 | } 23 | ], 24 | "include": [ 25 | "**/*" 26 | ], 27 | "exclude": [ 28 | "node_modules", 29 | "**/__tests__/*" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /tests/build/Dockerfile: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | FROM node:12 17 | 18 | WORKDIR /app 19 | 20 | ADD . /app 21 | 22 | RUN npm install request && npm install && npm run build 23 | 24 | ADD tests/build/main.ts /test/main.ts 25 | ADD tests/build/package.json /test/package.json 26 | 27 | WORKDIR /test 28 | 29 | RUN npm install && npm install /app && npm run build 30 | -------------------------------------------------------------------------------- /tests/build/main.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import agent from 'skywalking-backend-js'; 21 | 22 | agent.start({ 23 | serviceName: 'server', 24 | maxBufferSize: 1000, 25 | }); 26 | -------------------------------------------------------------------------------- /tests/build/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "build": "tsc main.ts" 6 | }, 7 | "author": "", 8 | "license": "ISC", 9 | "dependencies": { 10 | "tslib": "^2.1.0" 11 | }, 12 | "devDependencies": { 13 | "@types/node": "^14.14.21", 14 | "typescript": "^4.1.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/build/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "rootDir": ".", 5 | "baseUrl": ".", 6 | "outDir": ".", 7 | "resolveJsonModule": true, 8 | "composite": true, 9 | "esModuleInterop": true 10 | }, 11 | "files": [ 12 | "package.json" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tests/plugins/axios/client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import agent from '../../../src'; 22 | import axios from 'axios'; 23 | 24 | agent.start({ 25 | serviceName: 'client', 26 | maxBufferSize: 1000, 27 | }); 28 | 29 | const server = http.createServer(async (req, res) => { 30 | await axios 31 | .get(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`) 32 | .then((r) => res.end(JSON.stringify(r.data))) 33 | .catch(err => res.end(JSON.stringify(err))); 34 | }); 35 | 36 | server.listen(5001, () => console.info('Listening on port 5001...')); 37 | -------------------------------------------------------------------------------- /tests/plugins/axios/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: '2.1' 19 | 20 | services: 21 | collector: 22 | extends: 23 | file: ../common/base-compose.yml 24 | service: collector 25 | networks: 26 | - traveling-light 27 | 28 | server: 29 | extends: 30 | file: ../common/base-compose.yml 31 | service: agent 32 | ports: 33 | - 5000:5000 34 | volumes: 35 | - .:/app/tests/plugins/express 36 | healthcheck: 37 | test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000" ] 38 | interval: 5s 39 | timeout: 60s 40 | retries: 120 41 | entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/axios/server.ts' ] 42 | depends_on: 43 | collector: 44 | condition: service_healthy 45 | 46 | client: 47 | extends: 48 | file: ../common/base-compose.yml 49 | service: agent 50 | ports: 51 | - 5001:5001 52 | environment: 53 | SERVER: server:5000 54 | healthcheck: 55 | test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001" ] 56 | interval: 5s 57 | timeout: 60s 58 | retries: 120 59 | entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/axios/client.ts' ] 60 | depends_on: 61 | server: 62 | condition: service_healthy 63 | 64 | networks: 65 | traveling-light: 66 | -------------------------------------------------------------------------------- /tests/plugins/axios/server.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import agent from '../../../src'; 21 | import * as http from 'http'; 22 | import axios from 'axios'; 23 | 24 | agent.start({ 25 | serviceName: 'server', 26 | maxBufferSize: 1000, 27 | }); 28 | 29 | const server = http.createServer(async (req, res) => { 30 | const r = await axios.get('http://httpbin.org/json'); 31 | res.end(JSON.stringify(r.data)); 32 | }); 33 | 34 | server.listen(5000, () => console.info('Listening on port 5000...')); 35 | -------------------------------------------------------------------------------- /tests/plugins/axios/test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as path from 'path'; 21 | import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; 22 | import axios from 'axios'; 23 | import waitForExpect from 'wait-for-expect'; 24 | import { promises as fs } from 'fs'; 25 | 26 | const rootDir = path.resolve(__dirname); 27 | 28 | describe('plugin tests', () => { 29 | let compose: StartedDockerComposeEnvironment; 30 | 31 | beforeAll(async () => { 32 | compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') 33 | .withWaitStrategy('client', Wait.forHealthCheck()) 34 | .up(); 35 | }); 36 | 37 | afterAll(async () => { 38 | await compose.down(); 39 | }); 40 | 41 | it(__filename, async () => { 42 | await waitForExpect(async () => expect((await axios.get('http://localhost:5001/axios')).status).toBe(200)); 43 | 44 | const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); 45 | 46 | try { 47 | await waitForExpect(async () => 48 | expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), 49 | ); 50 | } catch (e) { 51 | const actualData = (await axios.get('http://localhost:12800/receiveData')).data; 52 | console.info({ actualData }); 53 | throw e; 54 | } 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /tests/plugins/common/Dockerfile.agent: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | ARG SW_NODE_VERSION 17 | 18 | FROM node:${SW_NODE_VERSION} 19 | 20 | ARG ROOT=. 21 | 22 | WORKDIR /app 23 | 24 | ADD $ROOT /app 25 | 26 | RUN npm install request && npm install 27 | -------------------------------------------------------------------------------- /tests/plugins/common/base-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: '2.1' 19 | 20 | services: 21 | collector: 22 | image: ghcr.io/apache/skywalking-agent-test-tool/mock-collector:fa81b1b6d9caef484a65b5019efa28cac4e3d21d 23 | ports: 24 | - 12800:12800 25 | networks: 26 | - traveling-light 27 | healthcheck: 28 | test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/12800" ] 29 | interval: 5s 30 | timeout: 60s 31 | retries: 120 32 | 33 | agent: 34 | build: 35 | context: ../../../ 36 | dockerfile: tests/plugins/common/Dockerfile.agent 37 | args: 38 | - SW_NODE_VERSION=${SW_NODE_VERSION:-latest} 39 | environment: 40 | SW_AGENT_COLLECTOR_BACKEND_SERVICES: collector:19876 41 | networks: 42 | - traveling-light 43 | 44 | networks: 45 | traveling-light: 46 | -------------------------------------------------------------------------------- /tests/plugins/express/client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import agent from '../../../src'; 22 | import express from 'express'; 23 | 24 | agent.start({ 25 | serviceName: 'client', 26 | maxBufferSize: 1000, 27 | }); 28 | 29 | const app = express(); 30 | 31 | const testRouter = express.Router(); 32 | app.use('/test', testRouter); 33 | 34 | testRouter.get('/express', (req, res) => { 35 | http 36 | .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { 37 | let data = ''; 38 | r.on('data', (chunk) => (data += chunk)); 39 | r.on('end', () => res.send(data)); 40 | }) 41 | .end(); 42 | }); 43 | 44 | app.listen(5001, () => console.info('Listening on port 5001...')); 45 | -------------------------------------------------------------------------------- /tests/plugins/express/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: '2.1' 19 | 20 | services: 21 | collector: 22 | extends: 23 | file: ../common/base-compose.yml 24 | service: collector 25 | networks: 26 | - traveling-light 27 | 28 | server: 29 | extends: 30 | file: ../common/base-compose.yml 31 | service: agent 32 | ports: 33 | - 5000:5000 34 | volumes: 35 | - .:/app/tests/plugins/express 36 | healthcheck: 37 | test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000" ] 38 | interval: 5s 39 | timeout: 60s 40 | retries: 120 41 | entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/express/server.ts' ] 42 | depends_on: 43 | collector: 44 | condition: service_healthy 45 | 46 | client: 47 | extends: 48 | file: ../common/base-compose.yml 49 | service: agent 50 | ports: 51 | - 5001:5001 52 | environment: 53 | SERVER: server:5000 54 | healthcheck: 55 | test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001" ] 56 | interval: 5s 57 | timeout: 60s 58 | retries: 120 59 | entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/express/client.ts' ] 60 | depends_on: 61 | server: 62 | condition: service_healthy 63 | 64 | networks: 65 | traveling-light: 66 | -------------------------------------------------------------------------------- /tests/plugins/express/server.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import agent from '../../../src'; 21 | import * as http from 'http'; 22 | 23 | import express from 'express'; 24 | 25 | agent.start({ 26 | serviceName: 'server', 27 | maxBufferSize: 1000, 28 | }); 29 | const app = express(); 30 | 31 | app.get('/express', (req, res) => { 32 | http 33 | .request('http://httpbin.org/json', (r) => { 34 | let data = ''; 35 | r.on('data', (chunk) => (data += chunk)); 36 | r.on('end', () => res.send(data)); 37 | }) 38 | .end(); 39 | }); 40 | 41 | app.listen(5000, () => console.info('Listening on port 5000...')); 42 | -------------------------------------------------------------------------------- /tests/plugins/express/test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as path from 'path'; 21 | import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; 22 | import axios from 'axios'; 23 | import waitForExpect from 'wait-for-expect'; 24 | import { promises as fs } from 'fs'; 25 | 26 | const rootDir = path.resolve(__dirname); 27 | 28 | describe('plugin tests', () => { 29 | let compose: StartedDockerComposeEnvironment; 30 | 31 | beforeAll(async () => { 32 | compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') 33 | .withWaitStrategy('client', Wait.forHealthCheck()) 34 | .up(); 35 | }); 36 | 37 | afterAll(async () => { 38 | await compose.down(); 39 | }); 40 | 41 | it(__filename, async () => { 42 | await waitForExpect(async () => expect((await axios.get('http://localhost:5001/test/express')).status).toBe(200)); 43 | 44 | const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); 45 | 46 | try { 47 | await waitForExpect(async () => 48 | expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), 49 | ); 50 | } catch (e) { 51 | const actualData = (await axios.get('http://localhost:12800/receiveData')).data; 52 | console.info({ actualData }); 53 | throw e; 54 | } 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /tests/plugins/http/client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import agent from '../../../src'; 22 | 23 | agent.start({ 24 | serviceName: 'client', 25 | maxBufferSize: 1000, 26 | }); 27 | 28 | const server = http.createServer((req, res) => { 29 | http 30 | .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { 31 | let data = ''; 32 | r.on('data', (chunk) => (data += chunk)); 33 | r.on('end', () => res.end(data)); 34 | }) 35 | .end(); 36 | }); 37 | 38 | server.listen(5001, () => console.info('Listening on port 5001...')); 39 | -------------------------------------------------------------------------------- /tests/plugins/http/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: '2.1' 19 | 20 | services: 21 | collector: 22 | extends: 23 | file: ../common/base-compose.yml 24 | service: collector 25 | networks: 26 | - traveling-light 27 | 28 | server: 29 | extends: 30 | file: ../common/base-compose.yml 31 | service: agent 32 | ports: 33 | - 5000:5000 34 | volumes: 35 | - .:/app/tests/plugins/http 36 | healthcheck: 37 | test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000" ] 38 | interval: 5s 39 | timeout: 60s 40 | retries: 120 41 | entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/http/server.ts' ] 42 | depends_on: 43 | collector: 44 | condition: service_healthy 45 | 46 | client: 47 | extends: 48 | file: ../common/base-compose.yml 49 | service: agent 50 | ports: 51 | - 5001:5001 52 | environment: 53 | SERVER: server:5000 54 | healthcheck: 55 | test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001" ] 56 | interval: 5s 57 | timeout: 60s 58 | retries: 120 59 | entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/http/client.ts' ] 60 | depends_on: 61 | server: 62 | condition: service_healthy 63 | 64 | networks: 65 | traveling-light: 66 | -------------------------------------------------------------------------------- /tests/plugins/http/server.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import agent from '../../../src'; 21 | import * as http from 'http'; 22 | 23 | agent.start({ 24 | serviceName: 'server', 25 | maxBufferSize: 1000, 26 | }); 27 | 28 | const server = http.createServer((req, res) => { 29 | http 30 | .request('http://httpbin.org/json', (r) => { 31 | let data = ''; 32 | r.on('data', (chunk) => (data += chunk)); 33 | r.on('end', () => res.end(data)); 34 | }) 35 | .end(); 36 | }); 37 | 38 | server.listen(5000, () => console.info('Listening on port 5000...')); 39 | -------------------------------------------------------------------------------- /tests/plugins/http/test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as path from 'path'; 21 | import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; 22 | import axios from 'axios'; 23 | import waitForExpect from 'wait-for-expect'; 24 | import { promises as fs } from 'fs'; 25 | 26 | const rootDir = path.resolve(__dirname); 27 | 28 | describe('plugin tests', () => { 29 | let compose: StartedDockerComposeEnvironment; 30 | 31 | beforeAll(async () => { 32 | compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') 33 | .withWaitStrategy('client', Wait.forHealthCheck()) 34 | .up(); 35 | }); 36 | 37 | afterAll(async () => { 38 | await compose.down(); 39 | }); 40 | 41 | it(__filename, async () => { 42 | await waitForExpect(async () => expect((await axios.get('http://localhost:5001/test')).status).toBe(200)); 43 | 44 | const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); 45 | 46 | try { 47 | await waitForExpect(async () => 48 | expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), 49 | ); 50 | } catch (e) { 51 | const actualData = (await axios.get('http://localhost:12800/receiveData')).data; 52 | console.info({ actualData }); 53 | throw e; 54 | } 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /tests/plugins/ioredis/client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import agent from '../../../src'; 22 | 23 | agent.start({ 24 | serviceName: 'client', 25 | maxBufferSize: 1000, 26 | }) 27 | 28 | const server = http.createServer((req, res) => { 29 | http 30 | .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { 31 | let data = ''; 32 | r.on('data', (chunk) => (data += chunk)); 33 | r.on('end', () => res.end(data)); 34 | }) 35 | .end(); 36 | }); 37 | 38 | server.listen(5001, () => console.info('Listening on port 5001...')); 39 | -------------------------------------------------------------------------------- /tests/plugins/ioredis/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: "2.1" 19 | 20 | services: 21 | collector: 22 | extends: 23 | file: ../common/base-compose.yml 24 | service: collector 25 | networks: 26 | - traveling-light 27 | 28 | redis: 29 | container_name: redis 30 | ports: 31 | - 6379:6379 32 | healthcheck: 33 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/6379"] 34 | interval: 5s 35 | timeout: 60s 36 | retries: 120 37 | image: "redis:latest" 38 | networks: 39 | - traveling-light 40 | 41 | server: 42 | extends: 43 | file: ../common/base-compose.yml 44 | service: agent 45 | ports: 46 | - 5000:5000 47 | environment: 48 | REDIS_HOST: redis 49 | volumes: 50 | - .:/app/tests/plugins/ioredis 51 | healthcheck: 52 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000"] 53 | interval: 5s 54 | timeout: 60s 55 | retries: 120 56 | entrypoint: 57 | ["bash", "-c", "npx ts-node /app/tests/plugins/ioredis/server.ts"] 58 | depends_on: 59 | collector: 60 | condition: service_healthy 61 | redis: 62 | condition: service_healthy 63 | 64 | client: 65 | extends: 66 | file: ../common/base-compose.yml 67 | service: agent 68 | ports: 69 | - 5001:5001 70 | environment: 71 | SERVER: server:5000 72 | healthcheck: 73 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001"] 74 | interval: 5s 75 | timeout: 60s 76 | retries: 120 77 | entrypoint: 78 | ["bash", "-c", "npx ts-node /app/tests/plugins/ioredis/client.ts"] 79 | depends_on: 80 | server: 81 | condition: service_healthy 82 | 83 | networks: 84 | traveling-light: 85 | -------------------------------------------------------------------------------- /tests/plugins/ioredis/server.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import Redis from 'ioredis'; 22 | import agent from '../../../src'; 23 | import assert from 'assert'; 24 | 25 | agent.start({ 26 | serviceName: 'server', 27 | maxBufferSize: 1000, 28 | }); 29 | 30 | const client = new Redis({ 31 | host: process.env.REDIS_HOST || 'redis', 32 | }); 33 | 34 | const server = http.createServer((req, res) => { 35 | (async () => { 36 | const cacheKey = 'now'; 37 | const now = '' + Date.now(); 38 | 39 | await client.set(cacheKey, now); 40 | const _now = await client.get(cacheKey); 41 | assert.strictEqual(now, _now); 42 | 43 | res.end(_now); 44 | })().catch((err: Error) => { 45 | res.statusCode = 500; 46 | res.end(err.message); 47 | }); 48 | }) 49 | 50 | server.listen(5000, () => console.info('Listening on port 5000...')); 51 | -------------------------------------------------------------------------------- /tests/plugins/ioredis/test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as path from 'path'; 21 | import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; 22 | import axios from 'axios'; 23 | import waitForExpect from 'wait-for-expect'; 24 | import { promises as fs } from 'fs'; 25 | 26 | const rootDir = path.resolve(__dirname); 27 | 28 | describe('plugin tests', () => { 29 | let compose: StartedDockerComposeEnvironment; 30 | 31 | beforeAll(async () => { 32 | compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') 33 | .withWaitStrategy('client', Wait.forHealthCheck()) 34 | .withWaitStrategy('redis', Wait.forHealthCheck()) 35 | .up(); 36 | }); 37 | 38 | afterAll(async () => { 39 | await compose.down(); 40 | }); 41 | 42 | it(__filename, async () => { 43 | await waitForExpect(async () => expect((await axios.get('http://localhost:5001/ioredis')).status).toBe(200)); 44 | 45 | const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); 46 | 47 | try { 48 | await waitForExpect(async () => 49 | expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), 50 | ); 51 | } catch (e) { 52 | const actualData = (await axios.get('http://localhost:12800/receiveData')).data; 53 | console.info({ actualData }); 54 | throw e; 55 | } 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /tests/plugins/mongodb/client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import agent from '../../../src'; 22 | 23 | process.env.SW_AGENT_LOGGING_LEVEL = 'ERROR'; 24 | 25 | agent.start({ 26 | serviceName: 'client', 27 | maxBufferSize: 1000, 28 | }) 29 | 30 | const server = http.createServer((req, res) => { 31 | http 32 | .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { 33 | let data = ''; 34 | r.on('data', (chunk) => (data += chunk)); 35 | r.on('end', () => res.end(data)); 36 | }) 37 | .end(); 38 | }); 39 | 40 | server.listen(5001, () => console.info('Listening on port 5001...')); 41 | -------------------------------------------------------------------------------- /tests/plugins/mongodb/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: "2.1" 19 | 20 | services: 21 | collector: 22 | extends: 23 | file: ../common/base-compose.yml 24 | service: collector 25 | networks: 26 | - traveling-light 27 | 28 | mongo: 29 | container_name: mongo 30 | environment: 31 | MONGO_INITDB_ROOT_USERNAME: "root" 32 | MONGO_INITDB_ROOT_PASSWORD: "root" 33 | MONGO_INITDB_DATABASE: "admin" 34 | ports: 35 | - 27017:27017 36 | volumes: 37 | - ./init:/docker-entrypoint-initdb.d 38 | healthcheck: 39 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/27017"] 40 | interval: 5s 41 | timeout: 60s 42 | retries: 120 43 | image: "mongo:latest" 44 | networks: 45 | - traveling-light 46 | 47 | server: 48 | extends: 49 | file: ../common/base-compose.yml 50 | service: agent 51 | ports: 52 | - 5000:5000 53 | environment: 54 | MONGO_HOST: mongo 55 | volumes: 56 | - .:/app/tests/plugins/pg 57 | healthcheck: 58 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000"] 59 | interval: 5s 60 | timeout: 60s 61 | retries: 120 62 | entrypoint: 63 | ["bash", "-c", "npx ts-node /app/tests/plugins/pg/server.ts"] 64 | depends_on: 65 | collector: 66 | condition: service_healthy 67 | mongo: 68 | condition: service_healthy 69 | 70 | client: 71 | extends: 72 | file: ../common/base-compose.yml 73 | service: agent 74 | ports: 75 | - 5001:5001 76 | environment: 77 | SERVER: server:5000 78 | healthcheck: 79 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001"] 80 | interval: 5s 81 | timeout: 60s 82 | retries: 120 83 | entrypoint: 84 | ["bash", "-c", "npx ts-node /app/tests/plugins/pg/client.ts"] 85 | depends_on: 86 | server: 87 | condition: service_healthy 88 | 89 | networks: 90 | traveling-light: 91 | -------------------------------------------------------------------------------- /tests/plugins/mongodb/init/init.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | db.createCollection('docs'); 21 | -------------------------------------------------------------------------------- /tests/plugins/mongodb/server.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import {MongoClient} from 'mongodb'; 22 | import agent from '../../../src'; 23 | 24 | process.env.SW_AGENT_LOGGING_LEVEL = 'ERROR'; 25 | 26 | agent.start({ 27 | serviceName: 'server', 28 | maxBufferSize: 1000, 29 | }); 30 | 31 | const server = http.createServer(async (req, res) => { 32 | await new Promise((resolve, reject) => { 33 | MongoClient.connect(`mongodb://root:root@${process.env.MONGO_HOST}:27017`, {useUnifiedTopology: true}, function(err: any, client: any) { 34 | if (err) { 35 | res.end(`${err}`); 36 | resolve(null); 37 | } else { 38 | client.db('admin').collection('docs').findOne().then( 39 | (resDB: any) => { 40 | res.end(`${resDB}`); 41 | resolve(null); 42 | client.close(); 43 | }, 44 | (err: any) => { 45 | res.end(`${err}`); 46 | resolve(null); 47 | client.close(); 48 | }, 49 | ); 50 | } 51 | }); 52 | }); 53 | }); 54 | 55 | server.listen(5000, () => console.info('Listening on port 5000...')); 56 | -------------------------------------------------------------------------------- /tests/plugins/mongodb/test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as path from 'path'; 21 | import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; 22 | import axios from 'axios'; 23 | import waitForExpect from 'wait-for-expect'; 24 | import { promises as fs } from 'fs'; 25 | 26 | const rootDir = path.resolve(__dirname); 27 | 28 | describe('plugin tests', () => { 29 | let compose: StartedDockerComposeEnvironment; 30 | 31 | beforeAll(async () => { 32 | compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') 33 | .withWaitStrategy('client', Wait.forHealthCheck()) 34 | .withWaitStrategy('mongo', Wait.forHealthCheck()) 35 | .up(); 36 | }); 37 | 38 | afterAll(async () => { 39 | await compose.down(); 40 | }); 41 | 42 | it(__filename, async () => { 43 | await waitForExpect(async () => expect((await axios.get('http://localhost:5001/mongo')).status).toBe(200)); 44 | 45 | const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); 46 | 47 | try { 48 | await waitForExpect(async () => 49 | expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), 50 | ); 51 | } catch (e) { 52 | const actualData = (await axios.get('http://localhost:12800/receiveData')).data; 53 | console.info({ actualData }); 54 | throw e; 55 | } 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /tests/plugins/mongoose/client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import agent from '../../../src'; 22 | 23 | process.env.SW_AGENT_LOGGING_LEVEL = 'ERROR'; 24 | 25 | agent.start({ 26 | serviceName: 'client', 27 | maxBufferSize: 1000, 28 | }) 29 | 30 | const server = http.createServer((req, res) => { 31 | http 32 | .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { 33 | let data = ''; 34 | r.on('data', (chunk) => (data += chunk)); 35 | r.on('end', () => res.end(data)); 36 | }) 37 | .end(); 38 | }); 39 | 40 | server.listen(5001, () => console.info('Listening on port 5001...')); 41 | -------------------------------------------------------------------------------- /tests/plugins/mongoose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: "2.1" 19 | 20 | services: 21 | collector: 22 | extends: 23 | file: ../common/base-compose.yml 24 | service: collector 25 | networks: 26 | - traveling-light 27 | 28 | mongo: 29 | container_name: mongo 30 | environment: 31 | MONGO_INITDB_ROOT_USERNAME: "root" 32 | MONGO_INITDB_ROOT_PASSWORD: "root" 33 | MONGO_INITDB_DATABASE: "admin" 34 | ports: 35 | - 27017:27017 36 | volumes: 37 | - ./init:/docker-entrypoint-initdb.d 38 | healthcheck: 39 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/27017"] 40 | interval: 5s 41 | timeout: 60s 42 | retries: 120 43 | image: "mongo:latest" 44 | networks: 45 | - traveling-light 46 | 47 | server: 48 | extends: 49 | file: ../common/base-compose.yml 50 | service: agent 51 | ports: 52 | - 5000:5000 53 | environment: 54 | MONGO_HOST: mongo 55 | volumes: 56 | - .:/app/tests/plugins/pg 57 | healthcheck: 58 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000"] 59 | interval: 5s 60 | timeout: 60s 61 | retries: 120 62 | entrypoint: 63 | ["bash", "-c", "npx ts-node /app/tests/plugins/pg/server.ts"] 64 | depends_on: 65 | collector: 66 | condition: service_healthy 67 | mongo: 68 | condition: service_healthy 69 | 70 | client: 71 | extends: 72 | file: ../common/base-compose.yml 73 | service: agent 74 | ports: 75 | - 5001:5001 76 | environment: 77 | SERVER: server:5000 78 | healthcheck: 79 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001"] 80 | interval: 5s 81 | timeout: 60s 82 | retries: 120 83 | entrypoint: 84 | ["bash", "-c", "npx ts-node /app/tests/plugins/pg/client.ts"] 85 | depends_on: 86 | server: 87 | condition: service_healthy 88 | 89 | networks: 90 | traveling-light: 91 | -------------------------------------------------------------------------------- /tests/plugins/mongoose/init/init.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | db.createCollection('docs'); 21 | -------------------------------------------------------------------------------- /tests/plugins/mongoose/server.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import mongoose from 'mongoose'; 22 | import agent from '../../../src'; 23 | 24 | process.env.SW_AGENT_LOGGING_LEVEL = 'ERROR'; 25 | 26 | agent.start({ 27 | serviceName: 'server', 28 | maxBufferSize: 1000, 29 | }); 30 | 31 | const server = http.createServer(async (req, res) => { 32 | await new Promise((resolve, reject) => { 33 | mongoose.connect(`mongodb://root:root@${process.env.MONGO_HOST}:27017/admin`, { 34 | useNewUrlParser: true, 35 | useUnifiedTopology: true, 36 | useFindAndModify: false, 37 | 38 | }).then(() => { 39 | const Test = new mongoose.Schema({ 40 | title: String 41 | }); 42 | 43 | const modelTest = mongoose.model('Test', Test); 44 | 45 | modelTest.find().then( 46 | (result: any) => { 47 | res.end(`${result}`); 48 | resolve(null); 49 | mongoose.connection.close(); 50 | }, 51 | 52 | (err: Error) => { 53 | res.end(`${err}`); 54 | resolve(null); 55 | mongoose.connection.close(); 56 | }, 57 | ); 58 | 59 | }).catch((err: Error) => { 60 | res.end(`${err}`); 61 | resolve(null); 62 | }); 63 | }); 64 | }); 65 | 66 | server.listen(5000, () => console.info('Listening on port 5000...')); 67 | -------------------------------------------------------------------------------- /tests/plugins/mongoose/test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as path from 'path'; 21 | import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; 22 | import axios from 'axios'; 23 | import waitForExpect from 'wait-for-expect'; 24 | import { promises as fs } from 'fs'; 25 | 26 | const rootDir = path.resolve(__dirname); 27 | 28 | describe('plugin tests', () => { 29 | let compose: StartedDockerComposeEnvironment; 30 | 31 | beforeAll(async () => { 32 | compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') 33 | .withWaitStrategy('client', Wait.forHealthCheck()) 34 | .withWaitStrategy('mongo', Wait.forHealthCheck()) 35 | .up(); 36 | }); 37 | 38 | afterAll(async () => { 39 | await compose.down(); 40 | }); 41 | 42 | it(__filename, async () => { 43 | await waitForExpect(async () => expect((await axios.get('http://localhost:5001/mongoose')).status).toBe(200)); 44 | 45 | const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); 46 | 47 | try { 48 | await waitForExpect(async () => 49 | expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), 50 | ); 51 | } catch (e) { 52 | const actualData = (await axios.get('http://localhost:12800/receiveData')).data; 53 | console.info({ actualData }); 54 | throw e; 55 | } 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /tests/plugins/mysql/client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import agent from '../../../src'; 22 | 23 | agent.start({ 24 | serviceName: 'client', 25 | maxBufferSize: 1000, 26 | }) 27 | 28 | const server = http.createServer((req, res) => { 29 | http 30 | .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { 31 | let data = ''; 32 | r.on('data', (chunk) => (data += chunk)); 33 | r.on('end', () => res.end(data)); 34 | }) 35 | .end(); 36 | }); 37 | 38 | server.listen(5001, () => console.info('Listening on port 5001...')); 39 | -------------------------------------------------------------------------------- /tests/plugins/mysql/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: "2.1" 19 | 20 | services: 21 | collector: 22 | extends: 23 | file: ../common/base-compose.yml 24 | service: collector 25 | networks: 26 | - traveling-light 27 | 28 | mysql: 29 | environment: 30 | MYSQL_ROOT_PASSWORD: "root" 31 | MYSQL_DATABASE: "test" 32 | ports: 33 | - 3306:3306 34 | volumes: 35 | - ./init:/docker-entrypoint-initdb.d 36 | healthcheck: 37 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306"] 38 | interval: 5s 39 | timeout: 60s 40 | retries: 120 41 | image: "docker.io/mysql:5.7.33" 42 | networks: 43 | - traveling-light 44 | 45 | server: 46 | extends: 47 | file: ../common/base-compose.yml 48 | service: agent 49 | ports: 50 | - 5000:5000 51 | environment: 52 | MYSQL_HOST: mysql 53 | volumes: 54 | - .:/app/tests/plugins/mysql 55 | healthcheck: 56 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000"] 57 | interval: 5s 58 | timeout: 60s 59 | retries: 120 60 | entrypoint: 61 | ["bash", "-c", "npx ts-node /app/tests/plugins/mysql/server.ts"] 62 | depends_on: 63 | collector: 64 | condition: service_healthy 65 | mysql: 66 | condition: service_healthy 67 | 68 | client: 69 | extends: 70 | file: ../common/base-compose.yml 71 | service: agent 72 | ports: 73 | - 5001:5001 74 | environment: 75 | SERVER: server:5000 76 | healthcheck: 77 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001"] 78 | interval: 5s 79 | timeout: 60s 80 | retries: 120 81 | entrypoint: 82 | ["bash", "-c", "npx ts-node /app/tests/plugins/mysql/client.ts"] 83 | depends_on: 84 | server: 85 | condition: service_healthy 86 | 87 | networks: 88 | traveling-light: 89 | -------------------------------------------------------------------------------- /tests/plugins/mysql/expected.data.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | segmentItems: 19 | - serviceName: server 20 | segmentSize: 1 21 | segments: 22 | - segmentId: not null 23 | spans: 24 | - operationName: mysql/query 25 | operationId: 0 26 | parentSpanId: 0 27 | spanId: 1 28 | spanLayer: Database 29 | startTime: gt 0 30 | endTime: gt 0 31 | componentId: 5 32 | spanType: Exit 33 | peer: mysql:3306 34 | skipAnalysis: false 35 | tags: 36 | - key: db.type 37 | value: Mysql 38 | - key: db.instance 39 | value: test 40 | - key: db.statement 41 | value: SELECT * FROM `user` WHERE `name` = "u1" 42 | - operationName: GET:/mysql 43 | operationId: 0 44 | parentSpanId: -1 45 | spanId: 0 46 | spanLayer: Http 47 | startTime: gt 0 48 | endTime: gt 0 49 | componentId: 49 50 | spanType: Entry 51 | peer: not null 52 | skipAnalysis: false 53 | tags: 54 | - key: coldStart 55 | value: 'true' 56 | - key: http.url 57 | value: http://server:5000/mysql 58 | - key: http.method 59 | value: GET 60 | - key: http.status_code 61 | value: "200" 62 | - key: http.status.msg 63 | value: OK 64 | refs: 65 | - parentEndpoint: "" 66 | networkAddress: server:5000 67 | refType: CrossProcess 68 | parentSpanId: 1 69 | parentTraceSegmentId: not null 70 | parentServiceInstance: not null 71 | parentService: client 72 | traceId: not null 73 | - serviceName: client 74 | segmentSize: 1 75 | segments: 76 | - segmentId: not null 77 | spans: 78 | - operationName: GET:/mysql 79 | operationId: 0 80 | parentSpanId: -1 81 | spanId: 0 82 | spanLayer: Http 83 | startTime: gt 0 84 | endTime: gt 0 85 | componentId: 49 86 | spanType: Entry 87 | peer: not null 88 | skipAnalysis: false 89 | tags: 90 | - key: coldStart 91 | value: 'true' 92 | - key: http.url 93 | value: http://localhost:5001/mysql 94 | - key: http.method 95 | value: GET 96 | - key: http.status_code 97 | value: "200" 98 | - key: http.status.msg 99 | value: OK 100 | - operationName: /mysql 101 | operationId: 0 102 | parentSpanId: 0 103 | spanId: 1 104 | spanLayer: Http 105 | startTime: gt 0 106 | endTime: gt 0 107 | componentId: 2 108 | spanType: Exit 109 | peer: server:5000 110 | skipAnalysis: false 111 | tags: 112 | - key: http.url 113 | value: http://server:5000/mysql 114 | - key: http.method 115 | value: GET 116 | - key: http.status_code 117 | value: "200" 118 | - key: http.status.msg 119 | value: OK 120 | -------------------------------------------------------------------------------- /tests/plugins/mysql/init/init.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one or more 2 | -- contributor license agreements. See the NOTICE file distributed with 3 | -- this work for additional information regarding copyright ownership. 4 | -- The ASF licenses this file to You under the Apache License, Version 2.0 5 | -- (the "License"); you may not use this file except in compliance with 6 | -- the License. You may obtain a copy of the License at 7 | -- 8 | -- http://www.apache.org/licenses/LICENSE-2.0 9 | -- 10 | -- Unless required by applicable law or agreed to in writing, software 11 | -- distributed under the License is distributed on an "AS IS" BASIS, 12 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | -- See the License for the specific language governing permissions and 14 | -- limitations under the License. 15 | 16 | use test; 17 | 18 | CREATE TABLE IF NOT EXISTS `user`( 19 | `id` INT UNSIGNED AUTO_INCREMENT, 20 | `name` VARCHAR(100) NOT NULL, 21 | PRIMARY KEY( `id` ) 22 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 23 | -------------------------------------------------------------------------------- /tests/plugins/mysql/server.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import mysql from 'mysql'; 22 | import agent from '../../../src'; 23 | 24 | agent.start({ 25 | serviceName: 'server', 26 | maxBufferSize: 1000, 27 | }) 28 | 29 | const server = http.createServer((req, res) => { 30 | const connection = mysql.createConnection({ 31 | host: process.env.MYSQL_HOST || 'mysql', 32 | user: 'root', 33 | password: 'root', 34 | database: 'test' 35 | }); 36 | connection.query( 37 | 'SELECT * FROM `user` WHERE `name` = "u1"', 38 | function (err: any, results: any, fields: any) { 39 | res.end(JSON.stringify({ 40 | results, 41 | fields 42 | })) 43 | } 44 | ); 45 | }) 46 | 47 | server.listen(5000, () => console.info('Listening on port 5000...')); 48 | -------------------------------------------------------------------------------- /tests/plugins/mysql/test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as path from 'path'; 21 | import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; 22 | import axios from 'axios'; 23 | import waitForExpect from 'wait-for-expect'; 24 | import { promises as fs } from 'fs'; 25 | 26 | const rootDir = path.resolve(__dirname); 27 | 28 | describe('plugin tests', () => { 29 | let compose: StartedDockerComposeEnvironment; 30 | 31 | beforeAll(async () => { 32 | compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') 33 | .withWaitStrategy('client', Wait.forHealthCheck()) 34 | .withWaitStrategy('mysql', Wait.forHealthCheck()) 35 | .up(); 36 | }); 37 | 38 | afterAll(async () => { 39 | await compose.down(); 40 | }); 41 | 42 | it(__filename, async () => { 43 | await waitForExpect(async () => expect((await axios.get('http://localhost:5001/mysql')).status).toBe(200)); 44 | 45 | const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); 46 | 47 | try { 48 | await waitForExpect(async () => 49 | expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), 50 | ); 51 | } catch (e) { 52 | const actualData = (await axios.get('http://localhost:12800/receiveData')).data; 53 | console.info({ actualData }); 54 | throw e; 55 | } 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /tests/plugins/mysql2/client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import agent from '../../../src'; 22 | 23 | agent.start({ 24 | serviceName: 'client', 25 | maxBufferSize: 1000, 26 | }) 27 | 28 | const server = http.createServer((req, res) => { 29 | http 30 | .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { 31 | let data = ''; 32 | r.on('data', (chunk) => (data += chunk)); 33 | r.on('end', () => res.end(data)); 34 | }) 35 | .end(); 36 | }); 37 | 38 | server.listen(5001, () => console.info('Listening on port 5001...')); 39 | -------------------------------------------------------------------------------- /tests/plugins/mysql2/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: "2.1" 19 | 20 | services: 21 | collector: 22 | extends: 23 | file: ../common/base-compose.yml 24 | service: collector 25 | networks: 26 | - traveling-light 27 | 28 | mysql: 29 | environment: 30 | MYSQL_ROOT_PASSWORD: "root" 31 | MYSQL_DATABASE: "test" 32 | ports: 33 | - 3306 34 | volumes: 35 | - ./init:/docker-entrypoint-initdb.d 36 | healthcheck: 37 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306"] 38 | interval: 5s 39 | timeout: 60s 40 | retries: 120 41 | image: "docker.io/mysql:5.7.33" 42 | networks: 43 | - traveling-light 44 | 45 | server: 46 | extends: 47 | file: ../common/base-compose.yml 48 | service: agent 49 | ports: 50 | - 5000:5000 51 | environment: 52 | MYSQL_HOST: mysql 53 | volumes: 54 | - .:/app/tests/plugins/mysql2 55 | healthcheck: 56 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000"] 57 | interval: 5s 58 | timeout: 60s 59 | retries: 120 60 | entrypoint: 61 | ["bash", "-c", "npx ts-node /app/tests/plugins/mysql/server.ts"] 62 | depends_on: 63 | collector: 64 | condition: service_healthy 65 | mysql: 66 | condition: service_healthy 67 | 68 | client: 69 | extends: 70 | file: ../common/base-compose.yml 71 | service: agent 72 | ports: 73 | - 5001:5001 74 | environment: 75 | SERVER: server:5000 76 | healthcheck: 77 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001"] 78 | interval: 5s 79 | timeout: 60s 80 | retries: 120 81 | entrypoint: 82 | ["bash", "-c", "npx ts-node /app/tests/plugins/mysql/client.ts"] 83 | depends_on: 84 | server: 85 | condition: service_healthy 86 | 87 | networks: 88 | traveling-light: 89 | -------------------------------------------------------------------------------- /tests/plugins/mysql2/expected.data.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | segmentItems: 19 | - serviceName: server 20 | segmentSize: 1 21 | segments: 22 | - segmentId: not null 23 | spans: 24 | - operationName: mysql/query 25 | operationId: 0 26 | parentSpanId: 0 27 | spanId: 1 28 | spanLayer: Database 29 | startTime: gt 0 30 | endTime: gt 0 31 | componentId: 5 32 | spanType: Exit 33 | peer: mysql:3306 34 | skipAnalysis: false 35 | tags: 36 | - key: db.type 37 | value: Mysql 38 | - key: db.instance 39 | value: test 40 | - key: db.statement 41 | value: SELECT * FROM `user` WHERE `name` = "u1" 42 | - operationName: GET:/mysql 43 | operationId: 0 44 | parentSpanId: -1 45 | spanId: 0 46 | spanLayer: Http 47 | startTime: gt 0 48 | endTime: gt 0 49 | componentId: 49 50 | spanType: Entry 51 | peer: not null 52 | skipAnalysis: false 53 | tags: 54 | - key: coldStart 55 | value: 'true' 56 | - key: http.url 57 | value: http://server:5000/mysql 58 | - key: http.method 59 | value: GET 60 | - key: http.status_code 61 | value: "200" 62 | - key: http.status.msg 63 | value: OK 64 | refs: 65 | - parentEndpoint: "" 66 | networkAddress: server:5000 67 | refType: CrossProcess 68 | parentSpanId: 1 69 | parentTraceSegmentId: not null 70 | parentServiceInstance: not null 71 | parentService: client 72 | traceId: not null 73 | - serviceName: client 74 | segmentSize: 1 75 | segments: 76 | - segmentId: not null 77 | spans: 78 | - operationName: GET:/mysql 79 | operationId: 0 80 | parentSpanId: -1 81 | spanId: 0 82 | spanLayer: Http 83 | startTime: gt 0 84 | endTime: gt 0 85 | componentId: 49 86 | spanType: Entry 87 | peer: not null 88 | skipAnalysis: false 89 | tags: 90 | - key: coldStart 91 | value: 'true' 92 | - key: http.url 93 | value: http://localhost:5001/mysql 94 | - key: http.method 95 | value: GET 96 | - key: http.status_code 97 | value: "200" 98 | - key: http.status.msg 99 | value: OK 100 | - operationName: /mysql 101 | operationId: 0 102 | parentSpanId: 0 103 | spanId: 1 104 | spanLayer: Http 105 | startTime: gt 0 106 | endTime: gt 0 107 | componentId: 2 108 | spanType: Exit 109 | peer: server:5000 110 | skipAnalysis: false 111 | tags: 112 | - key: http.url 113 | value: http://server:5000/mysql 114 | - key: http.method 115 | value: GET 116 | - key: http.status_code 117 | value: "200" 118 | - key: http.status.msg 119 | value: OK 120 | -------------------------------------------------------------------------------- /tests/plugins/mysql2/init/init.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one or more 2 | -- contributor license agreements. See the NOTICE file distributed with 3 | -- this work for additional information regarding copyright ownership. 4 | -- The ASF licenses this file to You under the Apache License, Version 2.0 5 | -- (the "License"); you may not use this file except in compliance with 6 | -- the License. You may obtain a copy of the License at 7 | -- 8 | -- http://www.apache.org/licenses/LICENSE-2.0 9 | -- 10 | -- Unless required by applicable law or agreed to in writing, software 11 | -- distributed under the License is distributed on an "AS IS" BASIS, 12 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | -- See the License for the specific language governing permissions and 14 | -- limitations under the License. 15 | 16 | use test; 17 | 18 | CREATE TABLE IF NOT EXISTS `user`( 19 | `id` INT UNSIGNED AUTO_INCREMENT, 20 | `name` VARCHAR(100) NOT NULL, 21 | PRIMARY KEY( `id` ) 22 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 23 | -------------------------------------------------------------------------------- /tests/plugins/mysql2/server.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import mysql from 'mysql2'; 22 | import agent from '../../../src'; 23 | 24 | agent.start({ 25 | serviceName: 'server', 26 | maxBufferSize: 1000, 27 | }) 28 | 29 | const server = http.createServer((req, res) => { 30 | const connection = mysql.createConnection({ 31 | host: process.env.MYSQL_HOST || 'mysql', 32 | user: 'root', 33 | password: 'root', 34 | database: 'test' 35 | }); 36 | connection.query( 37 | 'SELECT * FROM `user` WHERE `name` = "u1"', 38 | function (err: any, results: any, fields: any) { 39 | res.end(JSON.stringify({ 40 | results, 41 | fields 42 | })) 43 | } 44 | ); 45 | }) 46 | 47 | server.listen(5000, () => console.info('Listening on port 5000...')); 48 | -------------------------------------------------------------------------------- /tests/plugins/mysql2/test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as path from 'path'; 21 | import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; 22 | import axios from 'axios'; 23 | import waitForExpect from 'wait-for-expect'; 24 | import { promises as fs } from 'fs'; 25 | 26 | const rootDir = path.resolve(__dirname); 27 | 28 | describe('plugin tests', () => { 29 | let compose: StartedDockerComposeEnvironment; 30 | 31 | beforeAll(async () => { 32 | compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') 33 | .withWaitStrategy('client', Wait.forHealthCheck()) 34 | .withWaitStrategy('mysql', Wait.forHealthCheck()) 35 | .up(); 36 | }); 37 | 38 | afterAll(async () => { 39 | await compose.down(); 40 | }); 41 | 42 | it(__filename, async () => { 43 | await waitForExpect(async () => expect((await axios.get('http://localhost:5001/mysql')).status).toBe(200)); 44 | 45 | const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); 46 | 47 | try { 48 | await waitForExpect(async () => 49 | expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), 50 | ); 51 | } catch (e) { 52 | const actualData = (await axios.get('http://localhost:12800/receiveData')).data; 53 | console.info({ actualData }); 54 | throw e; 55 | } 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /tests/plugins/pg/client.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import agent from '../../../src'; 22 | 23 | agent.start({ 24 | serviceName: 'client', 25 | maxBufferSize: 1000, 26 | }) 27 | 28 | const server = http.createServer((req, res) => { 29 | http 30 | .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { 31 | let data = ''; 32 | r.on('data', (chunk) => (data += chunk)); 33 | r.on('end', () => res.end(data)); 34 | }) 35 | .end(); 36 | }); 37 | 38 | server.listen(5001, () => console.info('Listening on port 5001...')); 39 | -------------------------------------------------------------------------------- /tests/plugins/pg/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: "2.1" 19 | 20 | services: 21 | collector: 22 | extends: 23 | file: ../common/base-compose.yml 24 | service: collector 25 | networks: 26 | - traveling-light 27 | 28 | postgres: 29 | container_name: postgres 30 | environment: 31 | POSTGRES_USER: "root" 32 | POSTGRES_PASSWORD: "root" 33 | POSTGRES_DB: "test" 34 | ports: 35 | - 5432:5432 36 | volumes: 37 | - ./init:/docker-entrypoint-initdb.d 38 | healthcheck: 39 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5432"] 40 | interval: 5s 41 | timeout: 60s 42 | retries: 120 43 | image: "docker.io/postgres:13.2" 44 | networks: 45 | - traveling-light 46 | 47 | server: 48 | extends: 49 | file: ../common/base-compose.yml 50 | service: agent 51 | ports: 52 | - 5000:5000 53 | environment: 54 | POSTGRES_HOST: postgres 55 | volumes: 56 | - .:/app/tests/plugins/pg 57 | healthcheck: 58 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000"] 59 | interval: 5s 60 | timeout: 60s 61 | retries: 120 62 | entrypoint: 63 | ["bash", "-c", "npx ts-node /app/tests/plugins/pg/server.ts"] 64 | depends_on: 65 | collector: 66 | condition: service_healthy 67 | postgres: 68 | condition: service_healthy 69 | 70 | client: 71 | extends: 72 | file: ../common/base-compose.yml 73 | service: agent 74 | ports: 75 | - 5001:5001 76 | environment: 77 | SERVER: server:5000 78 | healthcheck: 79 | test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001"] 80 | interval: 5s 81 | timeout: 60s 82 | retries: 120 83 | entrypoint: 84 | ["bash", "-c", "npx ts-node /app/tests/plugins/pg/client.ts"] 85 | depends_on: 86 | server: 87 | condition: service_healthy 88 | 89 | networks: 90 | traveling-light: 91 | -------------------------------------------------------------------------------- /tests/plugins/pg/expected.data.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | segmentItems: 19 | - serviceName: server 20 | segmentSize: 1 21 | segments: 22 | - segmentId: not null 23 | spans: 24 | - operationName: pg/query 25 | operationId: 0 26 | parentSpanId: 0 27 | spanId: 1 28 | spanLayer: Database 29 | startTime: gt 0 30 | endTime: gt 0 31 | componentId: 22 32 | spanType: Exit 33 | peer: postgres:5432 34 | skipAnalysis: false 35 | tags: 36 | - { key: db.type, value: PostgreSQL } 37 | - { key: db.instance, value: test } 38 | - { key: db.statement, value: SELECT * FROM "user" where name = 'u1' } 39 | - operationName: GET:/postgres 40 | operationId: 0 41 | parentSpanId: -1 42 | spanId: 0 43 | spanLayer: Http 44 | startTime: gt 0 45 | endTime: gt 0 46 | componentId: 49 47 | spanType: Entry 48 | peer: not null 49 | skipAnalysis: false 50 | tags: 51 | - { key: coldStart, value: 'true' } 52 | - { key: http.url, value: 'http://server:5000/postgres' } 53 | - { key: http.method, value: GET } 54 | - { key: http.status_code, value: '200' } 55 | - { key: http.status.msg, value: OK } 56 | refs: 57 | - parentEndpoint: "" 58 | networkAddress: server:5000 59 | refType: CrossProcess 60 | parentSpanId: 1 61 | parentTraceSegmentId: not null 62 | parentServiceInstance: not null 63 | parentService: client 64 | traceId: not null 65 | - serviceName: client 66 | segmentSize: 1 67 | segments: 68 | - segmentId: not null 69 | spans: 70 | - operationName: GET:/postgres 71 | operationId: 0 72 | parentSpanId: -1 73 | spanId: 0 74 | spanLayer: Http 75 | startTime: gt 0 76 | endTime: gt 0 77 | componentId: 49 78 | spanType: Entry 79 | peer: not null 80 | skipAnalysis: false 81 | tags: 82 | - { key: coldStart, value: 'true' } 83 | - { key: http.url, value: 'http://localhost:5001/postgres' } 84 | - { key: http.method, value: GET } 85 | - { key: http.status_code, value: '200' } 86 | - { key: http.status.msg, value: OK } 87 | - operationName: /postgres 88 | operationId: 0 89 | parentSpanId: 0 90 | spanId: 1 91 | spanLayer: Http 92 | startTime: gt 0 93 | endTime: gt 0 94 | componentId: 2 95 | spanType: Exit 96 | peer: server:5000 97 | skipAnalysis: false 98 | tags: 99 | - { key: http.url, value: 'http://server:5000/postgres' } 100 | - { key: http.method, value: GET } 101 | - { key: http.status_code, value: '200' } 102 | - { key: http.status.msg, value: OK } 103 | -------------------------------------------------------------------------------- /tests/plugins/pg/init/init.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one or more 2 | -- contributor license agreements. See the NOTICE file distributed with 3 | -- this work for additional information regarding copyright ownership. 4 | -- The ASF licenses this file to You under the Apache License, Version 2.0 5 | -- (the "License"); you may not use this file except in compliance with 6 | -- the License. You may obtain a copy of the License at 7 | -- 8 | -- http://www.apache.org/licenses/LICENSE-2.0 9 | -- 10 | -- Unless required by applicable law or agreed to in writing, software 11 | -- distributed under the License is distributed on an "AS IS" BASIS, 12 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | -- See the License for the specific language governing permissions and 14 | -- limitations under the License. 15 | 16 | CREATE SEQUENCE user_seq; 17 | 18 | CREATE TABLE IF NOT EXISTS "user"( 19 | id INT CHECK (id > 0) DEFAULT NEXTVAL ('user_seq'), 20 | name VARCHAR(100) NOT NULL, 21 | PRIMARY KEY( id ) 22 | ); 23 | -------------------------------------------------------------------------------- /tests/plugins/pg/server.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as http from 'http'; 21 | import {Client} from 'pg'; 22 | import agent from '../../../src'; 23 | 24 | agent.start({ 25 | serviceName: 'server', 26 | maxBufferSize: 1000, 27 | }) 28 | 29 | const server = http.createServer((req, res) => { 30 | const client = new Client({ 31 | host: process.env.POSTGRES_HOST || 'postgres', 32 | user: 'root', 33 | password: 'root', 34 | database: 'test' 35 | }); 36 | client.connect(); 37 | client.query(`SELECT * FROM "user" where name = 'u1'`).then( 38 | (resDB: any) => { 39 | res.end(JSON.stringify(resDB.rows)); 40 | client.end(); 41 | }, 42 | (err: any) => { 43 | client.end(); 44 | }, 45 | ); 46 | }) 47 | 48 | server.listen(5000, () => console.info('Listening on port 5000...')); 49 | -------------------------------------------------------------------------------- /tests/plugins/pg/test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one or more 4 | * contributor license agreements. See the NOTICE file distributed with 5 | * this work for additional information regarding copyright ownership. 6 | * The ASF licenses this file to You under the Apache License, Version 2.0 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import * as path from 'path'; 21 | import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; 22 | import axios from 'axios'; 23 | import waitForExpect from 'wait-for-expect'; 24 | import { promises as fs } from 'fs'; 25 | 26 | const rootDir = path.resolve(__dirname); 27 | 28 | describe('plugin tests', () => { 29 | let compose: StartedDockerComposeEnvironment; 30 | 31 | beforeAll(async () => { 32 | compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') 33 | .withWaitStrategy('client', Wait.forHealthCheck()) 34 | .withWaitStrategy('postgres', Wait.forHealthCheck()) 35 | .up(); 36 | }); 37 | 38 | afterAll(async () => { 39 | await compose.down(); 40 | }); 41 | 42 | it(__filename, async () => { 43 | await waitForExpect(async () => expect((await axios.get('http://localhost:5001/postgres')).status).toBe(200)); 44 | 45 | const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); 46 | 47 | try { 48 | await waitForExpect(async () => 49 | expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), 50 | ); 51 | } catch (e) { 52 | const actualData = (await axios.get('http://localhost:12800/receiveData')).data; 53 | console.info({ actualData }); 54 | throw e; 55 | } 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "strict": true, 6 | "importHelpers": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "rootDir": "..", 12 | "baseUrl": "..", 13 | "resolveJsonModule": true, 14 | "declaration": true, 15 | "allowJs": true, 16 | "sourceMap": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "no-console": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "rootDir": ".", 5 | "baseUrl": ".", 6 | "outDir": ".", 7 | "resolveJsonModule": true, 8 | "composite": true, 9 | "esModuleInterop": true 10 | }, 11 | "files": [ 12 | "package.json" 13 | ], 14 | "include": [ 15 | "src/**/*", 16 | "tests/**/*" 17 | ] 18 | } 19 | --------------------------------------------------------------------------------