diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9e8ec25 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +quote_type = single diff --git a/README.md b/README.md index dfb635f..d2eab71 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,12 @@ microservices, cloud native and container-based (Docker, Kubernetes, Mesos) arch ## Set up NodeJS Agent -SkyWalking NodeJS SDK requires SkyWalking 8.0+. +SkyWalking NodeJS SDK requires SkyWalking backend (OAP) 8.0+ and NodeJS >= 10. ```typescript -import Agent from 'skywalking'; +import agent from 'skywalking'; -Agent.start({ +agent.start({ serviceName: '', serviceInstance: '', collectorAddress: '', @@ -38,15 +38,19 @@ Environment Variable | Description | Default | `SW_AGENT_COLLECTOR_BACKEND_SERVICES` | The backend OAP server address | `127.0.0.1:11800` | | `SW_AGENT_AUTHENTICATION` | The authentication token to verify that the agent is trusted by the backend OAP, as for how to configure the backend, refer to [the yaml](https://github.com/apache/skywalking/blob/4f0f39ffccdc9b41049903cc540b8904f7c9728e/oap-server/server-bootstrap/src/main/resources/application.yml#L155-L158). | not set | | `SW_AGENT_LOGGING_LEVEL` | The logging level, could be one of `CRITICAL`, `FATAL`, `ERROR`, `WARN`(`WARNING`), `INFO`, `DEBUG` | `INFO` | +| `SW_IGNORE_SUFFIX` | The suffices of endpoints that will be ignored (not traced), comma separated | `.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg` | +| `SW_TRACE_IGNORE_PATH` | The paths of endpoints that will be ignored (not traced), comma separated | `` | | `SW_AGENT_MAX_BUFFER_SIZE` | The maximum buffer size before sending the segment data to backend | `'1000'` | ## Supported Libraries -There're some built-in plugins that support automatic instrumentation of NodeJS libraries, the complete lists are as follows: +There are some built-in plugins that support automatic instrumentation of NodeJS libraries, the complete lists are as follows: Library | Plugin Name | :--- | :--- | -| built-in `http` module | `sw_http` | +| built-in `http` and `https` module | `http` | +| [`express`](https://expressjs.com) | `express` | +| [`axios`](https://github.com/axios/axios) | `axios` | ## License Apache 2.0 diff --git a/dist/LICENSE b/dist/LICENSE new file mode 100755 index 0000000..c103232 --- /dev/null +++ b/dist/LICENSE @@ -0,0 +1,256 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +======================================================================= +Apache SkyWalking NodeJS Agent Subcomponents: + +The Apache SkyWalking NodeJS Agent project contains subcomponents with separate copyright +notices and license terms. Your use of the source code for the these +subcomponents is subject to the terms and conditions of the following +licenses. + +======================================================================== +Apache 2.0 licenses +======================================================================== + +The following components are provided under the Apache License. See project link for details. +The text of each license is the standard Apache 2.0 license. + + grpc-node 1.10.1: https://github.com/grpc/grpc-node, Apache 2.0 + +======================================================================== +BSD licenses +======================================================================== + +The following components are provided under a BSD license. See project link for details. +The text of each license is also included at licenses/LICENSE-[project].txt. + + protobuf-js 3.14.0: https://github.com/protocolbuffers/protobuf/tree/master/js, BSD-3-Clause + +======================================================================== +0BSD licenses +======================================================================== + +The following components are provided under a BSD license. See project link for details. +The text of each license is also included at licenses/LICENSE-[project].txt. + + tslib 2.0.3: https://www.typescriptlang.org/, 0BSD + +======================================================================== +MIT licenses +======================================================================== + +The following components are provided under the MIT License. See project link for details. +The text of each license is also included at licenses/LICENSE-[project].txt. + + on-finished 2.3.0: https://github.com/jshttp/on-finished, MIT + uuid 8.1.0: https://github.com/uuidjs/uuid, MIT + winston 3.2.1: https://github.com/winstonjs/winston, MIT + +======================================================================== +ISC licenses +======================================================================== + +The following components are provided under the ISC License. See project link for details. +The text of each license is also included at licenses/LICENSE-[project].txt. + + semver 7.3.2: https://github.com/npm/node-semver, ISC diff --git a/dist/NOTICE b/dist/NOTICE new file mode 100755 index 0000000..e646f51 --- /dev/null +++ b/dist/NOTICE @@ -0,0 +1,5 @@ +Apache SkyWalking +Copyright 2017-2020 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/dist/licenses/LICENSE-on-finished.txt b/dist/licenses/LICENSE-on-finished.txt new file mode 100644 index 0000000..5931fd2 --- /dev/null +++ b/dist/licenses/LICENSE-on-finished.txt @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2013 Jonathan Ong +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/dist/licenses/LICENSE-protobuf-js.txt b/dist/licenses/LICENSE-protobuf-js.txt new file mode 100644 index 0000000..19b305b --- /dev/null +++ b/dist/licenses/LICENSE-protobuf-js.txt @@ -0,0 +1,32 @@ +Copyright 2008 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Code generated by the Protocol Buffer compiler is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. diff --git a/dist/licenses/LICENSE-semver.txt b/dist/licenses/LICENSE-semver.txt new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/dist/licenses/LICENSE-semver.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/dist/licenses/LICENSE-tslib.txt b/dist/licenses/LICENSE-tslib.txt new file mode 100644 index 0000000..b72046c --- /dev/null +++ b/dist/licenses/LICENSE-tslib.txt @@ -0,0 +1,12 @@ +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/dist/licenses/LICENSE-uuid.txt b/dist/licenses/LICENSE-uuid.txt new file mode 100644 index 0000000..f8969d3 --- /dev/null +++ b/dist/licenses/LICENSE-uuid.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2010-2020 Robert Kieffer and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dist/licenses/LICENSE-winston.txt b/dist/licenses/LICENSE-winston.txt new file mode 100644 index 0000000..15fbbb5 --- /dev/null +++ b/dist/licenses/LICENSE-winston.txt @@ -0,0 +1,19 @@ +Copyright (c) 2010 Charlie Robbins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/package-lock.json b/package-lock.json index dbc294d..451b25a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1045,6 +1045,16 @@ "@babel/types": "^7.3.0" } }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npm.taobao.org/@types/body-parser/download/@types/body-parser-1.19.0.tgz?cache=0&sync_timestamp=1605052521338&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fbody-parser%2Fdownload%2F%40types%2Fbody-parser-1.19.0.tgz", + "integrity": "sha1-BoWzxH6zAG/+0RfN1VFkth+AU48=", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, "@types/bytebuffer": { "version": "5.0.41", "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.41.tgz", @@ -1060,6 +1070,15 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npm.taobao.org/@types/connect/download/@types/connect-3.4.34.tgz?cache=0&sync_timestamp=1607458722751&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fconnect%2Fdownload%2F%40types%2Fconnect-3.4.34.tgz", + "integrity": "sha1-FwpAIjptZmAG2TyhKK8r6x2bGQE=", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/dockerode": { "version": "2.5.34", "resolved": "https://registry.npm.taobao.org/@types/dockerode/download/@types/dockerode-2.5.34.tgz", @@ -1069,6 +1088,29 @@ "@types/node": "*" } }, + "@types/express": { + "version": "4.17.9", + "resolved": "https://registry.npm.taobao.org/@types/express/download/@types/express-4.17.9.tgz?cache=0&sync_timestamp=1605057477768&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fexpress%2Fdownload%2F%40types%2Fexpress-4.17.9.tgz", + "integrity": "sha1-9fLfat1wP/KEKK3VK97IoQkbCng=", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.17", + "resolved": "https://registry.npm.taobao.org/@types/express-serve-static-core/download/@types/express-serve-static-core-4.17.17.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fexpress-serve-static-core%2Fdownload%2F%40types%2Fexpress-serve-static-core-4.17.17.tgz", + "integrity": "sha1-a6AkZRZbbJw9jbOije9rFvybcPU=", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "@types/google-protobuf": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.7.2.tgz", @@ -1129,6 +1171,12 @@ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, + "@types/mime": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/@types/mime/download/@types/mime-2.0.3.tgz", + "integrity": "sha1-yJO3NyHbc2mZQ7/DZTsd63+qSjo=", + "dev": true + }, "@types/node": { "version": "14.0.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz", @@ -1140,12 +1188,33 @@ "integrity": "sha1-5IbQ2XOW15vu3QpuM/RTT/a0lz4=", "dev": true }, + "@types/on-finished": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/on-finished/-/on-finished-2.3.1.tgz", + "integrity": "sha512-mzVYaYcFs5Jd2n/O6uYIRUsFRR1cHyZLRvkLCU0E7+G5WhY0qBDAR5fUCeZbvecYOSh9ikhlesyi2UfI8B9ckQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/prettier": { "version": "2.1.5", "resolved": "https://registry.npm.taobao.org/@types/prettier/download/@types/prettier-2.1.5.tgz", "integrity": "sha1-tqs7uinha4IdhOCez63tRiuBawA=", "dev": true }, + "@types/qs": { + "version": "6.9.5", + "resolved": "https://registry.npm.taobao.org/@types/qs/download/@types/qs-6.9.5.tgz?cache=0&sync_timestamp=1605055106687&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fqs%2Fdownload%2F%40types%2Fqs-6.9.5.tgz", + "integrity": "sha1-Q0cRvdSete5p2QwdZ8NUqajssYs=", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npm.taobao.org/@types/range-parser/download/@types/range-parser-1.2.3.tgz?cache=0&sync_timestamp=1605055243791&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Frange-parser%2Fdownload%2F%40types%2Frange-parser-1.2.3.tgz", + "integrity": "sha1-fuMwunyq+5gJC+zoal7kQRWQTCw=", + "dev": true + }, "@types/semver": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.2.0.tgz", @@ -1155,6 +1224,16 @@ "@types/node": "*" } }, + "@types/serve-static": { + "version": "1.13.8", + "resolved": "https://registry.npm.taobao.org/@types/serve-static/download/@types/serve-static-1.13.8.tgz?cache=0&sync_timestamp=1605657655340&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fserve-static%2Fdownload%2F%40types%2Fserve-static-1.13.8.tgz", + "integrity": "sha1-hREp1DRDPHCCFIV0/+wmPVgwnEY=", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, "@types/ssh2": { "version": "0.5.45", "resolved": "https://registry.npm.taobao.org/@types/ssh2/download/@types/ssh2-0.5.45.tgz?cache=0&sync_timestamp=1605057226497&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fssh2%2Fdownload%2F%40types%2Fssh2-0.5.45.tgz", @@ -1212,6 +1291,16 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz", + "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-7.4.1.tgz", @@ -1604,6 +1693,12 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, "array-sort": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-0.1.4.tgz", @@ -1842,6 +1937,47 @@ "tweetnacl": "^0.14.3" } }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npm.taobao.org/body-parser/download/body-parser-1.19.0.tgz", + "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566551397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433856030&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz", + "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=", + "dev": true + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1929,6 +2065,12 @@ "long": "~3" } }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/bytes/download/bytes-3.1.0.tgz", + "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=", + "dev": true + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -2203,6 +2345,21 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.3.tgz", + "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "dev": true + }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npm.taobao.org/convert-source-map/download/convert-source-map-1.7.0.tgz", @@ -2212,6 +2369,18 @@ "safe-buffer": "~5.1.1" } }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npm.taobao.org/cookie/download/cookie-0.4.0.tgz", + "integrity": "sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", @@ -2455,6 +2624,18 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -2579,6 +2760,11 @@ "safer-buffer": "^2.1.0" } }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, "emittery": { "version": "0.7.2", "resolved": "https://registry.npm.taobao.org/emittery/download/emittery-0.7.2.tgz", @@ -2599,6 +2785,12 @@ "env-variable": "0.0.x" } }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npm.taobao.org/end-of-stream/download/end-of-stream-1.4.4.tgz", @@ -2642,6 +2834,12 @@ "integrity": "sha1-Ck2uN9YA0VopukU9jvkg8YRDM/Y=", "dev": true }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -2679,6 +2877,12 @@ "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", "dev": true }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, "exec-sh": { "version": "0.3.4", "resolved": "https://registry.npm.taobao.org/exec-sh/download/exec-sh-0.3.4.tgz", @@ -2761,6 +2965,67 @@ "jest-regex-util": "^26.0.0" } }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npm.taobao.org/express/download/express-4.17.1.tgz", + "integrity": "sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566551397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433856030&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz", + "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=", + "dev": true + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz", @@ -2884,6 +3149,38 @@ } } }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.2.tgz", + "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566551397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433856030&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -2932,6 +3229,12 @@ "mime-types": "^2.1.12" } }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -2941,6 +3244,12 @@ "map-cache": "^0.2.2" } }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npm.taobao.org/fs-constants/download/fs-constants-1.0.0.tgz", @@ -3098,9 +3407,9 @@ "dev": true }, "google-protobuf": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.12.2.tgz", - "integrity": "sha512-4CZhpuRr1d6HjlyrxoXoocoGFnRYgKULgMtikMddA9ztRyYR59Aondv2FioyxWVamRo0rF2XpYawkTCBEQOSkA==" + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.14.0.tgz", + "integrity": "sha512-bwa8dBuMpOxg7COyqkW6muQuvNnWgVN8TX/epDRGW5m0jcrmq2QJyCyiV8ZE2/6LaIIqJtiv9bYokFhfpy/o6w==" }, "graceful-fs": { "version": "4.2.4", @@ -3489,6 +3798,27 @@ "kind-of": "^6.0.0" } }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.2.tgz?cache=0&sync_timestamp=1593407611415&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-errors%2Fdownload%2Fhttp-errors-1.7.2.tgz", + "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npm.taobao.org/http-signature/download/http-signature-1.2.0.tgz?cache=0&sync_timestamp=1600868532049&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-signature%2Fdownload%2Fhttp-signature-1.2.0.tgz", @@ -3580,6 +3910,12 @@ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", "dev": true }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npm.taobao.org/ipaddr.js/download/ipaddr.js-1.9.1.tgz", + "integrity": "sha1-v/OFQ+64mEglB5/zoqjmy9RngbM=", + "dev": true + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -5489,12 +5825,30 @@ "object-visit": "^1.0.0" } }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npm.taobao.org/merge-stream/download/merge-stream-2.0.0.tgz", "integrity": "sha1-UoI2KaFN0AyXcPtq1H3GMQ8sH2A=", "dev": true }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -5576,6 +5930,12 @@ } } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", + "dev": true + }, "mime-db": { "version": "1.44.0", "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.44.0.tgz?cache=0&sync_timestamp=1600831159918&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-db%2Fdownload%2Fmime-db-1.44.0.tgz", @@ -5773,6 +6133,12 @@ "sax": "^1.2.4" } }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npm.taobao.org/negotiator/download/negotiator-0.6.2.tgz", + "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=", + "dev": true + }, "neo-async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", @@ -5994,6 +6360,14 @@ "isobject": "^3.0.1" } }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6116,6 +6490,12 @@ "integrity": "sha1-9o5OW6GFKsLK3AD0VV//bCq7YXg=", "dev": true }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz", + "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=", + "dev": true + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -6145,6 +6525,12 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpath-to-regexp%2Fdownload%2Fpath-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz", @@ -6239,6 +6625,16 @@ "yargs": "^3.10.0" } }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npm.taobao.org/proxy-addr/download/proxy-addr-2.0.6.tgz", + "integrity": "sha1-/cIzZQVEfT8vLGOO0nLK9hS7sr8=", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npm.taobao.org/psl/download/psl-1.8.0.tgz", @@ -6267,6 +6663,24 @@ "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=", "dev": true }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npm.taobao.org/range-parser/download/range-parser-1.2.1.tgz", + "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npm.taobao.org/raw-body/download/raw-body-2.4.0.tgz", + "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -6640,6 +7054,64 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npm.taobao.org/send/download/send-0.17.1.tgz", + "integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566551397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433856030&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz?cache=0&sync_timestamp=1607433856030&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.1.tgz", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npm.taobao.org/serve-static/download/serve-static-1.14.1.tgz", + "integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -6666,6 +7138,12 @@ "split-string": "^3.0.1" } }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.1.tgz", + "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=", + "dev": true + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz", @@ -6991,6 +7469,12 @@ } } }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, "stealthy-require": { "version": "1.1.1", "resolved": "https://registry.npm.taobao.org/stealthy-require/download/stealthy-require-1.1.1.tgz", @@ -7423,6 +7907,12 @@ } } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/toidentifier/download/toidentifier-1.0.0.tgz", + "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=", + "dev": true + }, "tough-cookie": { "version": "3.0.1", "resolved": "https://registry.npm.taobao.org/tough-cookie/download/tough-cookie-3.0.1.tgz", @@ -7614,6 +8104,16 @@ "integrity": "sha1-CeJJ696FHTseSNJ8EFREZn8XuD0=", "dev": true }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npm.taobao.org/type-is/download/type-is-1.6.18.tgz", + "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npm.taobao.org/typedarray-to-buffer/download/typedarray-to-buffer-3.1.5.tgz", @@ -7671,6 +8171,12 @@ "set-value": "^2.0.1" } }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -7737,6 +8243,12 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, "uuid": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz", @@ -7771,6 +8283,12 @@ "spdx-expression-parse": "^3.0.0" } }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npm.taobao.org/verror/download/verror-1.10.0.tgz", diff --git a/package.json b/package.json index 458bbef..c81aaa2 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "skywalking", "version": "0.1.0", "description": "The NodeJS agent for Apache SkyWalking", + "homepage": "skywalking.apache.org", "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { @@ -35,12 +36,15 @@ "email": "dev@skywalking.apache.org" }, "devDependencies": { + "@types/express": "^4.17.9", "@types/google-protobuf": "^3.7.2", "@types/jest": "^26.0.15", "@types/node": "^14.0.11", + "@types/on-finished": "^2.3.1", "@types/semver": "^7.2.0", "@types/uuid": "^8.0.0", "axios": "^0.21.0", + "express": "^4.17.1", "grpc-tools": "^1.10.0", "grpc_tools_node_protoc_ts": "^4.0.0", "jest": "^26.6.3", @@ -55,8 +59,9 @@ "wait-for-expect": "^3.0.2" }, "dependencies": { - "google-protobuf": "^3.12.2", + "google-protobuf": "^3.14.0", "grpc": "^1.10.1", + "on-finished": "^2.3.0", "semver": "^7.3.2", "tslib": "^2.0.3", "uuid": "^8.1.0", diff --git a/src/agent/Buffer.ts b/src/agent/Buffer.ts index 8f4f061..99a18fb 100644 --- a/src/agent/Buffer.ts +++ b/src/agent/Buffer.ts @@ -18,18 +18,16 @@ */ import { createLogger } from '../logging'; -import Segment from '../trace/context/Segment'; import config from '../config/AgentConfig'; -import TraceReportClient from '../agent/protocol/grpc/clients/TraceReportClient'; const logger = createLogger(__filename); -class Buffer { - maxSize: number; - buffer: Segment[]; +export default class Buffer { + private readonly maxSize: number; + private readonly buffer: T[]; - constructor(maxSize: number = 1000) { - this.maxSize = maxSize; + constructor() { + this.maxSize = config.maxBufferSize; this.buffer = []; } @@ -37,16 +35,17 @@ class Buffer { return this.buffer.length; } - put(segment: Segment): this { - if (this.buffer.length > this.maxSize) { - logger.warn('Drop the data because of the buffer is oversized'); - return this; + put(element: T): boolean { + if (this.length > this.maxSize) { + logger.warn('Drop the data because of the buffer is oversize'); + return false; } - this.buffer.push(segment); - TraceReportClient.ref(); // this is currently hard-coded for grpc, if other protocols added need to change + this.buffer.push(element); - return this; + return true; } -} -export default new Buffer(config.maxBufferSize); + take(): T { + return this.buffer.splice(0, 1)[0]; + } +} diff --git a/src/agent/protocol/Protocol.ts b/src/agent/protocol/Protocol.ts index 7f87c6d..778437e 100644 --- a/src/agent/protocol/Protocol.ts +++ b/src/agent/protocol/Protocol.ts @@ -23,7 +23,7 @@ export default interface Protocol { isConnected: boolean; - heartbeat(): void; + heartbeat(): this; - report(): void; + report(): this; } diff --git a/src/agent/protocol/grpc/GrpcProtocol.ts b/src/agent/protocol/grpc/GrpcProtocol.ts index 4875459..df0bc1f 100644 --- a/src/agent/protocol/grpc/GrpcProtocol.ts +++ b/src/agent/protocol/grpc/GrpcProtocol.ts @@ -22,15 +22,25 @@ import HeartbeatClient from '../../../agent/protocol/grpc/clients/HeartbeatClien import TraceReportClient from '../../../agent/protocol/grpc/clients/TraceReportClient'; export default class GrpcProtocol implements Protocol { + private readonly heartbeatClient: HeartbeatClient; + private readonly traceReportClient: TraceReportClient; + + constructor() { + this.heartbeatClient = new HeartbeatClient(); + this.traceReportClient = new TraceReportClient(); + } + get isConnected(): boolean { - return HeartbeatClient.isConnected && TraceReportClient.isConnected; + return this.heartbeatClient.isConnected && this.traceReportClient.isConnected; } - heartbeat() { - HeartbeatClient.start(); + heartbeat(): this { + this.heartbeatClient.start(); + return this; } - report() { - TraceReportClient.start(); + report(): this { + this.traceReportClient.start(); + return this; } } diff --git a/src/agent/protocol/grpc/clients/HeartbeatClient.ts b/src/agent/protocol/grpc/clients/HeartbeatClient.ts index bd36b7a..660d620 100755 --- a/src/agent/protocol/grpc/clients/HeartbeatClient.ts +++ b/src/agent/protocol/grpc/clients/HeartbeatClient.ts @@ -32,19 +32,21 @@ import * as os from 'os'; const logger = createLogger(__filename); -class HeartbeatClient implements Client { - heartbeatClient?: ManagementServiceClient; - heartbeatTimer?: NodeJS.Timeout; +export default class HeartbeatClient implements Client { + private readonly managementServiceClient: ManagementServiceClient; + private heartbeatTimer?: NodeJS.Timeout; + + constructor() { + this.managementServiceClient = new ManagementServiceClient(config.collectorAddress, grpc.credentials.createInsecure(), { + interceptors: [AuthInterceptor], + }); + } get isConnected(): boolean { - return this.heartbeatClient?.getChannel().getConnectivityState(true) === connectivityState.READY; + return this.managementServiceClient.getChannel().getConnectivityState(true) === connectivityState.READY; } start() { - this.heartbeatClient = new ManagementServiceClient(config.collectorAddress, grpc.credentials.createInsecure(), { - interceptors: [AuthInterceptor], - }); - if (this.heartbeatTimer) { logger.warn(` The heartbeat timer has already been scheduled, @@ -69,7 +71,7 @@ class HeartbeatClient implements Client { ]); this.heartbeatTimer = setInterval(() => { - this.heartbeatClient?.reportInstanceProperties( + this.managementServiceClient.reportInstanceProperties( instanceProperties, (error, _) => { @@ -78,7 +80,7 @@ class HeartbeatClient implements Client { } }, ); - this.heartbeatClient?.keepAlive( + this.managementServiceClient.keepAlive( keepAlivePkg, (error, _) => { @@ -90,5 +92,3 @@ class HeartbeatClient implements Client { }, 20000).unref(); } } - -export default new HeartbeatClient(); diff --git a/src/agent/protocol/grpc/clients/TraceReportClient.ts b/src/agent/protocol/grpc/clients/TraceReportClient.ts index 9a18032..fcc7fd8 100755 --- a/src/agent/protocol/grpc/clients/TraceReportClient.ts +++ b/src/agent/protocol/grpc/clients/TraceReportClient.ts @@ -24,32 +24,40 @@ import { createLogger } from '../../../../logging'; import Client from './Client'; import { TraceSegmentReportServiceClient } from '../../../../proto/language-agent/Tracing_grpc_pb'; import AuthInterceptor from '../AuthInterceptor'; -import buffer from '../../../../agent/Buffer'; +import Buffer from '../../../../agent/Buffer'; import SegmentObjectAdapter from '../SegmentObjectAdapter'; +import { emitter } from '../../../../lib/EventEmitter'; +import Segment from '../../../../trace/context/Segment'; const logger = createLogger(__filename); -class TraceReportClient implements Client { - reporterClient?: TraceSegmentReportServiceClient; - timeout: any; +export default class TraceReportClient implements Client { + private readonly reporterClient: TraceSegmentReportServiceClient; + private readonly buffer: Buffer; + private timeout?: NodeJS.Timeout; - get isConnected(): boolean { - return this.reporterClient?.getChannel().getConnectivityState(true) === connectivityState.READY; - } - - ref() { - this.timeout.ref(); - } - - start() { + constructor() { + this.buffer = new Buffer(); this.reporterClient = new TraceSegmentReportServiceClient( config.collectorAddress, grpc.credentials.createInsecure(), { interceptors: [AuthInterceptor] }, ); + emitter.on('segment-finished', (segment) => { + if (this.buffer.put(segment)) { + this.timeout?.ref(); + } + }); + } + + get isConnected(): boolean { + return this.reporterClient?.getChannel().getConnectivityState(true) === connectivityState.READY; + } + + start() { const reportFunction = () => { try { - if (buffer.length === 0 || !this.reporterClient) { + if (this.buffer.length === 0) { return; } @@ -59,8 +67,8 @@ class TraceReportClient implements Client { } }); - while (buffer.length > 0) { - const segment = buffer.buffer.splice(0, 1)[0]; + while (this.buffer.length > 0) { + const segment = this.buffer.take(); if (segment) { if (logger.isDebugEnabled()) { logger.debug('Sending segment ', { segment }); @@ -79,5 +87,3 @@ class TraceReportClient implements Client { this.timeout = setTimeout(reportFunction, 1000).unref(); } } - -export default new TraceReportClient(); diff --git a/src/core/PluginInstaller.ts b/src/core/PluginInstaller.ts index 80ce569..e959501 100644 --- a/src/core/PluginInstaller.ts +++ b/src/core/PluginInstaller.ts @@ -26,12 +26,14 @@ import * as semver from 'semver'; const logger = createLogger(__filename); let topModule = module; -for (; topModule.parent; topModule = topModule.parent); +while (topModule.parent) { + topModule = topModule.parent; +} -class PluginInstaller { - pluginDir: string; - require: (name: string) => any = topModule.require.bind(topModule); - resolve = (request: string) => (module.constructor as any)._resolveFilename(request, topModule); +export default class PluginInstaller { + private readonly pluginDir: string; + private readonly require: (name: string) => any = topModule.require.bind(topModule); + private readonly resolve = (request: string) => (module.constructor as any)._resolveFilename(request, topModule); constructor() { this.pluginDir = path.resolve(__dirname, '..', 'plugins'); @@ -72,33 +74,30 @@ class PluginInstaller { install(): void { fs.readdirSync(this.pluginDir) - .filter((file) => !(file.endsWith('.d.ts') || file.endsWith('.js.map'))) - .forEach((file) => { - let plugin; - const pluginFile = path.join(this.pluginDir, file); - - try { - plugin = require(pluginFile).default as SwPlugin; - const { isSupported, version } = this.checkModuleVersion(plugin); - - if (!isSupported) { - logger.info(`Plugin ${plugin.module} ${version} doesn't satisfy the supported version ${plugin.versions}`); - return; - } - - logger.info(`Installing plugin ${plugin.module} ${plugin.versions}`); + .filter((file) => !(file.endsWith('.d.ts') || file.endsWith('.js.map'))) + .forEach((file) => { + let plugin; + const pluginFile = path.join(this.pluginDir, file); + + try { + plugin = require(pluginFile).default as SwPlugin; + const { isSupported, version } = this.checkModuleVersion(plugin); + + if (!isSupported) { + logger.info(`Plugin ${plugin.module} ${version} doesn't satisfy the supported version ${plugin.versions}`); + return; + } - plugin.install(); + logger.info(`Installing plugin ${plugin.module} ${plugin.versions}`); - } catch (e) { - if (plugin) { - logger.error(`Error installing plugin ${plugin.module} ${plugin.versions}`); - } else { - logger.error(`Error processing plugin ${pluginFile}`); - } + plugin.install(); + } catch (e) { + if (plugin) { + logger.error(`Error installing plugin ${plugin.module} ${plugin.versions}`); + } else { + logger.error(`Error processing plugin ${pluginFile}`); } - }); + } + }); } } - -export default new PluginInstaller(); diff --git a/src/index.ts b/src/index.ts index 698e22e..1dfae71 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,14 +20,12 @@ import config, { AgentConfig, finalizeConfig } from './config/AgentConfig'; import GrpcProtocol from './agent/protocol/grpc/GrpcProtocol'; import { createLogger } from './logging'; -import Protocol from './agent/protocol/Protocol'; import PluginInstaller from './core/PluginInstaller'; const logger = createLogger(__filename); class Agent { - started = false; - protocol: Protocol = new GrpcProtocol(); + private started = false; start(options: AgentConfig = {}): void { if (process.env.SW_DISABLE === 'true') { @@ -45,10 +43,9 @@ class Agent { this.started = true; - PluginInstaller.install(); + new PluginInstaller().install(); - this.protocol.heartbeat(); - this.protocol.report(); + new GrpcProtocol().heartbeat().report(); } } diff --git a/src/lib/EventEmitter.ts b/src/lib/EventEmitter.ts new file mode 100644 index 0000000..9d4f708 --- /dev/null +++ b/src/lib/EventEmitter.ts @@ -0,0 +1,32 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* tslint:disable:unified-signatures */ + +import { EventEmitter } from 'events'; +import Segment from '../trace/context/Segment'; + +declare interface SkyWalkingEventEmitter { + on(event: 'segment-finished', listener: (segment: Segment) => void): this; +} + +class SkyWalkingEventEmitter extends EventEmitter { +} + +export const emitter: SkyWalkingEventEmitter = new SkyWalkingEventEmitter(); diff --git a/src/plugins/AxiosPlugin.ts b/src/plugins/AxiosPlugin.ts index 37e330c..1ce7191 100644 --- a/src/plugins/AxiosPlugin.ts +++ b/src/plugins/AxiosPlugin.ts @@ -25,14 +25,13 @@ import Span from '../trace/span/Span'; import Tag from '../Tag'; import { SpanLayer } from '../proto/language-agent/Tracing_pb'; import { createLogger } from '../logging'; -import PluginInstaller from '../core/PluginInstaller'; const logger = createLogger(__filename); class AxiosPlugin implements SwPlugin { readonly module = 'axios'; readonly versions = '*'; - axios = PluginInstaller.require('axios').default; + axios = require('axios').default; install(): void { if (logger.isDebugEnabled()) { @@ -53,7 +52,7 @@ class AxiosPlugin implements SwPlugin { } span.stop(); - } + }; this.axios.interceptors.request.use( (config: any) => { @@ -71,7 +70,7 @@ class AxiosPlugin implements SwPlugin { error.config.span.stop(); return Promise.reject(error); - } + }, ); this.axios.interceptors.response.use( @@ -87,26 +86,23 @@ class AxiosPlugin implements SwPlugin { copyStatusAndStop(error.config.span, error.response); return Promise.reject(error); - } + }, ); const _request = this.axios.Axios.prototype.request; - this.axios.Axios.prototype.request = function (config: any) { + this.axios.Axios.prototype.request = function(config: any) { const { host, pathname: operation } = new URL(config.url); // TODO: this may throw invalid URL const span = ContextManager.current.newExitSpan(operation, host).start(); try { - span.component = Component.HTTP; // TODO: add Component.AXIOS (to main Skywalking project) + span.component = Component.AXIOS; // TODO: add Component.AXIOS (to main Skywalking project) span.layer = SpanLayer.HTTP; span.peer = host; span.tag(Tag.httpURL(host + operation)); span.async(); - const request = _request.call(this, {...config, span}); - - return request; - + return _request.call(this, { ...config, span }); } catch (e) { span.error(e); span.stop(); diff --git a/src/plugins/ExpressPlugin.ts b/src/plugins/ExpressPlugin.ts index 1cd3172..2ca1e8a 100644 --- a/src/plugins/ExpressPlugin.ts +++ b/src/plugins/ExpressPlugin.ts @@ -24,25 +24,18 @@ import { Component } from '../trace/Component'; import Tag from '../Tag'; import { SpanLayer } from '../proto/language-agent/Tracing_pb'; import { ContextCarrier } from '../trace/context/ContextCarrier'; -import { createLogger } from '../logging'; -import PluginInstaller from '../core/PluginInstaller'; - -const logger = createLogger(__filename); +import onFinished from 'on-finished'; class ExpressPlugin implements SwPlugin { readonly module = 'express'; readonly versions = '*'; install(): void { - if (logger.isDebugEnabled()) { - logger.debug('installing express plugin'); - } this.interceptServerRequest(); } private interceptServerRequest() { - const onFinished = PluginInstaller.require('on-finished'); - const router = PluginInstaller.require('express/lib/router'); + const router = require('express/lib/router'); const _handle = router.handle; router.handle = function(req: IncomingMessage, res: ServerResponse, out: any) { @@ -58,13 +51,16 @@ class ExpressPlugin implements SwPlugin { const span = ContextManager.current.newEntrySpan(operation, carrier).start(); let stopped = 0; - const stopIfNotStopped = () => { + const stopIfNotStopped = (err: Error | null) => { if (!stopped++) { span.stop(); span.tag(Tag.httpStatusCode(res.statusCode)); if (res.statusCode && res.statusCode >= 400) { span.errored = true; } + if (err) { + span.error(err); + } if (res.statusMessage) { span.tag(Tag.httpStatusMsg(res.statusMessage)); } @@ -78,21 +74,19 @@ class ExpressPlugin implements SwPlugin { span.tag(Tag.httpURL(span.peer + req.url)); const ret = _handle.call(this, req, res, (err: Error) => { - if (err) + if (err) { span.error(err); - else + } else { span.errored = true; + } out.call(this, err); stopped -= 1; // skip first stop attempt, make sure stop executes once status code and message is set - onFinished(req, stopIfNotStopped); // this must run after any handlers deferred in 'out' }); - onFinished(req, stopIfNotStopped); + onFinished(res, stopIfNotStopped); // this must run after any handlers deferred in 'out' return ret; - } catch (e) { - span.error(e); - stopIfNotStopped(); + stopIfNotStopped(e); throw e; } }; diff --git a/src/plugins/HttpPlugin.ts b/src/plugins/HttpPlugin.ts index 2984cc5..5c2d8b2 100644 --- a/src/plugins/HttpPlugin.ts +++ b/src/plugins/HttpPlugin.ts @@ -26,19 +26,13 @@ import Tag from '../Tag'; import ExitSpan from '../trace/span/ExitSpan'; import { SpanLayer } from '../proto/language-agent/Tracing_pb'; import { ContextCarrier } from '../trace/context/ContextCarrier'; -import { createLogger } from '../logging'; - -const logger = createLogger(__filename); +import onFinished from 'on-finished'; class HttpPlugin implements SwPlugin { readonly module = 'http'; readonly versions = '*'; install(): void { - if (logger.isDebugEnabled()) { - logger.debug('installing http plugin'); - } - const http = require('http'); const https = require('https'); @@ -51,7 +45,7 @@ class HttpPlugin implements SwPlugin { private interceptClientRequest(module: any) { const _request = module.request; - module.request = function () { + module.request = function() { const url: URL | string | RequestOptions = arguments[0]; const { host, pathname } = @@ -60,16 +54,26 @@ class HttpPlugin implements SwPlugin { : typeof url === 'string' ? new URL(url) // TODO: this may throw invalid URL : { - host: (url.host || url.hostname || 'unknown') + ':' + url.port, - pathname: url.path || '/', - }; + host: (url.host || url.hostname || 'unknown') + ':' + (url.port || 80), + pathname: url.path || '/', + }; const operation = pathname.replace(/\?.*$/g, ''); - let stopped = 0; // compensating if request aborted right after creation 'close' is not emitted - const stopIfNotStopped = () => !stopped++ ? span.stop() : null; // make sure we stop only once const span: ExitSpan = ContextManager.current.newExitSpan(operation, host).start() as ExitSpan; + let stopped = 0; // compensating if request aborted right after creation 'close' is not emitted + const stopIfNotStopped = (err?: Error | null) => { + if (stopped++) { + return; + } + span.stop(); + if (err) { + span.error(err); + } + }; + try { + // TODO: these should go into span class if (span.depth === 1) { // only set HTTP if this span is not overridden by a higher level one span.component = Component.HTTP; span.layer = SpanLayer.HTTP; @@ -82,17 +86,13 @@ class HttpPlugin implements SwPlugin { span.tag(Tag.httpURL(httpURL)); } - const request: ClientRequest = _request.apply(this, arguments); + const req: ClientRequest = _request.apply(this, arguments); span.inject().items.forEach((item) => { - request.setHeader(item.key, item.value); + req.setHeader(item.key, item.value); }); - request.on('close', stopIfNotStopped); - request.on('abort', () => (span.errored = true, stopIfNotStopped())); - request.on('error', (err) => (span.error(err), stopIfNotStopped())); - - request.prependListener('response', (res) => { + req.prependListener('response', (res) => { span.resync(); span.tag(Tag.httpStatusCode(res.statusCode)); if (res.statusCode && res.statusCode >= 400) { @@ -101,19 +101,15 @@ class HttpPlugin implements SwPlugin { if (res.statusMessage) { span.tag(Tag.httpStatusMsg(res.statusMessage)); } - stopIfNotStopped(); }); + onFinished(req, stopIfNotStopped); span.async(); - return request; + return req; } catch (e) { - if (!stopped) { // don't want to set error if exception occurs after clean close - span.error(e); - stopIfNotStopped(); - } - + stopIfNotStopped(e); throw e; } }; @@ -122,7 +118,7 @@ class HttpPlugin implements SwPlugin { private interceptServerRequest(module: any) { const _emit = module.Server.prototype.emit; - module.Server.prototype.emit = function () { + module.Server.prototype.emit = function() { if (arguments[0] !== 'request') { return _emit.apply(this, arguments); } @@ -138,27 +134,37 @@ class HttpPlugin implements SwPlugin { const carrier = ContextCarrier.from(headersMap); const operation = (req.url || '/').replace(/\?.*/g, ''); - const span = ContextManager.current.newEntrySpan(operation, carrier); + const span = ContextManager.current.newEntrySpan(operation, carrier).start(); - return ContextManager.withSpan(span, (self, args) => { - span.component = Component.HTTP_SERVER; - span.layer = SpanLayer.HTTP; - span.peer = req.headers.host || ''; - span.tag(Tag.httpURL(span.peer + req.url)); + span.component = Component.HTTP_SERVER; + span.layer = SpanLayer.HTTP; + span.peer = req.headers.host || ''; + span.tag(Tag.httpURL(span.peer + req.url)); - const ret = _emit.apply(self, args); - - span.tag(Tag.httpStatusCode(res.statusCode)); - if (res.statusCode && res.statusCode >= 400) { - span.errored = true; - } - if (res.statusMessage) { - span.tag(Tag.httpStatusMsg(res.statusMessage)); + let stopped = 0; + const stopIfNotStopped = (err: Error | null) => { + if (!stopped++) { + span.stop(); + span.tag(Tag.httpStatusCode(res.statusCode)); + if (res.statusCode && res.statusCode >= 400) { + span.errored = true; + } + if (err) { + span.error(err); + } + if (res.statusMessage) { + span.tag(Tag.httpStatusMsg(res.statusMessage)); + } } + }; + onFinished(res, stopIfNotStopped); - return ret; - - }, this, arguments); + try { + return _emit.apply(this, arguments); + } catch (e) { + stopIfNotStopped(e); + throw e; + } }; } } diff --git a/src/trace/Component.ts b/src/trace/Component.ts index 60c4c5d..1eb9597 100644 --- a/src/trace/Component.ts +++ b/src/trace/Component.ts @@ -18,11 +18,12 @@ */ export class Component { - static UNKNOWN = new Component(0); - static HTTP = new Component(2); - static MONGODB = new Component(9); - static HTTP_SERVER = new Component(49); - static EXPRESS = new Component(4002); + static readonly UNKNOWN = new Component(0); + static readonly HTTP = new Component(2); + static readonly MONGODB = new Component(9); + static readonly HTTP_SERVER = new Component(49); + static readonly EXPRESS = new Component(4002); + static readonly AXIOS = new Component(4005); - constructor(public id: number) {} + constructor(public readonly id: number) {} } diff --git a/src/trace/context/ContextManager.ts b/src/trace/context/ContextManager.ts index 3ed253e..a586856 100644 --- a/src/trace/context/ContextManager.ts +++ b/src/trace/context/ContextManager.ts @@ -21,26 +21,38 @@ import Context from '../../trace/context/Context'; import Span from '../../trace/span/Span'; import SpanContext from '../../trace/context/SpanContext'; -type AsyncState = {context: Context, spans: Span[]}; +import async_hooks from 'async_hooks'; -const async_hooks = require('async_hooks'); -let store: any; +type AsyncState = { context: Context, spans: Span[] }; + +let store: { + getStore(): AsyncState | undefined; + enterWith(s: AsyncState): void; +}; if (async_hooks.AsyncLocalStorage) { store = new async_hooks.AsyncLocalStorage(); - } else { // Node 10 doesn't have AsyncLocalStore, so recreate it const executionAsyncId = async_hooks.executionAsyncId; - const asyncLocalStore: {[index: string]: any} = {}; + const asyncLocalStore: { [index: string]: any } = {}; - store = new class { - getStore(): AsyncState | undefined { return asyncLocalStore[executionAsyncId()] as unknown as AsyncState; } - enterWith(store: AsyncState): void { asyncLocalStore[executionAsyncId()] = store; } - }(); + store = { + getStore(): AsyncState | undefined { + return asyncLocalStore[executionAsyncId()] as unknown as AsyncState; + }, + + enterWith(s: AsyncState): void { + asyncLocalStore[executionAsyncId()] = s; + }, + }; async_hooks.createHook({ - init(asyncId: number, type: string, triggerId: number) { asyncLocalStore[asyncId] = asyncLocalStore[triggerId]; }, - destroy(asyncId: number) { delete asyncLocalStore[asyncId]; } + init(asyncId: number, type: string, triggerId: number) { + asyncLocalStore[asyncId] = asyncLocalStore[triggerId]; + }, + destroy(asyncId: number) { + delete asyncLocalStore[asyncId]; + }, }).enable(); } @@ -49,23 +61,28 @@ class ContextManager { let asyncState = store.getStore(); if (asyncState === undefined) { - asyncState = {context: new SpanContext(), spans: []}; + asyncState = { context: new SpanContext(), spans: [] }; store.enterWith(asyncState); } return asyncState; } - get current(): Context { return this.asyncState.context; } - get spans(): Span[] { return this.asyncState.spans; } + get current(): Context { + return this.asyncState.context; + } + + get spans(): Span[] { + return this.asyncState.spans; + } spansDup(): Span[] { let asyncState = store.getStore(); if (asyncState === undefined) { - asyncState = {context: new SpanContext(), spans: []}; + asyncState = { context: new SpanContext(), spans: [] }; } else { - asyncState = {context: asyncState.context, spans: [...asyncState.spans]}; + asyncState = { context: asyncState.context, spans: [...asyncState.spans] }; } store.enterWith(asyncState); @@ -78,13 +95,12 @@ class ContextManager { } restore(context: Context, spans: Span[]): void { - store.enterWith({context, spans: spans || []}); + store.enterWith({ context, spans: spans || [] }); } withSpan(span: Span, callback: (...args: any[]) => any, ...args: any[]): any { - if(!span.startTime) + if (!span.startTime) span.start(); - try { return callback(...args); } catch (e) { @@ -96,9 +112,8 @@ class ContextManager { } async withSpanAwait(span: Span, callback: (...args: any[]) => any, ...args: any[]): Promise { - if(!span.startTime) + if (!span.startTime) span.start(); - try { return await callback(...args); } catch (e) { diff --git a/src/trace/context/SpanContext.ts b/src/trace/context/SpanContext.ts index c08f524..6eb2c59 100644 --- a/src/trace/context/SpanContext.ts +++ b/src/trace/context/SpanContext.ts @@ -26,12 +26,12 @@ import Segment from '../../trace/context/Segment'; import EntrySpan from '../../trace/span/EntrySpan'; import ExitSpan from '../../trace/span/ExitSpan'; import LocalSpan from '../../trace/span/LocalSpan'; -import buffer from '../../agent/Buffer'; import { createLogger } from '../../logging'; import { executionAsyncId } from 'async_hooks'; import { ContextCarrier } from './ContextCarrier'; import ContextManager from './ContextManager'; import { SpanType } from '../../proto/language-agent/Tracing_pb'; +import { emitter } from '../../lib/EventEmitter'; const logger = createLogger(__filename); @@ -155,7 +155,11 @@ export default class SpanContext implements Context { } start(span: Span): Context { - logger.debug('Starting span', { span: span.operation, spans: ContextManager.spans, nSpans: this.nSpans }); + logger.debug('Starting span', { + span: span.operation, + spans: ContextManager.spans, + nSpans: this.nSpans, + }); this.nSpans += 1; if (ContextManager.spans.every((s) => s.id !== span.id)) { @@ -166,7 +170,11 @@ export default class SpanContext implements Context { } stop(span: Span): boolean { - logger.debug('Stopping span', { span: span.operation, spans: ContextManager.spans, nSpans: this.nSpans }); + logger.debug('Stopping span', { + span: span.operation, + spans: ContextManager.spans, + nSpans: this.nSpans, + }); span.finish(this.segment); @@ -176,7 +184,7 @@ export default class SpanContext implements Context { } if (--this.nSpans === 0) { - buffer.put(this.segment); + emitter.emit('segment-finished', this.segment); ContextManager.clear(); return true; } @@ -185,7 +193,11 @@ export default class SpanContext implements Context { } async(span: Span) { - logger.debug('Async span', { span: span.operation, spans: ContextManager.spans, nSpans: this.nSpans }); + logger.debug('Async span', { + span: span.operation, + spans: ContextManager.spans, + nSpans: this.nSpans, + }); const idx = ContextManager.spans.indexOf(span); @@ -199,7 +211,11 @@ export default class SpanContext implements Context { } resync(span: Span) { - logger.debug('Resync span', { span: span.operation, spans: ContextManager.spans, nSpans: this.nSpans }); + logger.debug('Resync span', { + span: span.operation, + spans: ContextManager.spans, + nSpans: this.nSpans, + }); if ((span.context as SpanContext).nSpans === 1) { ContextManager.restore(span.context, [span]); diff --git a/src/trace/span/EntrySpan.ts b/src/trace/span/EntrySpan.ts index 59e841b..6c49e9a 100644 --- a/src/trace/span/EntrySpan.ts +++ b/src/trace/span/EntrySpan.ts @@ -47,9 +47,7 @@ export default class EntrySpan extends StackedSpan { const ref = SegmentRef.fromCarrier(carrier); - if (!this.refs.includes(ref)) { - this.refs.push(ref); - } + this.refer(ref); return this; } diff --git a/src/trace/span/StackedSpan.ts b/src/trace/span/StackedSpan.ts index 8c1886f..3b1fc74 100644 --- a/src/trace/span/StackedSpan.ts +++ b/src/trace/span/StackedSpan.ts @@ -18,12 +18,8 @@ */ import Span, { SpanCtorOptions } from '../../trace/span/Span'; -import Segment from '../../trace/context/Segment'; -import { createLogger } from '../../logging'; import { SpanType } from '../../proto/language-agent/Tracing_pb'; -const logger = createLogger(__filename); - export default class StackedSpan extends Span { depth = 0; diff --git a/tests/plugins/axios/client.ts b/tests/plugins/axios/client.ts new file mode 100644 index 0000000..91f435d --- /dev/null +++ b/tests/plugins/axios/client.ts @@ -0,0 +1,36 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as http from 'http'; +import agent from '../../../src'; +import axios from 'axios'; + +agent.start({ + serviceName: 'client', + maxBufferSize: 1000, +}); + +const server = http.createServer((req, res) => { + axios + .get(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`) + .then((r) => res.end(JSON.stringify(r.data))) + .catch(err => res.end(JSON.stringify(err))); +}); + +server.listen(5001, () => console.info('Listening on port 5001...')); diff --git a/tests/plugins/axios/docker-compose.yml b/tests/plugins/axios/docker-compose.yml new file mode 100644 index 0000000..b88cbee --- /dev/null +++ b/tests/plugins/axios/docker-compose.yml @@ -0,0 +1,65 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: '3.8' + +services: + collector: + extends: + file: ../common/base-compose.yml + service: collector + networks: + - traveling-light + + server: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5000:5000 + volumes: + - .:/app/tests/plugins/express + healthcheck: + test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000" ] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/axios/server.ts' ] + depends_on: + collector: + condition: service_healthy + + client: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5001:5001 + environment: + SERVER: server:5000 + healthcheck: + test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001" ] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/axios/client.ts' ] + depends_on: + server: + condition: service_healthy + +networks: + traveling-light: diff --git a/tests/plugins/axios/expected.data.yaml b/tests/plugins/axios/expected.data.yaml new file mode 100644 index 0000000..1f24544 --- /dev/null +++ b/tests/plugins/axios/expected.data.yaml @@ -0,0 +1,109 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +segmentItems: + - serviceName: server + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: /json + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Http + tags: + - key: http.url + value: httpbin.org:80/json + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + startTime: gt 0 + endTime: gt 0 + componentId: 4005 + spanType: Exit + peer: httpbin.org + skipAnalysis: false + - operationName: /axios + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + tags: + - key: http.url + value: server:5000/axios + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + refs: + - parentEndpoint: '' + networkAddress: server:5000 + refType: CrossProcess + parentSpanId: 1 + parentTraceSegmentId: not null + parentServiceInstance: not null + parentService: client + traceId: not null + startTime: gt 0 + endTime: gt 0 + componentId: 49 + spanType: Entry + peer: server:5000 + skipAnalysis: false + - serviceName: client + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: /axios + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Http + tags: + - key: http.url + value: server:5000/axios + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + startTime: gt 0 + endTime: gt 0 + componentId: 4005 + spanType: Exit + peer: server:5000 + skipAnalysis: false + - operationName: /axios + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + tags: + - key: http.url + value: localhost:5001/axios + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + startTime: gt 0 + endTime: gt 0 + componentId: 49 + spanType: Entry + peer: localhost:5001 + skipAnalysis: false diff --git a/tests/plugins/axios/server.ts b/tests/plugins/axios/server.ts new file mode 100644 index 0000000..bbca28d --- /dev/null +++ b/tests/plugins/axios/server.ts @@ -0,0 +1,34 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import agent from '../../../src'; +import * as http from 'http'; +import axios from 'axios'; + +agent.start({ + serviceName: 'server', + maxBufferSize: 1000, +}); + +const server = http.createServer(async (req, res) => { + const r = await axios.get('http://httpbin.org/json'); + res.end(JSON.stringify(r.data)); +}); + +server.listen(5000, () => console.info('Listening on port 5000...')); diff --git a/tests/plugins/axios/test.ts b/tests/plugins/axios/test.ts new file mode 100644 index 0000000..63156be --- /dev/null +++ b/tests/plugins/axios/test.ts @@ -0,0 +1,56 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as path from 'path'; +import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; +import axios from 'axios'; +import waitForExpect from 'wait-for-expect'; +import { promises as fs } from 'fs'; + +const rootDir = path.resolve(__dirname); + +describe('plugin tests', () => { + let compose: StartedDockerComposeEnvironment; + + beforeAll(async () => { + compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') + .withWaitStrategy('client', Wait.forHealthCheck()) + .up(); + }); + + afterAll(async () => { + await compose.down(); + }); + + it(__filename, async () => { + await waitForExpect(async () => expect((await axios.get('http://localhost:5001/axios')).status).toBe(200)); + + const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); + + try { + await waitForExpect(async () => + expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), + ); + } catch (e) { + const actualData = (await axios.get('http://localhost:12800/receiveData')).data; + console.info({ actualData }); + throw e; + } + }); +}); diff --git a/tests/plugins/common/Dockerfile.agent b/tests/plugins/common/Dockerfile.agent index 4c51529..5e907d4 100644 --- a/tests/plugins/common/Dockerfile.agent +++ b/tests/plugins/common/Dockerfile.agent @@ -24,4 +24,3 @@ WORKDIR /app ADD $ROOT /app RUN npm install request && npm install - diff --git a/tests/plugins/common/base-compose.yml b/tests/plugins/common/base-compose.yml index 1e16c0a..677493b 100644 --- a/tests/plugins/common/base-compose.yml +++ b/tests/plugins/common/base-compose.yml @@ -22,7 +22,6 @@ services: build: context: ../../../ dockerfile: tests/plugins/common/Dockerfile.tool - container_name: collector ports: - 12800:12800 networks: diff --git a/tests/plugins/express/client.ts b/tests/plugins/express/client.ts new file mode 100644 index 0000000..20e0487 --- /dev/null +++ b/tests/plugins/express/client.ts @@ -0,0 +1,41 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as http from 'http'; +import agent from '../../../src'; +import express from 'express'; + +agent.start({ + serviceName: 'client', + maxBufferSize: 1000, +}); + +const app = express(); + +app.get('/express', (req, res) => { + http + .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { + let data = ''; + r.on('data', (chunk) => (data += chunk)); + r.on('end', () => res.send(data)); + }) + .end(); +}); + +app.listen(5001, () => console.info('Listening on port 5001...')); diff --git a/tests/plugins/express/docker-compose.yml b/tests/plugins/express/docker-compose.yml new file mode 100644 index 0000000..224e4c6 --- /dev/null +++ b/tests/plugins/express/docker-compose.yml @@ -0,0 +1,65 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: '3.8' + +services: + collector: + extends: + file: ../common/base-compose.yml + service: collector + networks: + - traveling-light + + server: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5000:5000 + volumes: + - .:/app/tests/plugins/express + healthcheck: + test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5000" ] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/express/server.ts' ] + depends_on: + collector: + condition: service_healthy + + client: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - 5001:5001 + environment: + SERVER: server:5000 + healthcheck: + test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5001" ] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: [ 'bash', '-c', 'npx ts-node /app/tests/plugins/express/client.ts' ] + depends_on: + server: + condition: service_healthy + +networks: + traveling-light: diff --git a/tests/plugins/express/expected.data.yaml b/tests/plugins/express/expected.data.yaml new file mode 100644 index 0000000..4f6311a --- /dev/null +++ b/tests/plugins/express/expected.data.yaml @@ -0,0 +1,109 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +segmentItems: + - serviceName: server + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: /json + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Http + tags: + - key: http.url + value: httpbin.org/json + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + startTime: gt 0 + endTime: gt 0 + componentId: 2 + spanType: Exit + peer: httpbin.org + skipAnalysis: false + - operationName: /express + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + tags: + - key: http.url + value: server:5000/express + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + refs: + - parentEndpoint: '' + networkAddress: server:5000 + refType: CrossProcess + parentSpanId: 1 + parentTraceSegmentId: not null + parentServiceInstance: not null + parentService: client + traceId: not null + startTime: gt 0 + endTime: gt 0 + componentId: 4002 + spanType: Entry + peer: server:5000 + skipAnalysis: false + - serviceName: client + segmentSize: 1 + segments: + - segmentId: not null + spans: + - operationName: /express + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Http + tags: + - key: http.url + value: server:5000/express + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + startTime: gt 0 + endTime: gt 0 + componentId: 2 + spanType: Exit + peer: server:5000 + skipAnalysis: false + - operationName: /express + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + tags: + - key: http.url + value: localhost:5001/express + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + startTime: gt 0 + endTime: gt 0 + componentId: 4002 + spanType: Entry + peer: localhost:5001 + skipAnalysis: false diff --git a/tests/plugins/express/server.ts b/tests/plugins/express/server.ts new file mode 100644 index 0000000..9ce3346 --- /dev/null +++ b/tests/plugins/express/server.ts @@ -0,0 +1,41 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import agent from '../../../src'; +import * as http from 'http'; + +import express from 'express'; + +agent.start({ + serviceName: 'server', + maxBufferSize: 1000, +}); +const app = express(); + +app.get('/express', (req, res) => { + http + .request('http://httpbin.org/json', (r) => { + let data = ''; + r.on('data', (chunk) => (data += chunk)); + r.on('end', () => res.send(data)); + }) + .end(); +}); + +app.listen(5000, () => console.info('Listening on port 5000...')); diff --git a/tests/plugins/express/test.ts b/tests/plugins/express/test.ts new file mode 100644 index 0000000..b046f34 --- /dev/null +++ b/tests/plugins/express/test.ts @@ -0,0 +1,56 @@ +/*! + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as path from 'path'; +import { DockerComposeEnvironment, StartedDockerComposeEnvironment, Wait } from 'testcontainers'; +import axios from 'axios'; +import waitForExpect from 'wait-for-expect'; +import { promises as fs } from 'fs'; + +const rootDir = path.resolve(__dirname); + +describe('plugin tests', () => { + let compose: StartedDockerComposeEnvironment; + + beforeAll(async () => { + compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') + .withWaitStrategy('client', Wait.forHealthCheck()) + .up(); + }); + + afterAll(async () => { + await compose.down(); + }); + + it(__filename, async () => { + await waitForExpect(async () => expect((await axios.get('http://localhost:5001/express')).status).toBe(200)); + + const expectedData = await fs.readFile(path.join(rootDir, 'expected.data.yaml'), 'utf8'); + + try { + await waitForExpect(async () => + expect((await axios.post('http://localhost:12800/dataValidate', expectedData)).status).toBe(200), + ); + } catch (e) { + const actualData = (await axios.get('http://localhost:12800/receiveData')).data; + console.info({ actualData }); + throw e; + } + }); +}); diff --git a/tests/plugins/http/client.ts b/tests/plugins/http/client.ts index 2c2ce5e..c1ee8a9 100644 --- a/tests/plugins/http/client.ts +++ b/tests/plugins/http/client.ts @@ -18,21 +18,21 @@ */ import * as http from 'http'; -import Agent from '../../../src'; +import agent from '../../../src'; -Agent.start({ +agent.start({ serviceName: 'client', maxBufferSize: 1000, }); const server = http.createServer((req, res) => { http - .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { - let data = ''; - r.on('data', (chunk) => (data += chunk)); - r.on('end', () => res.end(data)); - }) - .end(); + .request(`http://${process.env.SERVER || 'localhost:5000'}${req.url}`, (r) => { + let data = ''; + r.on('data', (chunk) => (data += chunk)); + r.on('end', () => res.end(data)); + }) + .end(); }); server.listen(5001, () => console.info('Listening on port 5001...')); diff --git a/tests/plugins/http/docker-compose.yml b/tests/plugins/http/docker-compose.yml index 8905cdb..e5d8ca7 100644 --- a/tests/plugins/http/docker-compose.yml +++ b/tests/plugins/http/docker-compose.yml @@ -29,7 +29,6 @@ services: extends: file: ../common/base-compose.yml service: agent - container_name: server ports: - 5000:5000 volumes: @@ -48,7 +47,6 @@ services: extends: file: ../common/base-compose.yml service: agent - container_name: client ports: - 5001:5001 environment: diff --git a/tests/plugins/http/expected.data.yaml b/tests/plugins/http/expected.data.yaml index 29e6cff..1e9e6f2 100644 --- a/tests/plugins/http/expected.data.yaml +++ b/tests/plugins/http/expected.data.yaml @@ -21,25 +21,6 @@ segmentItems: segments: - segmentId: not null spans: - - operationName: /test - operationId: 0 - parentSpanId: -1 - spanId: 0 - spanLayer: Http - startTime: gt 0 - endTime: gt 0 - componentId: 49 - isError: false - spanType: Entry - peer: server:5000 - skipAnalysis: false - tags: - - { key: http.url, value: server:5000/test } - - { key: http.status.code, value: '200' } - refs: - - { parentEndpoint: '', networkAddress: 'server:5000', refType: CrossProcess, - parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not null, - parentService: client, traceId: not null } - operationName: /json operationId: 0 parentSpanId: 0 @@ -48,19 +29,16 @@ segmentItems: startTime: gt 0 endTime: gt 0 componentId: 2 - isError: false spanType: Exit peer: httpbin.org skipAnalysis: false tags: - - { key: http.url, value: httpbin.org/json } - - { key: http.status.code, value: '200' } - - { key: http.status.msg, value: OK } - - serviceName: client - segmentSize: 1 - segments: - - segmentId: not null - spans: + - key: http.url + value: httpbin.org/json + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK - operationName: /test operationId: 0 parentSpanId: -1 @@ -69,13 +47,30 @@ segmentItems: startTime: gt 0 endTime: gt 0 componentId: 49 - isError: false spanType: Entry - peer: localhost:5001 + peer: server:5000 skipAnalysis: false tags: - - { key: http.url, value: localhost:5001/test } - - { key: http.status.code, value: '200' } + - key: http.url + value: server:5000/test + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + refs: + - parentEndpoint: '' + networkAddress: server:5000 + refType: CrossProcess + parentSpanId: 1 + parentTraceSegmentId: not null + parentServiceInstance: not null + parentService: client + traceId: not null + - serviceName: client + segmentSize: 1 + segments: + - segmentId: not null + spans: - operationName: /test operationId: 0 parentSpanId: 0 @@ -84,11 +79,31 @@ segmentItems: startTime: gt 0 endTime: gt 0 componentId: 2 - isError: false spanType: Exit peer: server:5000 skipAnalysis: false tags: - - { key: http.url, value: 'server:5000/test' } - - { key: http.status.code, value: '200' } - - { key: http.status.msg, value: OK } + - key: http.url + value: server:5000/test + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK + - operationName: /test + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 49 + spanType: Entry + peer: localhost:5001 + skipAnalysis: false + tags: + - key: http.url + value: localhost:5001/test + - key: http.status.code + value: '200' + - key: http.status.msg + value: OK diff --git a/tests/plugins/http/server.ts b/tests/plugins/http/server.ts index b9cbc2f..bf362c7 100644 --- a/tests/plugins/http/server.ts +++ b/tests/plugins/http/server.ts @@ -17,22 +17,22 @@ * */ -import Agent from '../../../src'; +import agent from '../../../src'; import * as http from 'http'; -Agent.start({ +agent.start({ serviceName: 'server', maxBufferSize: 1000, }); const server = http.createServer((req, res) => { http - .request('http://httpbin.org/json', (r) => { - let data = ''; - r.on('data', (chunk) => (data += chunk)); - r.on('end', () => res.end(data)); - }) - .end(); + .request('http://httpbin.org/json', (r) => { + let data = ''; + r.on('data', (chunk) => (data += chunk)); + r.on('end', () => res.end(data)); + }) + .end(); }); server.listen(5000, () => console.info('Listening on port 5000...')); diff --git a/tests/plugins/http/test.ts b/tests/plugins/http/test.ts index 07966da..3550b0e 100644 --- a/tests/plugins/http/test.ts +++ b/tests/plugins/http/test.ts @@ -30,8 +30,8 @@ describe('plugin tests', () => { beforeAll(async () => { compose = await new DockerComposeEnvironment(rootDir, 'docker-compose.yml') - .withWaitStrategy('client', Wait.forHealthCheck()) - .up(); + .withWaitStrategy('client', Wait.forHealthCheck()) + .up(); }); afterAll(async () => {