From 5194ce1bb827f77c279f7fa2e7314973cb725533 Mon Sep 17 00:00:00 2001 From: Naseem Date: Thu, 21 Nov 2019 02:24:25 -0500 Subject: [PATCH 01/67] Add ioredis --- README.md | 1 + .../opentelemetry-plugin-ioredis/.npmignore | 4 + packages/opentelemetry-plugin-ioredis/LICENSE | 201 ++++++++++++++++ .../opentelemetry-plugin-ioredis/README.md | 63 +++++ .../opentelemetry-plugin-ioredis/package.json | 70 ++++++ .../opentelemetry-plugin-ioredis/src/enums.ts | 32 +++ .../opentelemetry-plugin-ioredis/src/index.ts | 17 ++ .../src/ioredis.ts | 59 +++++ .../opentelemetry-plugin-ioredis/src/types.ts | 33 +++ .../opentelemetry-plugin-ioredis/src/utils.ts | 96 ++++++++ .../test/assertionUtils.ts | 76 ++++++ .../test/ioredis.test.ts | 219 ++++++++++++++++++ .../test/testUtils.ts | 54 +++++ .../tsconfig.json | 11 + .../opentelemetry-plugin-ioredis/tslint.json | 4 + 15 files changed, 940 insertions(+) create mode 100644 packages/opentelemetry-plugin-ioredis/.npmignore create mode 100644 packages/opentelemetry-plugin-ioredis/LICENSE create mode 100644 packages/opentelemetry-plugin-ioredis/README.md create mode 100644 packages/opentelemetry-plugin-ioredis/package.json create mode 100644 packages/opentelemetry-plugin-ioredis/src/enums.ts create mode 100644 packages/opentelemetry-plugin-ioredis/src/index.ts create mode 100644 packages/opentelemetry-plugin-ioredis/src/ioredis.ts create mode 100644 packages/opentelemetry-plugin-ioredis/src/types.ts create mode 100644 packages/opentelemetry-plugin-ioredis/src/utils.ts create mode 100644 packages/opentelemetry-plugin-ioredis/test/assertionUtils.ts create mode 100644 packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts create mode 100644 packages/opentelemetry-plugin-ioredis/test/testUtils.ts create mode 100644 packages/opentelemetry-plugin-ioredis/tsconfig.json create mode 100644 packages/opentelemetry-plugin-ioredis/tslint.json diff --git a/README.md b/README.md index ebca1e3cea3..493f489c844 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ OpenTelemetry can collect tracing data automatically using plugins. Vendors/User - [@opentelemetry/plugin-dns](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-dns) - [@opentelemetry/plugin-mongodb](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-mongodb) - WIP - [@opentelemetry/plugin-postgres](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-postgres) - WIP +- [@opentelemetry/plugin-ioredis](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-ioredis) - WIP - [@opentelemetry/plugin-redis](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-redis) - WIP - [@opentelemetry/plugin-mysql](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-mysql) - WIP diff --git a/packages/opentelemetry-plugin-ioredis/.npmignore b/packages/opentelemetry-plugin-ioredis/.npmignore new file mode 100644 index 00000000000..9505ba9450f --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/.npmignore @@ -0,0 +1,4 @@ +/bin +/coverage +/doc +/test diff --git a/packages/opentelemetry-plugin-ioredis/LICENSE b/packages/opentelemetry-plugin-ioredis/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/LICENSE @@ -0,0 +1,201 @@ + 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. diff --git a/packages/opentelemetry-plugin-ioredis/README.md b/packages/opentelemetry-plugin-ioredis/README.md new file mode 100644 index 00000000000..b2120a57d9e --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/README.md @@ -0,0 +1,63 @@ +# OpenTelemetry ioredis Instrumentation for Node.js +[![Gitter chat][gitter-image]][gitter-url] +[![dependencies][dependencies-image]][dependencies-url] +[![devDependencies][devDependencies-image]][devDependencies-url] +[![Apache License][license-image]][license-image] + +This module provides automatic instrumentation for [`ioredis@^4.14.0`](https://github.com/luin/ioredis). + +For automatic instrumentation see the +[@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node) package. + +## Installation + +``` +npm install --save @opentelemetry/plugin-ioredis +``` + +### Supported Versions + - `>=4.14.0` + +## Usage + +OpenTelemetry IORedis Instrumentation allows the user to automatically collect trace data and export them to the backend of choice, to give observability to distributed systems when working with [ioredis](https://www.npmjs.com/package/ioredis). + +To load a specific plugin (**ioredis** in this case), specify it in the Node Tracer's configuration +```js +const { NodeTracer } = require('@opentelemetry/node'); + +const tracer = new NodeTracer({ + plugins: { + ioredis: { + enabled: true, + // You may use a package name or absolute path to the file. + path: '@opentelemetry/plugin-ioredis', + } + } +}); +``` + +To load all the [supported plugins](https://github.com/open-telemetry/opentelemetry-js#plugins), use below approach. Each plugin is only loaded when the module that it patches is loaded; in other words, there is no computational overhead for listing plugins for unused modules. +```javascript +const { NodeTracer } = require('@opentelemetry/node'); + +const tracer = new NodeTracer(); +``` + +## Useful links +- For more information on OpenTelemetry, visit: +- For more about OpenTelemetry JavaScript: +- For help or feedback on this project, join us on [gitter][gitter-url] + +## License + +Apache 2.0 - See [LICENSE][license-url] for more information. + +[gitter-image]: https://badges.gitter.im/open-telemetry/opentelemetry-js.svg +[gitter-url]: https://gitter.im/open-telemetry/opentelemetry-node?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge +[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/master/LICENSE +[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat +[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-plugin-redis +[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-redis +[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-plugin-redis +[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-redis&type=dev diff --git a/packages/opentelemetry-plugin-ioredis/package.json b/packages/opentelemetry-plugin-ioredis/package.json new file mode 100644 index 00000000000..38c6eb9acd6 --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/package.json @@ -0,0 +1,70 @@ +{ + "name": "@opentelemetry/plugin-ioredis", + "version": "0.2.0", + "description": "OpenTelemetry ioredis automatic instrumentation package.", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", + "repository": "open-telemetry/opentelemetry-js", + "scripts": { + "test": "nyc ts-mocha -p tsconfig.json 'test/**/*.ts'", + "test:debug": "cross-env RUN_REDIS_TESTS_LOCAL=true ts-mocha --inspect-brk --no-timeouts -p tsconfig.json 'test/**/*.test.ts'", + "test:local": "cross-env RUN_REDIS_TESTS_LOCAL=true yarn test", + "tdd": "yarn test -- --watch-extensions ts --watch", + "clean": "rimraf build/*", + "check": "gts check", + "precompile": "tsc --version", + "compile": "tsc -p .", + "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../", + "fix": "gts fix", + "prepare": "npm run compile" + }, + "keywords": [ + "opentelemetry", + "ioredis", + "redis", + "nodejs", + "tracing", + "profiling", + "plugin" + ], + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + }, + "files": [ + "build/src/**/*.js", + "build/src/**/*.d.ts", + "doc", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@opentelemetry/node": "^0.2.0", + "@opentelemetry/tracing": "^0.2.0", + "@types/ioredis": "^4.0.19", + "@types/mocha": "^5.2.7", + "@types/node": "^12.6.9", + "@types/shimmer": "^1.0.1", + "codecov": "^3.6.1", + "cross-env": "^6.0.3", + "gts": "^1.1.0", + "ioredis": "^4.14.1", + "mocha": "^6.2.0", + "nyc": "^14.1.1", + "rimraf": "^3.0.0", + "ts-mocha": "^6.0.0", + "ts-node": "^8.3.0", + "tslint-consistent-codestyle": "^1.15.1", + "tslint-microsoft-contrib": "^6.2.0", + "typescript": "3.7.2" + }, + "dependencies": { + "@opentelemetry/core": "^0.2.0", + "@opentelemetry/types": "^0.2.0", + "shimmer": "^1.2.1" + } +} diff --git a/packages/opentelemetry-plugin-ioredis/src/enums.ts b/packages/opentelemetry-plugin-ioredis/src/enums.ts new file mode 100644 index 00000000000..035287a14bb --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/src/enums.ts @@ -0,0 +1,32 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export enum AttributeNames { + // required by https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md#databases-client-calls + COMPONENT = 'component', + DB_TYPE = 'db.type', + DB_INSTANCE = 'db.instance', + DB_STATEMENT = 'db.statement', + PEER_ADDRESS = 'peer.address', + PEER_HOSTNAME = 'peer.host', + + // optional + DB_USER = 'db.user', + PEER_PORT = 'peer.port', + PEER_IPV4 = 'peer.ipv4', + PEER_IPV6 = 'peer.ipv6', + PEER_SERVICE = 'peer.service', +} diff --git a/packages/opentelemetry-plugin-ioredis/src/index.ts b/packages/opentelemetry-plugin-ioredis/src/index.ts new file mode 100644 index 00000000000..795d06430b8 --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/src/index.ts @@ -0,0 +1,17 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './ioredis'; diff --git a/packages/opentelemetry-plugin-ioredis/src/ioredis.ts b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts new file mode 100644 index 00000000000..ab438f4302f --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts @@ -0,0 +1,59 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePlugin } from '@opentelemetry/core'; +import * as ioredisTypes from 'ioredis'; +import * as shimmer from 'shimmer'; +import { getTracedSendCommand } from './utils'; + +export class IORedisPlugin extends BasePlugin { + static readonly COMPONENT = 'ioredis'; + readonly supportedVersions = ['^4.14.0']; + + constructor(readonly moduleName: string) { + super(); + } + + protected patch() { + if (this._moduleExports) { + this._logger.debug('Patching ioredis.prototype.sendCommand'); + shimmer.wrap( + this._moduleExports.prototype, + 'sendCommand', + this._getPatchSendCommand() + ); + } + return this._moduleExports.prototype; + } + + protected unpatch(): void { + if (this._moduleExports) { + shimmer.unwrap(this._moduleExports.prototype, 'sendCommand'); + } + } + + /** + * Patch send command internal to trace requests + */ + private _getPatchSendCommand() { + const tracer = this._tracer; + return function sendCommand(original: Function) { + return getTracedSendCommand(tracer, original); + }; + } +} + +export const plugin = new IORedisPlugin(IORedisPlugin.COMPONENT); diff --git a/packages/opentelemetry-plugin-ioredis/src/types.ts b/packages/opentelemetry-plugin-ioredis/src/types.ts new file mode 100644 index 00000000000..e76280cd22d --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/src/types.ts @@ -0,0 +1,33 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface IORedisCommand { + reject: (err: Error) => void; + resolve: (result: {}) => void; + promise: Promise<{}>; + args: Array; + callback: Function | undefined; + name: string; +} + +export interface IORedisPluginClientTypes { + options?: { + host: string; + port: string; + }; + + address?: string; +} diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts new file mode 100644 index 00000000000..24ba051d15d --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -0,0 +1,96 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as ioredisTypes from 'ioredis'; +import { Tracer, SpanKind, Span, CanonicalCode } from '@opentelemetry/types'; +import { IORedisPluginClientTypes, IORedisCommand } from './types'; +import { IORedisPlugin } from './ioredis'; +import { AttributeNames } from './enums'; + +const endSpan = (span: Span, err?: Error | null) => { + if (err) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: err.message, + }); + } else { + span.setStatus({ code: CanonicalCode.OK }); + } + span.end(); +}; + +export const getTracedCreateClient = (tracer: Tracer, original: Function) => { + return function createClientTrace(this: ioredisTypes.Redis) { + const client: ioredisTypes.Redis = original.apply(this, arguments); + return tracer.bind(client); + }; +}; + +export const getTracedSendCommand = (tracer: Tracer, original: Function) => { + return function sendCommandTrace( + this: ioredisTypes.Redis & IORedisPluginClientTypes, + cmd?: IORedisCommand + ) { + const parentSpan = tracer.getCurrentSpan(); + + if (arguments.length === 1 && typeof cmd === 'object') { + const span = tracer.startSpan(`${IORedisPlugin.COMPONENT}-${cmd.name}`, { + kind: SpanKind.CLIENT, + parent: parentSpan || undefined, + attributes: { + [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, + [AttributeNames.DB_STATEMENT]: cmd.name, + }, + }); + + // Set attributes for not explicitly typed IORedisPluginClientTypes + if (this.options) { + span.setAttributes({ + [AttributeNames.PEER_HOSTNAME]: this.options.host, + [AttributeNames.PEER_PORT]: this.options.port, + }); + } + if (this.address) { + span.setAttribute( + AttributeNames.PEER_ADDRESS, + `redis://${this.address}` + ); + } + + const originalCallback = arguments[0].callback; + if (originalCallback) { + (arguments[0] as IORedisCommand).callback = function callback( + this: unknown, + err: Error | null, + _reply: T + ) { + endSpan(span, err); + return originalCallback.apply(this, arguments); + }; + } + try { + // Span will be ended in callback + return original.apply(this, arguments); + } catch (rethrow) { + endSpan(span, rethrow); + throw rethrow; // rethrow after ending span + } + } + + // We don't know how to trace this call, so don't start/stop a span + return original.apply(this, arguments); + }; +}; diff --git a/packages/opentelemetry-plugin-ioredis/test/assertionUtils.ts b/packages/opentelemetry-plugin-ioredis/test/assertionUtils.ts new file mode 100644 index 00000000000..fdb517c7a09 --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/test/assertionUtils.ts @@ -0,0 +1,76 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + SpanKind, + Attributes, + Event, + Span, + Status, +} from '@opentelemetry/types'; +import * as assert from 'assert'; +import { ReadableSpan } from '@opentelemetry/tracing'; +import { + hrTimeToMilliseconds, + hrTimeToMicroseconds, +} from '@opentelemetry/core'; + +export const assertSpan = ( + span: ReadableSpan, + kind: SpanKind, + attributes: Attributes, + events: Event[], + status: Status +) => { + assert.strictEqual(span.spanContext.traceId.length, 32); + assert.strictEqual(span.spanContext.spanId.length, 16); + assert.strictEqual(span.kind, kind); + + assert.ok(span.endTime); + assert.strictEqual(span.links.length, 0); + + assert.ok( + hrTimeToMicroseconds(span.startTime) < hrTimeToMicroseconds(span.endTime) + ); + assert.ok(hrTimeToMilliseconds(span.endTime) > 0); + + // attributes + assert.deepStrictEqual(span.attributes, attributes); + + // events + assert.deepStrictEqual(span.events, events); + + assert.strictEqual(span.status.code, status.code); + if (status.message) { + assert.strictEqual(span.status.message, status.message); + } +}; + +// Check if childSpan was propagated from parentSpan +export const assertPropagation = ( + childSpan: ReadableSpan, + parentSpan: Span +) => { + const targetSpanContext = childSpan.spanContext; + const sourceSpanContext = parentSpan.context(); + assert.strictEqual(targetSpanContext.traceId, sourceSpanContext.traceId); + assert.strictEqual(childSpan.parentSpanId, sourceSpanContext.spanId); + assert.strictEqual( + targetSpanContext.traceFlags, + sourceSpanContext.traceFlags + ); + assert.notStrictEqual(targetSpanContext.spanId, sourceSpanContext.spanId); +}; diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts new file mode 100644 index 00000000000..6fcf98b99e1 --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -0,0 +1,219 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; +import { NodeTracer } from '@opentelemetry/node'; +import { plugin, IORedisPlugin } from '../src'; +import * as ioredisTypes from 'ioredis'; +import { NoopLogger } from '@opentelemetry/core'; +import * as dockerUtils from './testUtils'; +import * as assertionUtils from './assertionUtils'; +import { SpanKind, Status, CanonicalCode } from '@opentelemetry/types'; +import { AttributeNames } from '../src/enums'; + +const memoryExporter = new InMemorySpanExporter(); + +const CONFIG = { + host: process.env.OPENTELEMETRY_REDIS_HOST || 'localhost', + port: process.env.OPENTELEMETRY_REDIS_PORT || '63790', +}; + +const URL = `redis://${CONFIG.host}:${CONFIG.port}`; + +const DEFAULT_ATTRIBUTES = { + [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, + [AttributeNames.PEER_HOSTNAME]: CONFIG.host, + [AttributeNames.PEER_PORT]: CONFIG.port, + [AttributeNames.PEER_ADDRESS]: URL, +}; + +const okStatus: Status = { + code: CanonicalCode.OK, +}; + +describe('ioredis', () => { + const tracer = new NodeTracer(); + let ioredis: typeof ioredisTypes; + const shouldTestLocal = process.env.RUN_REDIS_TESTS_LOCAL; + const shouldTest = process.env.RUN_REDIS_TESTS || shouldTestLocal; + + before(function() { + // needs to be "function" to have MochaContext "this" scope + if (!shouldTest) { + // this.skip() workaround + // https://github.com/mochajs/mocha/issues/2683#issuecomment-375629901 + this.test!.parent!.pending = true; + this.skip(); + } + + if (shouldTestLocal) { + dockerUtils.startDocker(); + } + + ioredis = require('ioredis'); + tracer.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); + plugin.enable(ioredis, tracer, new NoopLogger()); + }); + + after(() => { + if (shouldTestLocal) { + dockerUtils.cleanUpDocker(); + } + }); + + it('should have correct module name', () => { + assert.strictEqual(plugin.moduleName, IORedisPlugin.COMPONENT); + }); + + describe('#createClient()', () => { + it('should propagate the current span to event handlers', done => { + const span = tracer.startSpan('test span'); + let client: ioredisTypes.Redis; + const readyHandler = () => { + assert.strictEqual(tracer.getCurrentSpan(), span); + client.quit(done); + }; + const errorHandler = (err: Error) => { + assert.ifError(err); + client.quit(done); + }; + + tracer.withSpan(span, () => { + client = new ioredis(URL); + client.on('ready', readyHandler); + client.on('error', errorHandler); + }); + }); + }); + + describe('#send_internal_message()', () => { + let client: ioredisTypes.Redis; + + const REDIS_OPERATIONS: Array<{ + description: string; + command: string; + method: (cb: ioredisTypes.CallbackFunction) => unknown; + }> = [ + { + description: 'insert', + command: 'hset', + method: (cb: ioredisTypes.CallbackFunction) => + client.hset('hash', 'random', 'random', cb), + }, + { + description: 'get', + command: 'get', + method: (cb: ioredisTypes.CallbackFunction) => + client.get('test'), + }, + { + description: 'delete', + command: 'del', + method: (cb: ioredisTypes.CallbackFunction) => + client.del('test'), + }, + ]; + + before(done => { + client = new ioredis(URL); + client.on('error', err => { + done(err); + }); + client.on('ready', done); + }); + + beforeEach(done => { + client.set('test', 'data', () => { + memoryExporter.reset(); + done(); + }); + }); + + after(done => { + client.quit(done); + }); + + afterEach(done => { + client.del('hash', () => { + memoryExporter.reset(); + done(); + }); + }); + + describe('Instrumenting query operations', () => { + REDIS_OPERATIONS.forEach(operation => { + it(`should create a child span for ${operation.description}`, done => { + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_STATEMENT]: operation.command, + }; + const span = tracer.startSpan('test span'); + tracer.withSpan(span, () => { + operation.method((err, _result) => { + assert.ifError(err); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + console.error('HAHAHAHAHAHAHAHA'); + + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual( + endedSpans[0].name, + `redis-${operation.command}` + ); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + done(); + }); + }); + }); + }); + }); + + describe('Removing instrumentation', () => { + before(() => { + plugin.disable(); + }); + + REDIS_OPERATIONS.forEach(operation => { + it(`should not create a child span for ${operation.description}`, done => { + const span = tracer.startSpan('test span'); + tracer.withSpan(span, () => { + operation.method((err, _) => { + assert.ifError(err); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 1); + assert.strictEqual(endedSpans[0], span); + done(); + }); + }); + }); + }); + }); + }); +}); diff --git a/packages/opentelemetry-plugin-ioredis/test/testUtils.ts b/packages/opentelemetry-plugin-ioredis/test/testUtils.ts new file mode 100644 index 00000000000..a40b0b4aab0 --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/test/testUtils.ts @@ -0,0 +1,54 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as childProcess from 'child_process'; +export function startDocker() { + const tasks = [ + run('docker run -d -p 63790:6379 --name otjsredis redis:alpine'), + ]; + + for (let i = 0; i < tasks.length; i++) { + const task = tasks[i]; + if (task && task.code !== 0) { + console.error('Failed to start container!'); + console.error(task.output); + return false; + } + } + return true; +} + +export function cleanUpDocker() { + run('docker stop otjsredis'); + run('docker rm otjsredis'); +} + +function run(cmd: string) { + try { + const proc = childProcess.spawnSync(cmd, { + shell: true, + }); + return { + code: proc.status, + output: proc.output + .map(v => String.fromCharCode.apply(null, v as any)) + .join(''), + }; + } catch (e) { + console.log(e); + return; + } +} diff --git a/packages/opentelemetry-plugin-ioredis/tsconfig.json b/packages/opentelemetry-plugin-ioredis/tsconfig.json new file mode 100644 index 00000000000..a2042cd68b1 --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.base", + "compilerOptions": { + "rootDir": ".", + "outDir": "build" + }, + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] +} diff --git a/packages/opentelemetry-plugin-ioredis/tslint.json b/packages/opentelemetry-plugin-ioredis/tslint.json new file mode 100644 index 00000000000..0710b135d07 --- /dev/null +++ b/packages/opentelemetry-plugin-ioredis/tslint.json @@ -0,0 +1,4 @@ +{ + "rulesDirectory": ["node_modules/tslint-microsoft-contrib"], + "extends": ["../../tslint.base.js", "./node_modules/tslint-consistent-codestyle"] +} From 78c809697bf4cd16c5f205912da9cec71624ae78 Mon Sep 17 00:00:00 2001 From: Naseem Date: Thu, 21 Nov 2019 16:34:54 -0500 Subject: [PATCH 02/67] remove unused util --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 24ba051d15d..1d137fababb 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -32,13 +32,6 @@ const endSpan = (span: Span, err?: Error | null) => { span.end(); }; -export const getTracedCreateClient = (tracer: Tracer, original: Function) => { - return function createClientTrace(this: ioredisTypes.Redis) { - const client: ioredisTypes.Redis = original.apply(this, arguments); - return tracer.bind(client); - }; -}; - export const getTracedSendCommand = (tracer: Tracer, original: Function) => { return function sendCommandTrace( this: ioredisTypes.Redis & IORedisPluginClientTypes, From 51c5761c2fa2f0e4b809157e81ce0341fa37bd67 Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 22 Nov 2019 02:29:39 -0500 Subject: [PATCH 03/67] Console error when callback cannot be found in command object --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 1d137fababb..dacccf14b88 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -63,9 +63,10 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { ); } - const originalCallback = arguments[0].callback; + const command = arguments[0]; + const originalCallback = command.callback; if (originalCallback) { - (arguments[0] as IORedisCommand).callback = function callback( + (command as IORedisCommand).callback = function callback( this: unknown, err: Error | null, _reply: T @@ -73,6 +74,8 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { endSpan(span, err); return originalCallback.apply(this, arguments); }; + } else { + console.error('Cannot figure out your callback', command); } try { // Span will be ended in callback From 3601d2ef5374c25004cd7d10e475267919d57370 Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 22 Nov 2019 02:30:53 -0500 Subject: [PATCH 04/67] fix --- .../test/ioredis.test.ts | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 6fcf98b99e1..573d022fe04 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -115,13 +115,13 @@ describe('ioredis', () => { description: 'insert', command: 'hset', method: (cb: ioredisTypes.CallbackFunction) => - client.hset('hash', 'random', 'random', cb), + client.hset('hash', 'testField', 'testValue', cb), }, { description: 'get', command: 'get', - method: (cb: ioredisTypes.CallbackFunction) => - client.get('test'), + method: (cb: ioredisTypes.CallbackFunction) => + client.get('test', cb), }, { description: 'delete', @@ -139,22 +139,18 @@ describe('ioredis', () => { client.on('ready', done); }); - beforeEach(done => { - client.set('test', 'data', () => { - memoryExporter.reset(); - done(); - }); + beforeEach(async () => { + await client.set('test', 'data'); + memoryExporter.reset(); }); after(done => { client.quit(done); }); - afterEach(done => { - client.del('hash', () => { - memoryExporter.reset(); - done(); - }); + afterEach(async () => { + client.del('hash'); + memoryExporter.reset(); }); describe('Instrumenting query operations', () => { @@ -169,8 +165,6 @@ describe('ioredis', () => { operation.method((err, _result) => { assert.ifError(err); assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); - console.error('HAHAHAHAHAHAHAHA'); - span.end(); const endedSpans = memoryExporter.getFinishedSpans(); assert.strictEqual(endedSpans.length, 2); From 339d7e4556560cc2535b4550ad8ec33b94ca12d1 Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 22 Nov 2019 16:02:07 -0500 Subject: [PATCH 05/67] There never seems to be a this.address --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index dacccf14b88..830a1ee2fc1 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -56,12 +56,6 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { [AttributeNames.PEER_PORT]: this.options.port, }); } - if (this.address) { - span.setAttribute( - AttributeNames.PEER_ADDRESS, - `redis://${this.address}` - ); - } const command = arguments[0]; const originalCallback = command.callback; From 81f025e6a6766b3fa9ef00082b04da798c6e8ea8 Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 22 Nov 2019 16:04:19 -0500 Subject: [PATCH 06/67] err can be made non-optional here since it is currently always passed in --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 830a1ee2fc1..ebcf633e318 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -20,7 +20,7 @@ import { IORedisPluginClientTypes, IORedisCommand } from './types'; import { IORedisPlugin } from './ioredis'; import { AttributeNames } from './enums'; -const endSpan = (span: Span, err?: Error | null) => { +const endSpan = (span: Span, err: Error | null) => { if (err) { span.setStatus({ code: CanonicalCode.UNKNOWN, From 9c22f7c931babed12cfbafa9e26f5995d90a2744 Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 22 Nov 2019 16:24:10 -0500 Subject: [PATCH 07/67] No address property --- packages/opentelemetry-plugin-ioredis/src/types.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/types.ts b/packages/opentelemetry-plugin-ioredis/src/types.ts index e76280cd22d..74563299054 100644 --- a/packages/opentelemetry-plugin-ioredis/src/types.ts +++ b/packages/opentelemetry-plugin-ioredis/src/types.ts @@ -28,6 +28,4 @@ export interface IORedisPluginClientTypes { host: string; port: string; }; - - address?: string; } From 04210a04cf9db3e9abc09f408cf1932fe9fae6a9 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 23 Nov 2019 17:48:38 -0500 Subject: [PATCH 08/67] Update packages --- packages/opentelemetry-plugin-ioredis/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/package.json b/packages/opentelemetry-plugin-ioredis/package.json index 38c6eb9acd6..93412ee9f86 100644 --- a/packages/opentelemetry-plugin-ioredis/package.json +++ b/packages/opentelemetry-plugin-ioredis/package.json @@ -47,18 +47,18 @@ "@opentelemetry/tracing": "^0.2.0", "@types/ioredis": "^4.0.19", "@types/mocha": "^5.2.7", - "@types/node": "^12.6.9", + "@types/node": "^12.12.12", "@types/shimmer": "^1.0.1", "codecov": "^3.6.1", "cross-env": "^6.0.3", - "gts": "^1.1.0", + "gts": "^1.1.2", "ioredis": "^4.14.1", - "mocha": "^6.2.0", + "mocha": "^6.2.2", "nyc": "^14.1.1", "rimraf": "^3.0.0", "ts-mocha": "^6.0.0", - "ts-node": "^8.3.0", - "tslint-consistent-codestyle": "^1.15.1", + "ts-node": "^8.5.2", + "tslint-consistent-codestyle": "^1.16.0", "tslint-microsoft-contrib": "^6.2.0", "typescript": "3.7.2" }, From 2912a72f9e4c8b11b37eee85add06d4466ade44c Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 23 Nov 2019 17:49:15 -0500 Subject: [PATCH 09/67] Adjust value of DB_STATEMENT --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index ebcf633e318..6661ee0630b 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -45,7 +45,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { parent: parentSpan || undefined, attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, - [AttributeNames.DB_STATEMENT]: cmd.name, + [AttributeNames.DB_STATEMENT]: cmd.name + ' ' + cmd.args.join(' '), }, }); From c6547d464107c1ddfa9a4a5b7d5bb8b4f73ca298 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 23 Nov 2019 17:55:59 -0500 Subject: [PATCH 10/67] Add DB_TYPE attribute --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 6661ee0630b..36d07fbc950 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -45,6 +45,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { parent: parentSpan || undefined, attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, + [AttributeNames.DB_TYPE]: 'redis', [AttributeNames.DB_STATEMENT]: cmd.name + ' ' + cmd.args.join(' '), }, }); From ea7dd9b7bfc69989dcc9619efe170ed07471cad4 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sun, 24 Nov 2019 17:02:56 -0500 Subject: [PATCH 11/67] Handle async/await ioredis commands that have no cb --- .../opentelemetry-plugin-ioredis/src/utils.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 36d07fbc950..e585290900e 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -58,10 +58,10 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { }); } - const command = arguments[0]; - const originalCallback = command.callback; + const originalCallback = cmd.callback; + const originalPromise = cmd.promise; if (originalCallback) { - (command as IORedisCommand).callback = function callback( + (cmd as IORedisCommand).callback = function callback( this: unknown, err: Error | null, _reply: T @@ -69,8 +69,26 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { endSpan(span, err); return originalCallback.apply(this, arguments); }; - } else { - console.error('Cannot figure out your callback', command); + } else if (originalPromise) { + return originalPromise + .then((result: unknown) => { + // Return a pass-along promise which ends the span and then goes to user's orig resolvers + return new Promise((resolve, _) => { + span.setStatus({ code: CanonicalCode.OK }); + span.end(); + resolve(result); + }); + }) + .catch((error: Error) => { + return new Promise((_, reject) => { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: error.message, + }); + span.end(); + reject(error); + }); + }); } try { // Span will be ended in callback From 96468360f04f54fc5ce7e67d919222f8509fd445 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sun, 24 Nov 2019 23:58:37 -0500 Subject: [PATCH 12/67] Fix promise handling and adjust attributes --- .../opentelemetry-plugin-ioredis/src/utils.ts | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index e585290900e..be45fae4d07 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -40,7 +40,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { const parentSpan = tracer.getCurrentSpan(); if (arguments.length === 1 && typeof cmd === 'object') { - const span = tracer.startSpan(`${IORedisPlugin.COMPONENT}-${cmd.name}`, { + const span = tracer.startSpan(cmd.name, { kind: SpanKind.CLIENT, parent: parentSpan || undefined, attributes: { @@ -52,9 +52,12 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { // Set attributes for not explicitly typed IORedisPluginClientTypes if (this.options) { + const { host } = this.options; + const { port } = this.options; span.setAttributes({ - [AttributeNames.PEER_HOSTNAME]: this.options.host, - [AttributeNames.PEER_PORT]: this.options.port, + [AttributeNames.PEER_HOSTNAME]: host, + [AttributeNames.PEER_PORT]: port, + [AttributeNames.PEER_ADDRESS]: `redis://${host}:${port}`, }); } @@ -69,8 +72,16 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { endSpan(span, err); return originalCallback.apply(this, arguments); }; + try { + // Span will be ended in callback + return original.apply(this, arguments); + } catch (rethrow) { + endSpan(span, rethrow); + throw rethrow; // rethrow after ending span + } } else if (originalPromise) { - return originalPromise + const result = original.apply(this, arguments); + return result .then((result: unknown) => { // Return a pass-along promise which ends the span and then goes to user's orig resolvers return new Promise((resolve, _) => { @@ -90,13 +101,6 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { }); }); } - try { - // Span will be ended in callback - return original.apply(this, arguments); - } catch (rethrow) { - endSpan(span, rethrow); - throw rethrow; // rethrow after ending span - } } // We don't know how to trace this call, so don't start/stop a span From f7a7d78cf903d96a549ef83f787936dc88bff0fa Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 25 Nov 2019 00:00:55 -0500 Subject: [PATCH 13/67] Use template literal --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index be45fae4d07..83c189d1252 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -46,7 +46,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, [AttributeNames.DB_TYPE]: 'redis', - [AttributeNames.DB_STATEMENT]: cmd.name + ' ' + cmd.args.join(' '), + [AttributeNames.DB_STATEMENT]: `${cmd.name} ${cmd.args.join(' ')}`, }, }); From 4bc164486460eea936909d1306d5338e72519562 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 25 Nov 2019 00:02:39 -0500 Subject: [PATCH 14/67] Add same comments as pg plugin for promise handling --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 83c189d1252..63e2ed5ce96 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -80,7 +80,9 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { throw rethrow; // rethrow after ending span } } else if (originalPromise) { + // Perform the original query const result = original.apply(this, arguments); + // Bind promise to parent span and end the span return result .then((result: unknown) => { // Return a pass-along promise which ends the span and then goes to user's orig resolvers From d3bfc0b69345b1598233fcd8e29581f338367dd5 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 25 Nov 2019 00:04:52 -0500 Subject: [PATCH 15/67] Use endSpan function --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 63e2ed5ce96..a8cab3de3ee 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -87,18 +87,13 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { .then((result: unknown) => { // Return a pass-along promise which ends the span and then goes to user's orig resolvers return new Promise((resolve, _) => { - span.setStatus({ code: CanonicalCode.OK }); - span.end(); + endSpan(span, null); resolve(result); }); }) .catch((error: Error) => { return new Promise((_, reject) => { - span.setStatus({ - code: CanonicalCode.UNKNOWN, - message: error.message, - }); - span.end(); + endSpan(span, error); reject(error); }); }); From fe69ce9650fc30d2f5a4084ccfb5d23b80573f04 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 25 Nov 2019 20:32:38 -0500 Subject: [PATCH 16/67] fix: combine 2 lines into 1 --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index a8cab3de3ee..0313420bd62 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -52,8 +52,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { // Set attributes for not explicitly typed IORedisPluginClientTypes if (this.options) { - const { host } = this.options; - const { port } = this.options; + const { host, port } = this.options; span.setAttributes({ [AttributeNames.PEER_HOSTNAME]: host, [AttributeNames.PEER_PORT]: port, From ced07675d341db96ce28b4027abd16aec8b45bb0 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 25 Nov 2019 20:33:22 -0500 Subject: [PATCH 17/67] chore: update modules --- packages/opentelemetry-plugin-ioredis/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/package.json b/packages/opentelemetry-plugin-ioredis/package.json index 93412ee9f86..ec7d5a0c49c 100644 --- a/packages/opentelemetry-plugin-ioredis/package.json +++ b/packages/opentelemetry-plugin-ioredis/package.json @@ -47,7 +47,7 @@ "@opentelemetry/tracing": "^0.2.0", "@types/ioredis": "^4.0.19", "@types/mocha": "^5.2.7", - "@types/node": "^12.12.12", + "@types/node": "^12.12.14", "@types/shimmer": "^1.0.1", "codecov": "^3.6.1", "cross-env": "^6.0.3", From f028231b49d5e3be724dc70131ca2d6c53101704 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 25 Nov 2019 20:41:46 -0500 Subject: [PATCH 18/67] fix: tests --- .../test/ioredis.test.ts | 111 ++++++++++++++++-- 1 file changed, 100 insertions(+), 11 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 573d022fe04..2f359242c31 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -32,7 +32,7 @@ const memoryExporter = new InMemorySpanExporter(); const CONFIG = { host: process.env.OPENTELEMETRY_REDIS_HOST || 'localhost', - port: process.env.OPENTELEMETRY_REDIS_PORT || '63790', + port: process.env.OPENTELEMETRY_REDIS_PORT || 63790, }; const URL = `redis://${CONFIG.host}:${CONFIG.port}`; @@ -106,7 +106,7 @@ describe('ioredis', () => { describe('#send_internal_message()', () => { let client: ioredisTypes.Redis; - const REDIS_OPERATIONS: Array<{ + const IOREDIS_CALLBACK_OPERATIONS: Array<{ description: string; command: string; method: (cb: ioredisTypes.CallbackFunction) => unknown; @@ -120,15 +120,9 @@ describe('ioredis', () => { { description: 'get', command: 'get', - method: (cb: ioredisTypes.CallbackFunction) => + method: (cb: ioredisTypes.CallbackFunction) => client.get('test', cb), }, - { - description: 'delete', - command: 'del', - method: (cb: ioredisTypes.CallbackFunction) => - client.del('test'), - }, ]; before(done => { @@ -154,7 +148,102 @@ describe('ioredis', () => { }); describe('Instrumenting query operations', () => { - REDIS_OPERATIONS.forEach(operation => { + it('should create a child span for hset promise', done => { + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_TYPE]: 'redis', + [AttributeNames.DB_STATEMENT]: 'hset hash random random', + }; + const span = tracer.startSpan('test span'); + tracer.withSpan(span, async () => { + try { + await client.hset('hash', 'random', 'random'); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual(endedSpans[0].name, `hset`); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + done(); + } catch (error) { + assert.ifError(error); + done(); + } + }); + }); + + it('should create a child span for get promise', done => { + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_TYPE]: 'redis', + [AttributeNames.DB_STATEMENT]: 'get test', + }; + const span = tracer.startSpan('test span'); + tracer.withSpan(span, async () => { + try { + const value = await client.get('test'); + assert.strictEqual(value, 'data'); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual(endedSpans[0].name, `get`); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + done(); + } catch (error) { + assert.ifError(error); + done(); + } + }); + }); + + it('should create a child span for del', done => { + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_TYPE]: 'redis', + [AttributeNames.DB_STATEMENT]: 'del test', + }; + const span = tracer.startSpan('test span'); + tracer.withSpan(span, async () => { + try { + const result = await client.del('test'); + assert.strictEqual(result, 1); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual(endedSpans[0].name, 'del'); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + done(); + } catch (error) { + assert.ifError(error); + done(); + } + }); + }); + + IOREDIS_CALLBACK_OPERATIONS.forEach(operation => { it(`should create a child span for ${operation.description}`, done => { const attributes = { ...DEFAULT_ATTRIBUTES, @@ -192,7 +281,7 @@ describe('ioredis', () => { plugin.disable(); }); - REDIS_OPERATIONS.forEach(operation => { + IOREDIS_CALLBACK_OPERATIONS.forEach(operation => { it(`should not create a child span for ${operation.description}`, done => { const span = tracer.startSpan('test span'); tracer.withSpan(span, () => { From fab7e83617a5c298bf795e912b362890fad2b489 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 25 Nov 2019 20:53:07 -0500 Subject: [PATCH 19/67] style: rename error --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 0313420bd62..60c098ace56 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -74,9 +74,9 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { try { // Span will be ended in callback return original.apply(this, arguments); - } catch (rethrow) { - endSpan(span, rethrow); - throw rethrow; // rethrow after ending span + } catch (error) { + endSpan(span, error); + throw error; } } else if (originalPromise) { // Perform the original query From 5a1638b6113cd945870c300fa7c91f89f56fd837 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 25 Nov 2019 20:58:51 -0500 Subject: [PATCH 20/67] docs: update min version --- packages/opentelemetry-plugin-ioredis/README.md | 4 ++-- packages/opentelemetry-plugin-ioredis/src/ioredis.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/README.md b/packages/opentelemetry-plugin-ioredis/README.md index b2120a57d9e..a2f922dd5d1 100644 --- a/packages/opentelemetry-plugin-ioredis/README.md +++ b/packages/opentelemetry-plugin-ioredis/README.md @@ -4,7 +4,7 @@ [![devDependencies][devDependencies-image]][devDependencies-url] [![Apache License][license-image]][license-image] -This module provides automatic instrumentation for [`ioredis@^4.14.0`](https://github.com/luin/ioredis). +This module provides automatic instrumentation for [`ioredis`](https://github.com/luin/ioredis). For automatic instrumentation see the [@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node) package. @@ -16,7 +16,7 @@ npm install --save @opentelemetry/plugin-ioredis ``` ### Supported Versions - - `>=4.14.0` + - `>=2.0.0` ## Usage diff --git a/packages/opentelemetry-plugin-ioredis/src/ioredis.ts b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts index ab438f4302f..8c53b8d595f 100644 --- a/packages/opentelemetry-plugin-ioredis/src/ioredis.ts +++ b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts @@ -21,7 +21,7 @@ import { getTracedSendCommand } from './utils'; export class IORedisPlugin extends BasePlugin { static readonly COMPONENT = 'ioredis'; - readonly supportedVersions = ['^4.14.0']; + readonly supportedVersions = ['^2.0.0']; constructor(readonly moduleName: string) { super(); From 16a1f402444e43198c6e312bd2add6bab449f0b8 Mon Sep 17 00:00:00 2001 From: Naseem Date: Tue, 26 Nov 2019 11:47:09 -0500 Subject: [PATCH 21/67] fix: nullish coalescing --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 60c098ace56..6deb8177b09 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -42,7 +42,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { if (arguments.length === 1 && typeof cmd === 'object') { const span = tracer.startSpan(cmd.name, { kind: SpanKind.CLIENT, - parent: parentSpan || undefined, + parent: parentSpan ?? undefined, attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, [AttributeNames.DB_TYPE]: 'redis', From a6b5f63584afdc3878de24a4ad2f1dab7f8a623f Mon Sep 17 00:00:00 2001 From: Naseem Date: Tue, 26 Nov 2019 11:49:01 -0500 Subject: [PATCH 22/67] Revert "fix: nullish coalescing" This reverts commit 16a1f402444e43198c6e312bd2add6bab449f0b8. --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 6deb8177b09..60c098ace56 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -42,7 +42,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { if (arguments.length === 1 && typeof cmd === 'object') { const span = tracer.startSpan(cmd.name, { kind: SpanKind.CLIENT, - parent: parentSpan ?? undefined, + parent: parentSpan || undefined, attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, [AttributeNames.DB_TYPE]: 'redis', From b002cfab0b394b7a9028b6dad3a25b25d2ee1e2a Mon Sep 17 00:00:00 2001 From: Naseem Date: Wed, 27 Nov 2019 21:35:36 -0500 Subject: [PATCH 23/67] fix: make DB_TYPE a const --- packages/opentelemetry-plugin-ioredis/src/ioredis.ts | 1 + packages/opentelemetry-plugin-ioredis/src/utils.ts | 2 +- packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/ioredis.ts b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts index 8c53b8d595f..67c7c474f35 100644 --- a/packages/opentelemetry-plugin-ioredis/src/ioredis.ts +++ b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts @@ -21,6 +21,7 @@ import { getTracedSendCommand } from './utils'; export class IORedisPlugin extends BasePlugin { static readonly COMPONENT = 'ioredis'; + static readonly DB_TYPE = 'redis'; readonly supportedVersions = ['^2.0.0']; constructor(readonly moduleName: string) { diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 60c098ace56..1e04ddb1e9d 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -45,7 +45,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { parent: parentSpan || undefined, attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, - [AttributeNames.DB_TYPE]: 'redis', + [AttributeNames.DB_TYPE]: IORedisPlugin.DB_TYPE, [AttributeNames.DB_STATEMENT]: `${cmd.name} ${cmd.args.join(' ')}`, }, }); diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 2f359242c31..827c91c4015 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -39,6 +39,7 @@ const URL = `redis://${CONFIG.host}:${CONFIG.port}`; const DEFAULT_ATTRIBUTES = { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, + [AttributeNames.DB_TYPE]: IORedisPlugin.DB_TYPE, [AttributeNames.PEER_HOSTNAME]: CONFIG.host, [AttributeNames.PEER_PORT]: CONFIG.port, [AttributeNames.PEER_ADDRESS]: URL, @@ -151,7 +152,6 @@ describe('ioredis', () => { it('should create a child span for hset promise', done => { const attributes = { ...DEFAULT_ATTRIBUTES, - [AttributeNames.DB_TYPE]: 'redis', [AttributeNames.DB_STATEMENT]: 'hset hash random random', }; const span = tracer.startSpan('test span'); @@ -182,7 +182,6 @@ describe('ioredis', () => { it('should create a child span for get promise', done => { const attributes = { ...DEFAULT_ATTRIBUTES, - [AttributeNames.DB_TYPE]: 'redis', [AttributeNames.DB_STATEMENT]: 'get test', }; const span = tracer.startSpan('test span'); @@ -214,7 +213,6 @@ describe('ioredis', () => { it('should create a child span for del', done => { const attributes = { ...DEFAULT_ATTRIBUTES, - [AttributeNames.DB_TYPE]: 'redis', [AttributeNames.DB_STATEMENT]: 'del test', }; const span = tracer.startSpan('test span'); From bef1af5d690baf43cfc4bd4ba20668ef461b7bec Mon Sep 17 00:00:00 2001 From: Naseem Date: Wed, 27 Nov 2019 21:39:44 -0500 Subject: [PATCH 24/67] fix: update links --- packages/opentelemetry-plugin-ioredis/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/README.md b/packages/opentelemetry-plugin-ioredis/README.md index a2f922dd5d1..8ca0dac6149 100644 --- a/packages/opentelemetry-plugin-ioredis/README.md +++ b/packages/opentelemetry-plugin-ioredis/README.md @@ -57,7 +57,7 @@ Apache 2.0 - See [LICENSE][license-url] for more information. [gitter-url]: https://gitter.im/open-telemetry/opentelemetry-node?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge [license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/master/LICENSE [license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat -[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-plugin-redis -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-redis -[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-plugin-redis -[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-redis&type=dev +[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-plugin-ioredis +[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-ioredis +[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-plugin-ioredis +[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-ioredis&type=dev From 94cda5bc22bf9181a01febf9d85cc41a150d7028 Mon Sep 17 00:00:00 2001 From: Naseem Date: Wed, 27 Nov 2019 21:41:24 -0500 Subject: [PATCH 25/67] fix: move ioredis to dependencies, update @types/ioredis --- packages/opentelemetry-plugin-ioredis/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/package.json b/packages/opentelemetry-plugin-ioredis/package.json index ec7d5a0c49c..dc3e48ff630 100644 --- a/packages/opentelemetry-plugin-ioredis/package.json +++ b/packages/opentelemetry-plugin-ioredis/package.json @@ -45,14 +45,13 @@ "devDependencies": { "@opentelemetry/node": "^0.2.0", "@opentelemetry/tracing": "^0.2.0", - "@types/ioredis": "^4.0.19", + "@types/ioredis": "^4.0.20", "@types/mocha": "^5.2.7", "@types/node": "^12.12.14", "@types/shimmer": "^1.0.1", "codecov": "^3.6.1", "cross-env": "^6.0.3", "gts": "^1.1.2", - "ioredis": "^4.14.1", "mocha": "^6.2.2", "nyc": "^14.1.1", "rimraf": "^3.0.0", @@ -63,6 +62,7 @@ "typescript": "3.7.2" }, "dependencies": { + "ioredis": "^4.14.1", "@opentelemetry/core": "^0.2.0", "@opentelemetry/types": "^0.2.0", "shimmer": "^1.2.1" From c872bf7fa836b91d065ef754a4b0d642ecadfb37 Mon Sep 17 00:00:00 2001 From: Naseem Date: Wed, 27 Nov 2019 21:43:09 -0500 Subject: [PATCH 26/67] fix: add a comment about nullish coalescing --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 1e04ddb1e9d..7b529fe9f8e 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -42,6 +42,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { if (arguments.length === 1 && typeof cmd === 'object') { const span = tracer.startSpan(cmd.name, { kind: SpanKind.CLIENT, + // Change || to ?? once widely supported in IDEs parent: parentSpan || undefined, attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, From 76099293d58be8bd50413d7c0a0d3b076e8feec0 Mon Sep 17 00:00:00 2001 From: Naseem Date: Wed, 27 Nov 2019 21:50:38 -0500 Subject: [PATCH 27/67] fix: move done from try/catch to finally --- .../opentelemetry-plugin-ioredis/test/ioredis.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 827c91c4015..80cf739eade 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -171,9 +171,9 @@ describe('ioredis', () => { okStatus ); assertionUtils.assertPropagation(endedSpans[0], span); - done(); } catch (error) { assert.ifError(error); + } finally { done(); } }); @@ -202,9 +202,9 @@ describe('ioredis', () => { okStatus ); assertionUtils.assertPropagation(endedSpans[0], span); - done(); } catch (error) { assert.ifError(error); + } finally { done(); } }); @@ -233,16 +233,16 @@ describe('ioredis', () => { okStatus ); assertionUtils.assertPropagation(endedSpans[0], span); - done(); } catch (error) { assert.ifError(error); + } finally { done(); } }); }); IOREDIS_CALLBACK_OPERATIONS.forEach(operation => { - it(`should create a child span for ${operation.description}`, done => { + it(`should create a child span for cb style ${operation.description}`, done => { const attributes = { ...DEFAULT_ATTRIBUTES, [AttributeNames.DB_STATEMENT]: operation.command, @@ -280,7 +280,7 @@ describe('ioredis', () => { }); IOREDIS_CALLBACK_OPERATIONS.forEach(operation => { - it(`should not create a child span for ${operation.description}`, done => { + it(`should not create a child span for cb style ${operation.description}`, done => { const span = tracer.startSpan('test span'); tracer.withSpan(span, () => { operation.method((err, _) => { From b5112315b6d792767ac274f0e17a1af9574bfcca Mon Sep 17 00:00:00 2001 From: Naseem Date: Wed, 27 Nov 2019 21:54:45 -0500 Subject: [PATCH 28/67] fix: gts fix --- packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 80cf739eade..549ba3e6940 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -204,7 +204,7 @@ describe('ioredis', () => { assertionUtils.assertPropagation(endedSpans[0], span); } catch (error) { assert.ifError(error); - } finally { + } finally { done(); } }); @@ -235,7 +235,7 @@ describe('ioredis', () => { assertionUtils.assertPropagation(endedSpans[0], span); } catch (error) { assert.ifError(error); - } finally { + } finally { done(); } }); From 626bcc222144503b8378685f077bb0823626fe54 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 30 Nov 2019 11:41:56 -0500 Subject: [PATCH 29/67] fix: callback type --- packages/opentelemetry-plugin-ioredis/src/types.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/types.ts b/packages/opentelemetry-plugin-ioredis/src/types.ts index 74563299054..f73d49a65e2 100644 --- a/packages/opentelemetry-plugin-ioredis/src/types.ts +++ b/packages/opentelemetry-plugin-ioredis/src/types.ts @@ -14,12 +14,14 @@ * limitations under the License. */ +import * as ioredisTypes from 'ioredis'; + export interface IORedisCommand { - reject: (err: Error) => void; + reject: (err: Error) => void; resolve: (result: {}) => void; promise: Promise<{}>; args: Array; - callback: Function | undefined; + callback: ioredisTypes.CallbackFunction; name: string; } From 3820fd50d19a20bc828bb97d7d28d21ecb9f8149 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 30 Nov 2019 11:42:25 -0500 Subject: [PATCH 30/67] style: move cb tests up --- .../test/ioredis.test.ts | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 549ba3e6940..f587d5e7ee6 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -149,6 +149,38 @@ describe('ioredis', () => { }); describe('Instrumenting query operations', () => { + IOREDIS_CALLBACK_OPERATIONS.forEach(operation => { + it(`should create a child span for cb style ${operation.description}`, done => { + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_STATEMENT]: operation.command, + }; + const span = tracer.startSpan('test span'); + tracer.withSpan(span, () => { + operation.method((err, _result) => { + assert.ifError(err); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual( + endedSpans[0].name, + `redis-${operation.command}` + ); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + done(); + }); + }); + }); + }); + it('should create a child span for hset promise', done => { const attributes = { ...DEFAULT_ATTRIBUTES, @@ -240,38 +272,6 @@ describe('ioredis', () => { } }); }); - - IOREDIS_CALLBACK_OPERATIONS.forEach(operation => { - it(`should create a child span for cb style ${operation.description}`, done => { - const attributes = { - ...DEFAULT_ATTRIBUTES, - [AttributeNames.DB_STATEMENT]: operation.command, - }; - const span = tracer.startSpan('test span'); - tracer.withSpan(span, () => { - operation.method((err, _result) => { - assert.ifError(err); - assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); - span.end(); - const endedSpans = memoryExporter.getFinishedSpans(); - assert.strictEqual(endedSpans.length, 2); - assert.strictEqual( - endedSpans[0].name, - `redis-${operation.command}` - ); - assertionUtils.assertSpan( - endedSpans[0], - SpanKind.CLIENT, - attributes, - [], - okStatus - ); - assertionUtils.assertPropagation(endedSpans[0], span); - done(); - }); - }); - }); - }); }); describe('Removing instrumentation', () => { From 69f9043998108c02fb88002969201c6fe3f6b4f6 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 30 Nov 2019 13:28:19 -0500 Subject: [PATCH 31/67] fix: gts --- packages/opentelemetry-plugin-ioredis/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/types.ts b/packages/opentelemetry-plugin-ioredis/src/types.ts index f73d49a65e2..00b64580900 100644 --- a/packages/opentelemetry-plugin-ioredis/src/types.ts +++ b/packages/opentelemetry-plugin-ioredis/src/types.ts @@ -17,7 +17,7 @@ import * as ioredisTypes from 'ioredis'; export interface IORedisCommand { - reject: (err: Error) => void; + reject: (err: Error) => void; resolve: (result: {}) => void; promise: Promise<{}>; args: Array; From e0d4bd02932db1579fc89ee4f444a8ac704e1de3 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 30 Nov 2019 13:30:17 -0500 Subject: [PATCH 32/67] fix: remove unused code --- .../opentelemetry-plugin-ioredis/src/utils.ts | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 7b529fe9f8e..162b3805972 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -61,25 +61,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { }); } - const originalCallback = cmd.callback; - const originalPromise = cmd.promise; - if (originalCallback) { - (cmd as IORedisCommand).callback = function callback( - this: unknown, - err: Error | null, - _reply: T - ) { - endSpan(span, err); - return originalCallback.apply(this, arguments); - }; - try { - // Span will be ended in callback - return original.apply(this, arguments); - } catch (error) { - endSpan(span, error); - throw error; - } - } else if (originalPromise) { + if (cmd.promise) { // Perform the original query const result = original.apply(this, arguments); // Bind promise to parent span and end the span From 2ef5a5c85a0837ec8559f5411a4e3d52e486a864 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 30 Nov 2019 13:33:27 -0500 Subject: [PATCH 33/67] fix: update ts-node --- packages/opentelemetry-plugin-ioredis/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/package.json b/packages/opentelemetry-plugin-ioredis/package.json index dc3e48ff630..24c6122083e 100644 --- a/packages/opentelemetry-plugin-ioredis/package.json +++ b/packages/opentelemetry-plugin-ioredis/package.json @@ -56,7 +56,7 @@ "nyc": "^14.1.1", "rimraf": "^3.0.0", "ts-mocha": "^6.0.0", - "ts-node": "^8.5.2", + "ts-node": "^8.5.4", "tslint-consistent-codestyle": "^1.16.0", "tslint-microsoft-contrib": "^6.2.0", "typescript": "3.7.2" From a5ee375c5ea756f31d9a484e1ae48de9d1c2198b Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 30 Nov 2019 13:34:02 -0500 Subject: [PATCH 34/67] fix: nested cb for cb style call tests --- .../test/ioredis.test.ts | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index f587d5e7ee6..ceaa638018d 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -159,22 +159,24 @@ describe('ioredis', () => { tracer.withSpan(span, () => { operation.method((err, _result) => { assert.ifError(err); - assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); - span.end(); - const endedSpans = memoryExporter.getFinishedSpans(); - assert.strictEqual(endedSpans.length, 2); - assert.strictEqual( - endedSpans[0].name, - `redis-${operation.command}` - ); - assertionUtils.assertSpan( - endedSpans[0], - SpanKind.CLIENT, - attributes, - [], - okStatus - ); - assertionUtils.assertPropagation(endedSpans[0], span); + (_result: string | number) => { + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual( + endedSpans[0].name, + `redis-${operation.command}` + ); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + }; done(); }); }); From 477141201ea526ed122aa6bfa66f2fdd5f8994dc Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 2 Dec 2019 15:30:42 -0500 Subject: [PATCH 35/67] fix: prevent span leaking with better if statement, add else --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 162b3805972..b470e97228b 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -61,7 +61,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { }); } - if (cmd.promise) { + if (arguments.length === 1 && typeof cmd === 'object') { // Perform the original query const result = original.apply(this, arguments); // Bind promise to parent span and end the span @@ -81,8 +81,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { }); } } - // We don't know how to trace this call, so don't start/stop a span - return original.apply(this, arguments); + else return original.apply(this, arguments); }; }; From af7470ded8d2650a119f649aa1b4dada0d989697 Mon Sep 17 00:00:00 2001 From: Naseem Date: Thu, 5 Dec 2019 00:34:16 -0500 Subject: [PATCH 36/67] fix: remove unneeded || undefined --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index b470e97228b..eed87e81e87 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -42,8 +42,7 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { if (arguments.length === 1 && typeof cmd === 'object') { const span = tracer.startSpan(cmd.name, { kind: SpanKind.CLIENT, - // Change || to ?? once widely supported in IDEs - parent: parentSpan || undefined, + parent: parentSpan, attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, [AttributeNames.DB_TYPE]: IORedisPlugin.DB_TYPE, From 540b0caee1be2fdd6f687c44341321e53572666e Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 6 Dec 2019 01:06:38 -0500 Subject: [PATCH 37/67] fix: ioredis as dev dependency --- packages/opentelemetry-plugin-ioredis/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/package.json b/packages/opentelemetry-plugin-ioredis/package.json index 24c6122083e..c5f6dbfce1b 100644 --- a/packages/opentelemetry-plugin-ioredis/package.json +++ b/packages/opentelemetry-plugin-ioredis/package.json @@ -52,6 +52,7 @@ "codecov": "^3.6.1", "cross-env": "^6.0.3", "gts": "^1.1.2", + "ioredis": "^4.14.1", "mocha": "^6.2.2", "nyc": "^14.1.1", "rimraf": "^3.0.0", @@ -62,7 +63,6 @@ "typescript": "3.7.2" }, "dependencies": { - "ioredis": "^4.14.1", "@opentelemetry/core": "^0.2.0", "@opentelemetry/types": "^0.2.0", "shimmer": "^1.2.1" From 8c0f1f431480adbbda6ab16134532a31e877e5ed Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 6 Dec 2019 23:54:23 -0500 Subject: [PATCH 38/67] fix: do not return a new promise, end span in .then handler --- .../opentelemetry-plugin-ioredis/src/utils.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index eed87e81e87..e2994503911 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -63,20 +63,14 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { if (arguments.length === 1 && typeof cmd === 'object') { // Perform the original query const result = original.apply(this, arguments); - // Bind promise to parent span and end the span return result - .then((result: unknown) => { - // Return a pass-along promise which ends the span and then goes to user's orig resolvers - return new Promise((resolve, _) => { - endSpan(span, null); - resolve(result); - }); + .then((res: unknown) => { + endSpan(span, null); + return res; }) .catch((error: Error) => { - return new Promise((_, reject) => { - endSpan(span, error); - reject(error); - }); + endSpan(span, error); + throw error; }); } } From d6bdd0e8529db39d4a0719c37f4f636140ee574e Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 6 Dec 2019 23:59:07 -0500 Subject: [PATCH 39/67] fix: if statement --- .../opentelemetry-plugin-ioredis/src/utils.ts | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index e2994503911..c26a2852d17 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -50,7 +50,6 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { }, }); - // Set attributes for not explicitly typed IORedisPluginClientTypes if (this.options) { const { host, port } = this.options; span.setAttributes({ @@ -60,21 +59,16 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { }); } - if (arguments.length === 1 && typeof cmd === 'object') { - // Perform the original query - const result = original.apply(this, arguments); - return result - .then((res: unknown) => { - endSpan(span, null); - return res; - }) - .catch((error: Error) => { - endSpan(span, error); - throw error; - }); - } - } - // We don't know how to trace this call, so don't start/stop a span - else return original.apply(this, arguments); + const result = original.apply(this, arguments); + return result + .then((res: unknown) => { + endSpan(span, null); + return res; + }) + .catch((error: Error) => { + endSpan(span, error); + throw error; + }); + } else return original.apply(this, arguments); }; }; From 22bc3edccabcfddce4ccb089ab21fdefd2dbd4cd Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 7 Dec 2019 00:07:08 -0500 Subject: [PATCH 40/67] fix: guard against args not being an array --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index c26a2852d17..2381320ed27 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -46,7 +46,9 @@ export const getTracedSendCommand = (tracer: Tracer, original: Function) => { attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, [AttributeNames.DB_TYPE]: IORedisPlugin.DB_TYPE, - [AttributeNames.DB_STATEMENT]: `${cmd.name} ${cmd.args.join(' ')}`, + [AttributeNames.DB_STATEMENT]: Array.isArray(cmd.args) + ? `${cmd.name} ${cmd.args.join(' ')}` + : cmd.name, }, }); From af0dfe7462c42100435ea7d5521a6cd45d59ee91 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 7 Dec 2019 00:12:40 -0500 Subject: [PATCH 41/67] fix: tests --- .../test/ioredis.test.ts | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index ceaa638018d..2d72aedbfc1 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -183,13 +183,13 @@ describe('ioredis', () => { }); }); - it('should create a child span for hset promise', done => { + it('should create a child span for hset promise', async () => { const attributes = { ...DEFAULT_ATTRIBUTES, [AttributeNames.DB_STATEMENT]: 'hset hash random random', }; const span = tracer.startSpan('test span'); - tracer.withSpan(span, async () => { + await tracer.withSpan(span, async () => { try { await client.hset('hash', 'random', 'random'); assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); @@ -207,19 +207,17 @@ describe('ioredis', () => { assertionUtils.assertPropagation(endedSpans[0], span); } catch (error) { assert.ifError(error); - } finally { - done(); } }); }); - it('should create a child span for get promise', done => { + it('should create a child span for get promise', async () => { const attributes = { ...DEFAULT_ATTRIBUTES, [AttributeNames.DB_STATEMENT]: 'get test', }; const span = tracer.startSpan('test span'); - tracer.withSpan(span, async () => { + await tracer.withSpan(span, async () => { try { const value = await client.get('test'); assert.strictEqual(value, 'data'); @@ -238,19 +236,17 @@ describe('ioredis', () => { assertionUtils.assertPropagation(endedSpans[0], span); } catch (error) { assert.ifError(error); - } finally { - done(); } }); }); - it('should create a child span for del', done => { + it('should create a child span for del', async () => { const attributes = { ...DEFAULT_ATTRIBUTES, [AttributeNames.DB_STATEMENT]: 'del test', }; const span = tracer.startSpan('test span'); - tracer.withSpan(span, async () => { + await tracer.withSpan(span, async () => { try { const result = await client.del('test'); assert.strictEqual(result, 1); @@ -269,8 +265,6 @@ describe('ioredis', () => { assertionUtils.assertPropagation(endedSpans[0], span); } catch (error) { assert.ifError(error); - } finally { - done(); } }); }); @@ -297,6 +291,22 @@ describe('ioredis', () => { }); }); }); + + it('should not create a child span for hset promise upon error', async () => { + const span = tracer.startSpan('test span'); + await tracer.withSpan(span, async () => { + try { + await client.hset('hash', 'random', 'random'); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 1); + assert.strictEqual(endedSpans[0].name, `test span`); + } catch (error) { + assert.ifError(error); + } + }); + }); }); }); }); From ea6b5f3221541c52eef6576363afb0584573e71d Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 9 Dec 2019 07:20:52 -0500 Subject: [PATCH 42/67] Update packages/opentelemetry-plugin-ioredis/README.md Reword sentence to read better Co-Authored-By: Daniel Dyla --- packages/opentelemetry-plugin-ioredis/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/README.md b/packages/opentelemetry-plugin-ioredis/README.md index 8ca0dac6149..34f5a89ded0 100644 --- a/packages/opentelemetry-plugin-ioredis/README.md +++ b/packages/opentelemetry-plugin-ioredis/README.md @@ -20,7 +20,7 @@ npm install --save @opentelemetry/plugin-ioredis ## Usage -OpenTelemetry IORedis Instrumentation allows the user to automatically collect trace data and export them to the backend of choice, to give observability to distributed systems when working with [ioredis](https://www.npmjs.com/package/ioredis). +OpenTelemetry IORedis Instrumentation allows the user to automatically trace service calls using the [ioredis](https://www.npmjs.com/package/ioredis) npm package. To load a specific plugin (**ioredis** in this case), specify it in the Node Tracer's configuration ```js From afe9fd3c7de11e6a5274dc1e6ea1d4a5c8181621 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 9 Dec 2019 07:26:12 -0500 Subject: [PATCH 43/67] docs: remove sentence from Usage section --- packages/opentelemetry-plugin-ioredis/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/README.md b/packages/opentelemetry-plugin-ioredis/README.md index 34f5a89ded0..78095ac4e2f 100644 --- a/packages/opentelemetry-plugin-ioredis/README.md +++ b/packages/opentelemetry-plugin-ioredis/README.md @@ -20,8 +20,6 @@ npm install --save @opentelemetry/plugin-ioredis ## Usage -OpenTelemetry IORedis Instrumentation allows the user to automatically trace service calls using the [ioredis](https://www.npmjs.com/package/ioredis) npm package. - To load a specific plugin (**ioredis** in this case), specify it in the Node Tracer's configuration ```js const { NodeTracer } = require('@opentelemetry/node'); From 159d9d193341eed29a84bc12c9202188c1acb148 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 9 Dec 2019 08:07:34 -0500 Subject: [PATCH 44/67] fix: remove unecessary check, rename functions --- .../src/ioredis.ts | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/ioredis.ts b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts index 67c7c474f35..adcf69d7bb9 100644 --- a/packages/opentelemetry-plugin-ioredis/src/ioredis.ts +++ b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts @@ -17,7 +17,7 @@ import { BasePlugin } from '@opentelemetry/core'; import * as ioredisTypes from 'ioredis'; import * as shimmer from 'shimmer'; -import { getTracedSendCommand } from './utils'; +import { traceSendCommand } from './utils'; export class IORedisPlugin extends BasePlugin { static readonly COMPONENT = 'ioredis'; @@ -28,15 +28,14 @@ export class IORedisPlugin extends BasePlugin { super(); } - protected patch() { - if (this._moduleExports) { - this._logger.debug('Patching ioredis.prototype.sendCommand'); - shimmer.wrap( - this._moduleExports.prototype, - 'sendCommand', - this._getPatchSendCommand() - ); - } + protected patch(): typeof ioredisTypes { + this._logger.debug('Patching ioredis.prototype.sendCommand'); + shimmer.wrap( + this._moduleExports.prototype, + 'sendCommand', + this._patchSendCommand() + ); + return this._moduleExports.prototype; } @@ -49,10 +48,10 @@ export class IORedisPlugin extends BasePlugin { /** * Patch send command internal to trace requests */ - private _getPatchSendCommand() { + private _patchSendCommand() { const tracer = this._tracer; - return function sendCommand(original: Function) { - return getTracedSendCommand(tracer, original); + return (original: Function) => { + return traceSendCommand(tracer, original); }; } } From 136d0b5e3659f5dc2c9761e4edce11e22087fdc3 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 9 Dec 2019 08:07:48 -0500 Subject: [PATCH 45/67] fix: rename functions --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 2381320ed27..b105bba353f 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -32,8 +32,8 @@ const endSpan = (span: Span, err: Error | null) => { span.end(); }; -export const getTracedSendCommand = (tracer: Tracer, original: Function) => { - return function sendCommandTrace( +export const traceSendCommand = (tracer: Tracer, original: Function) => { + return function( this: ioredisTypes.Redis & IORedisPluginClientTypes, cmd?: IORedisCommand ) { From 99343277ec5bfdcce3b7c301819123ca83523fcb Mon Sep 17 00:00:00 2001 From: Naseem Date: Thu, 12 Dec 2019 08:34:11 -0500 Subject: [PATCH 46/67] fix: port type is number --- packages/opentelemetry-plugin-ioredis/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/types.ts b/packages/opentelemetry-plugin-ioredis/src/types.ts index 00b64580900..347425a1e77 100644 --- a/packages/opentelemetry-plugin-ioredis/src/types.ts +++ b/packages/opentelemetry-plugin-ioredis/src/types.ts @@ -28,6 +28,6 @@ export interface IORedisCommand { export interface IORedisPluginClientTypes { options?: { host: string; - port: string; + port: number; }; } From 949c9d49b4c4bf5f0288eb770974417eec54bb0d Mon Sep 17 00:00:00 2001 From: Naseem Date: Thu, 12 Dec 2019 08:36:35 -0500 Subject: [PATCH 47/67] docs: add link to available options --- packages/opentelemetry-plugin-ioredis/src/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opentelemetry-plugin-ioredis/src/types.ts b/packages/opentelemetry-plugin-ioredis/src/types.ts index 347425a1e77..751f6b2c0be 100644 --- a/packages/opentelemetry-plugin-ioredis/src/types.ts +++ b/packages/opentelemetry-plugin-ioredis/src/types.ts @@ -26,6 +26,7 @@ export interface IORedisCommand { } export interface IORedisPluginClientTypes { + // https://github.com/luin/ioredis/blob/master/API.md options?: { host: string; port: number; From 0fc75eabee88e7156473a54a699b382550451135 Mon Sep 17 00:00:00 2001 From: Naseem Date: Thu, 12 Dec 2019 10:11:12 -0500 Subject: [PATCH 48/67] fix: always convert string port to number --- packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 2d72aedbfc1..5dc3a5ef07d 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -32,7 +32,7 @@ const memoryExporter = new InMemorySpanExporter(); const CONFIG = { host: process.env.OPENTELEMETRY_REDIS_HOST || 'localhost', - port: process.env.OPENTELEMETRY_REDIS_PORT || 63790, + port: parseInt(process.env.OPENTELEMETRY_REDIS_PORT || '63790', 10), }; const URL = `redis://${CONFIG.host}:${CONFIG.port}`; From 43b06a366b5993d7a09ef7169c95a0f567976c16 Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 13 Dec 2019 15:20:54 -0500 Subject: [PATCH 49/67] fix: improve inherit options type from ioredis types --- packages/opentelemetry-plugin-ioredis/src/types.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/types.ts b/packages/opentelemetry-plugin-ioredis/src/types.ts index 751f6b2c0be..ff2391fd31e 100644 --- a/packages/opentelemetry-plugin-ioredis/src/types.ts +++ b/packages/opentelemetry-plugin-ioredis/src/types.ts @@ -27,8 +27,7 @@ export interface IORedisCommand { export interface IORedisPluginClientTypes { // https://github.com/luin/ioredis/blob/master/API.md - options?: { - host: string; - port: number; - }; + host?: string, + port?: number, + options?: ioredisTypes.RedisOptions } From b0c53114a759dda6d8d1bf7ed7bf3fbbbdf1ca83 Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 13 Dec 2019 17:30:16 -0500 Subject: [PATCH 50/67] fix: options always exists --- .../opentelemetry-plugin-ioredis/src/utils.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index b105bba353f..4a40381f6a9 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -52,14 +52,13 @@ export const traceSendCommand = (tracer: Tracer, original: Function) => { }, }); - if (this.options) { - const { host, port } = this.options; - span.setAttributes({ - [AttributeNames.PEER_HOSTNAME]: host, - [AttributeNames.PEER_PORT]: port, - [AttributeNames.PEER_ADDRESS]: `redis://${host}:${port}`, - }); - } + const { host, port } = this.options; + + span.setAttributes({ + [AttributeNames.PEER_HOSTNAME]: host, + [AttributeNames.PEER_PORT]: port, + [AttributeNames.PEER_ADDRESS]: `redis://${host}:${port}`, + }); const result = original.apply(this, arguments); return result From c4a6c078ebe3a3f86eb043ec1a0774a51f29bd39 Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 13 Dec 2019 17:30:46 -0500 Subject: [PATCH 51/67] fix: options is always there and all we need --- packages/opentelemetry-plugin-ioredis/src/types.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/types.ts b/packages/opentelemetry-plugin-ioredis/src/types.ts index ff2391fd31e..5c8a618fa0b 100644 --- a/packages/opentelemetry-plugin-ioredis/src/types.ts +++ b/packages/opentelemetry-plugin-ioredis/src/types.ts @@ -27,7 +27,5 @@ export interface IORedisCommand { export interface IORedisPluginClientTypes { // https://github.com/luin/ioredis/blob/master/API.md - host?: string, - port?: number, - options?: ioredisTypes.RedisOptions + options: ioredisTypes.RedisOptions; } From 0728a13b6f17afe99ffd45e6c97c3476beef363f Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 14 Dec 2019 16:59:16 -0500 Subject: [PATCH 52/67] test: streamify scanning --- .../test/ioredis.test.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 5dc3a5ef07d..e4b5055c6dc 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -211,6 +211,45 @@ describe('ioredis', () => { }); }); + it('should create a child span for streamify scanning', async () => { + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_STATEMENT]: 'scan 0', + }; + const span = tracer.startSpan('test span'); + await tracer.withSpan(span, async () => { + const stream = client.scanStream(); + stream + .on('data', resultKeys => { + // `resultKeys` is an array of strings representing key names. + // Note that resultKeys may contain 0 keys, and that it will sometimes + // contain duplicates due to SCAN's implementation in Redis. + for (var i = 0; i < resultKeys.length; i++) { + console.log(resultKeys[i]); + } + }) + .on('end', () => { + console.log('all keys have been visited'); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual(endedSpans[0].name, `scan`); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + }) + .on('error', error => { + assert.ifError(error); + }); + }); + }); + it('should create a child span for get promise', async () => { const attributes = { ...DEFAULT_ATTRIBUTES, From 09c5386bfecdac4f344bca50f2d2487eb1e7ff02 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 14 Dec 2019 19:04:23 -0500 Subject: [PATCH 53/67] test: lua --- .../test/ioredis.test.ts | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index e4b5055c6dc..65038b1f39c 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -211,13 +211,13 @@ describe('ioredis', () => { }); }); - it('should create a child span for streamify scanning', async () => { + it('should create a child span for streamify scanning', done => { const attributes = { ...DEFAULT_ATTRIBUTES, [AttributeNames.DB_STATEMENT]: 'scan 0', }; const span = tracer.startSpan('test span'); - await tracer.withSpan(span, async () => { + tracer.withSpan(span, () => { const stream = client.scanStream(); stream .on('data', resultKeys => { @@ -243,13 +243,53 @@ describe('ioredis', () => { okStatus ); assertionUtils.assertPropagation(endedSpans[0], span); + done(); }) - .on('error', error => { - assert.ifError(error); + .on('error', err => { + done(err); }); }); }); + it(`should create a child span for lua`, done => { + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_STATEMENT]: 'eval return {KEYS[1],ARGV[1]} 1 test', + }; + + const span = tracer.startSpan('test span'); + tracer.withSpan(span, () => { + // This will define a command echo: + client.defineCommand('echo', { + numberOfKeys: 1, + lua: 'return {KEYS[1],ARGV[1]}', + }); + + // Now `echo` can be used just like any other ordinary command, + // and ioredis will try to use `EVALSHA` internally when possible for better performance. + client.echo('test', (err, result) => { + assert.ifError(err); + + assert.strictEqual(memoryExporter.getFinishedSpans().length, 2); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 3); + assert.strictEqual(endedSpans[2].name, 'test span'); + assert.strictEqual(endedSpans[1].name, 'eval'); + assert.strictEqual(endedSpans[0].name, 'evalsha'); + assertionUtils.assertSpan( + endedSpans[1], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + done(); + }); + }); + }); + it('should create a child span for get promise', async () => { const attributes = { ...DEFAULT_ATTRIBUTES, From 664cab597671d19e8c2662335a18cc0066c7254d Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 14 Dec 2019 19:53:30 -0500 Subject: [PATCH 54/67] test: cb tests were flawed --- .../test/ioredis.test.ts | 68 +++++++++++++------ 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 65038b1f39c..81e027a06e2 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -159,24 +159,22 @@ describe('ioredis', () => { tracer.withSpan(span, () => { operation.method((err, _result) => { assert.ifError(err); - (_result: string | number) => { - assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); - span.end(); - const endedSpans = memoryExporter.getFinishedSpans(); - assert.strictEqual(endedSpans.length, 2); - assert.strictEqual( - endedSpans[0].name, - `redis-${operation.command}` - ); - assertionUtils.assertSpan( - endedSpans[0], - SpanKind.CLIENT, - attributes, - [], - okStatus - ); - assertionUtils.assertPropagation(endedSpans[0], span); - }; + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual( + endedSpans[0].name, + `redis-${operation.command}` + ); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); done(); }); }); @@ -264,7 +262,6 @@ describe('ioredis', () => { numberOfKeys: 1, lua: 'return {KEYS[1],ARGV[1]}', }); - // Now `echo` can be used just like any other ordinary command, // and ioredis will try to use `EVALSHA` internally when possible for better performance. client.echo('test', (err, result) => { @@ -290,6 +287,39 @@ describe('ioredis', () => { }); }); + it(`should create a child span for multi/transaction`, done => { + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_STATEMENT]: 'ok', + }; + + const span = tracer.startSpan('test span'); + tracer.withSpan(span, () => { + client + .multi() + .set('foo', 'bar') + .get('foo') + .exec((err, _results) => { + assert.ifError(err); + + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual(endedSpans[0].name, 'okokok'); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + done(); + }); + }); + }); + it('should create a child span for get promise', async () => { const attributes = { ...DEFAULT_ATTRIBUTES, From 79fdef3564732e02b23abcc84f7f5f1be95ab48a Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 14 Dec 2019 23:23:34 -0500 Subject: [PATCH 55/67] fix: trace cb invocations properly --- .../opentelemetry-plugin-ioredis/src/utils.ts | 23 ++++++++++++++++++- .../test/ioredis.test.ts | 22 +++++++++--------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 4a40381f6a9..7ce3a1be877 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -20,7 +20,7 @@ import { IORedisPluginClientTypes, IORedisCommand } from './types'; import { IORedisPlugin } from './ioredis'; import { AttributeNames } from './enums'; -const endSpan = (span: Span, err: Error | null) => { +const endSpan = (span: Span, err: NodeJS.ErrnoException | null | undefined) => { if (err) { span.setStatus({ code: CanonicalCode.UNKNOWN, @@ -60,6 +60,27 @@ export const traceSendCommand = (tracer: Tracer, original: Function) => { [AttributeNames.PEER_ADDRESS]: `redis://${host}:${port}`, }); + // Callback style invocations + const originalCallback = arguments[0].callback; + if (originalCallback) { + (arguments[0] as IORedisCommand).callback = function callback( + this: unknown, + err: NodeJS.ErrnoException | null | undefined, + _result: T + ) { + endSpan(span, err); + return originalCallback.apply(this, arguments); + }; + try { + // Span will be ended in callback + return original.apply(this, arguments); + } catch (err) { + endSpan(span, err); + throw err; + } + } + + // Promise style invocations const result = original.apply(this, arguments); return result .then((res: unknown) => { diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 81e027a06e2..66eb0f22e55 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -109,18 +109,21 @@ describe('ioredis', () => { const IOREDIS_CALLBACK_OPERATIONS: Array<{ description: string; - command: string; + name: string; + args: Array; method: (cb: ioredisTypes.CallbackFunction) => unknown; }> = [ { description: 'insert', - command: 'hset', + name: 'hset', + args: ['hash', 'testField', 'testValue'], method: (cb: ioredisTypes.CallbackFunction) => client.hset('hash', 'testField', 'testValue', cb), }, { description: 'get', - command: 'get', + name: 'get', + args: ['test'], method: (cb: ioredisTypes.CallbackFunction) => client.get('test', cb), }, @@ -149,24 +152,21 @@ describe('ioredis', () => { }); describe('Instrumenting query operations', () => { - IOREDIS_CALLBACK_OPERATIONS.forEach(operation => { - it(`should create a child span for cb style ${operation.description}`, done => { + IOREDIS_CALLBACK_OPERATIONS.forEach(command => { + it(`should create a child span for cb style ${command.description}`, done => { const attributes = { ...DEFAULT_ATTRIBUTES, - [AttributeNames.DB_STATEMENT]: operation.command, + [AttributeNames.DB_STATEMENT]: `${command.name} ${command.args.join(' ')}`, }; const span = tracer.startSpan('test span'); tracer.withSpan(span, () => { - operation.method((err, _result) => { + command.method((err, _result) => { assert.ifError(err); assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); span.end(); const endedSpans = memoryExporter.getFinishedSpans(); assert.strictEqual(endedSpans.length, 2); - assert.strictEqual( - endedSpans[0].name, - `redis-${operation.command}` - ); + assert.strictEqual(endedSpans[0].name, command.name); assertionUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, From 537f193bc80173b6eae9a04f93398a47f373c625 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 14 Dec 2019 23:58:04 -0500 Subject: [PATCH 56/67] refactor: same code handles cb and promise style invocations --- .../opentelemetry-plugin-ioredis/src/utils.ts | 37 ++++--------------- .../test/ioredis.test.ts | 4 +- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 7ce3a1be877..a02a29d4fba 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -60,37 +60,14 @@ export const traceSendCommand = (tracer: Tracer, original: Function) => { [AttributeNames.PEER_ADDRESS]: `redis://${host}:${port}`, }); - // Callback style invocations - const originalCallback = arguments[0].callback; - if (originalCallback) { - (arguments[0] as IORedisCommand).callback = function callback( - this: unknown, - err: NodeJS.ErrnoException | null | undefined, - _result: T - ) { - endSpan(span, err); - return originalCallback.apply(this, arguments); - }; - try { - // Span will be ended in callback - return original.apply(this, arguments); - } catch (err) { - endSpan(span, err); - throw err; - } + try { + const result = original.apply(this, arguments); + endSpan(span, null); + return result; + } catch (error) { + endSpan(span, error); + throw error; } - - // Promise style invocations - const result = original.apply(this, arguments); - return result - .then((res: unknown) => { - endSpan(span, null); - return res; - }) - .catch((error: Error) => { - endSpan(span, error); - throw error; - }); } else return original.apply(this, arguments); }; }; diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 66eb0f22e55..e0da229c530 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -156,7 +156,9 @@ describe('ioredis', () => { it(`should create a child span for cb style ${command.description}`, done => { const attributes = { ...DEFAULT_ATTRIBUTES, - [AttributeNames.DB_STATEMENT]: `${command.name} ${command.args.join(' ')}`, + [AttributeNames.DB_STATEMENT]: `${command.name} ${command.args.join( + ' ' + )}`, }; const span = tracer.startSpan('test span'); tracer.withSpan(span, () => { From 025965479034449da66d8af75c95ac73835f2b43 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sun, 15 Dec 2019 00:04:17 -0500 Subject: [PATCH 57/67] fix: update packages --- packages/opentelemetry-plugin-ioredis/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/package.json b/packages/opentelemetry-plugin-ioredis/package.json index c5f6dbfce1b..0c7e88a2e40 100644 --- a/packages/opentelemetry-plugin-ioredis/package.json +++ b/packages/opentelemetry-plugin-ioredis/package.json @@ -43,11 +43,11 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/node": "^0.2.0", - "@opentelemetry/tracing": "^0.2.0", - "@types/ioredis": "^4.0.20", + "@opentelemetry/node": "^0.3.0", + "@opentelemetry/tracing": "^0.3.0", + "@types/ioredis": "^4.14.2", "@types/mocha": "^5.2.7", - "@types/node": "^12.12.14", + "@types/node": "^12.12.17", "@types/shimmer": "^1.0.1", "codecov": "^3.6.1", "cross-env": "^6.0.3", @@ -60,11 +60,11 @@ "ts-node": "^8.5.4", "tslint-consistent-codestyle": "^1.16.0", "tslint-microsoft-contrib": "^6.2.0", - "typescript": "3.7.2" + "typescript": "3.7.3" }, "dependencies": { - "@opentelemetry/core": "^0.2.0", - "@opentelemetry/types": "^0.2.0", + "@opentelemetry/core": "^0.3.0", + "@opentelemetry/types": "^0.3.0", "shimmer": "^1.2.1" } } From 842a7807663d6ff2f693a6005cac01b8a6d4ae99 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sun, 15 Dec 2019 01:19:26 -0500 Subject: [PATCH 58/67] fix: db statement format --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index a02a29d4fba..5630ccfc950 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -46,9 +46,10 @@ export const traceSendCommand = (tracer: Tracer, original: Function) => { attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, [AttributeNames.DB_TYPE]: IORedisPlugin.DB_TYPE, - [AttributeNames.DB_STATEMENT]: Array.isArray(cmd.args) - ? `${cmd.name} ${cmd.args.join(' ')}` - : cmd.name, + [AttributeNames.DB_STATEMENT]: + Array.isArray(cmd.args) && cmd.args.length + ? `${cmd.name} ${cmd.args.join(' ')}` + : cmd.name, }, }); From 151b6797cfdca947c990e8568cdd360157efd578 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sun, 15 Dec 2019 18:46:56 -0500 Subject: [PATCH 59/67] test: add pipeline test --- .../test/ioredis.test.ts | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index e0da229c530..404e0d6f993 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -292,7 +292,7 @@ describe('ioredis', () => { it(`should create a child span for multi/transaction`, done => { const attributes = { ...DEFAULT_ATTRIBUTES, - [AttributeNames.DB_STATEMENT]: 'ok', + [AttributeNames.DB_STATEMENT]: 'multi', }; const span = tracer.startSpan('test span'); @@ -304,11 +304,14 @@ describe('ioredis', () => { .exec((err, _results) => { assert.ifError(err); - assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 4); span.end(); const endedSpans = memoryExporter.getFinishedSpans(); - assert.strictEqual(endedSpans.length, 2); - assert.strictEqual(endedSpans[0].name, 'okokok'); + assert.strictEqual(endedSpans.length, 5); + assert.strictEqual(endedSpans[0].name, 'multi'); + assert.strictEqual(endedSpans[1].name, 'set'); + assert.strictEqual(endedSpans[2].name, 'get'); + assert.strictEqual(endedSpans[3].name, 'exec'); assertionUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, @@ -322,6 +325,40 @@ describe('ioredis', () => { }); }); + it(`should create a child span for pipeline`, done => { + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_STATEMENT]: 'set foo bar', + }; + + const span = tracer.startSpan('test span'); + tracer.withSpan(span, () => { + const pipeline = client.pipeline(); + pipeline.set("foo", "bar"); + pipeline.del("cc"); + pipeline.exec((err, results) => { + assert.ifError(err); + + assert.strictEqual(memoryExporter.getFinishedSpans().length, 2); + span.end(); + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 3); + assert.strictEqual(endedSpans[0].name, 'set'); + assert.strictEqual(endedSpans[1].name, 'del'); + assert.strictEqual(endedSpans[2].name, 'test span'); + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + done(); + }); + }); + }); + it('should create a child span for get promise', async () => { const attributes = { ...DEFAULT_ATTRIBUTES, From 2d76a4f974f217f098d54ca57272c775c1ec90c4 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sun, 15 Dec 2019 18:47:28 -0500 Subject: [PATCH 60/67] fix: allow arguments to be greater than 1 --- packages/opentelemetry-plugin-ioredis/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 5630ccfc950..c10a1589a2a 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -39,7 +39,7 @@ export const traceSendCommand = (tracer: Tracer, original: Function) => { ) { const parentSpan = tracer.getCurrentSpan(); - if (arguments.length === 1 && typeof cmd === 'object') { + if (arguments.length >= 1 && typeof cmd === 'object') { const span = tracer.startSpan(cmd.name, { kind: SpanKind.CLIENT, parent: parentSpan, From 2b163051973f40122b5d9280e33c2342866e0d6a Mon Sep 17 00:00:00 2001 From: Naseem Date: Sun, 15 Dec 2019 19:00:14 -0500 Subject: [PATCH 61/67] fix: gts --- packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 404e0d6f993..4f33e922a27 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -334,8 +334,8 @@ describe('ioredis', () => { const span = tracer.startSpan('test span'); tracer.withSpan(span, () => { const pipeline = client.pipeline(); - pipeline.set("foo", "bar"); - pipeline.del("cc"); + pipeline.set('foo', 'bar'); + pipeline.del('cc'); pipeline.exec((err, results) => { assert.ifError(err); @@ -356,8 +356,8 @@ describe('ioredis', () => { assertionUtils.assertPropagation(endedSpans[0], span); done(); }); + }); }); - }); it('should create a child span for get promise', async () => { const attributes = { From 2c9217c3caeef47ce94599dd03f57daf94c787b8 Mon Sep 17 00:00:00 2001 From: Naseem Date: Wed, 18 Dec 2019 21:40:41 -0500 Subject: [PATCH 62/67] test: trace client connection/disconnection --- .../test/ioredis.test.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 4f33e922a27..9cbcdf99d18 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -87,9 +87,32 @@ describe('ioredis', () => { it('should propagate the current span to event handlers', done => { const span = tracer.startSpan('test span'); let client: ioredisTypes.Redis; + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_STATEMENT]: 'info', + }; const readyHandler = () => { + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(tracer.getCurrentSpan(), span); + assert.strictEqual(endedSpans.length, 1); + assert.strictEqual(endedSpans[0].name, `info`); + assertionUtils.assertPropagation(endedSpans[0], span); + + assertionUtils.assertSpan( + endedSpans[0], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + span.end(); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual(endedSpans[1].name, `test span`); + client.quit(done); + assert.strictEqual(endedSpans.length, 3); + assert.strictEqual(endedSpans[2].name, `quit`); }; const errorHandler = (err: Error) => { assert.ifError(err); From 3b47e26c3a8d97dd9d0383a15ba91e710bda3413 Mon Sep 17 00:00:00 2001 From: Naseem Date: Wed, 18 Dec 2019 23:34:05 -0500 Subject: [PATCH 63/67] feat: trace connection --- .../src/ioredis.ts | 17 ++++++++++- .../opentelemetry-plugin-ioredis/src/utils.ts | 30 +++++++++++++++++++ .../test/ioredis.test.ts | 15 +++++----- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/src/ioredis.ts b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts index adcf69d7bb9..4fedf82c537 100644 --- a/packages/opentelemetry-plugin-ioredis/src/ioredis.ts +++ b/packages/opentelemetry-plugin-ioredis/src/ioredis.ts @@ -17,7 +17,7 @@ import { BasePlugin } from '@opentelemetry/core'; import * as ioredisTypes from 'ioredis'; import * as shimmer from 'shimmer'; -import { traceSendCommand } from './utils'; +import { traceConnection, traceSendCommand } from './utils'; export class IORedisPlugin extends BasePlugin { static readonly COMPONENT = 'ioredis'; @@ -36,12 +36,20 @@ export class IORedisPlugin extends BasePlugin { this._patchSendCommand() ); + this._logger.debug('patching ioredis.prototype.connect'); + shimmer.wrap( + this._moduleExports.prototype, + 'connect', + this._patchConnection() + ); + return this._moduleExports.prototype; } protected unpatch(): void { if (this._moduleExports) { shimmer.unwrap(this._moduleExports.prototype, 'sendCommand'); + shimmer.unwrap(this._moduleExports.prototype, 'connect'); } } @@ -54,6 +62,13 @@ export class IORedisPlugin extends BasePlugin { return traceSendCommand(tracer, original); }; } + + private _patchConnection() { + const tracer = this._tracer; + return (original: Function) => { + return traceConnection(tracer, original); + }; + } } export const plugin = new IORedisPlugin(IORedisPlugin.COMPONENT); diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index c10a1589a2a..8dbae4478b5 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -32,6 +32,36 @@ const endSpan = (span: Span, err: NodeJS.ErrnoException | null | undefined) => { span.end(); }; +export const traceConnection = (tracer: Tracer, original: Function) => { + return function(this: ioredisTypes.Redis & IORedisPluginClientTypes) { + const parentSpan = tracer.getCurrentSpan(); + const span = tracer.startSpan('connect', { + kind: SpanKind.CLIENT, + parent: parentSpan, + attributes: { + [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, + [AttributeNames.DB_TYPE]: IORedisPlugin.DB_TYPE, + [AttributeNames.DB_STATEMENT]: 'connect', + }, + }); + const { host, port } = this.options; + + span.setAttributes({ + [AttributeNames.PEER_HOSTNAME]: host, + [AttributeNames.PEER_PORT]: port, + [AttributeNames.PEER_ADDRESS]: `redis://${host}:${port}`, + }); + try { + const client = original.apply(this, arguments); + endSpan(span, null); + return client; + } catch (error) { + endSpan(span, error); + throw error; + } + }; +}; + export const traceSendCommand = (tracer: Tracer, original: Function) => { return function( this: ioredisTypes.Redis & IORedisPluginClientTypes, diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 9cbcdf99d18..5ba9aebac74 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -89,14 +89,15 @@ describe('ioredis', () => { let client: ioredisTypes.Redis; const attributes = { ...DEFAULT_ATTRIBUTES, - [AttributeNames.DB_STATEMENT]: 'info', + [AttributeNames.DB_STATEMENT]: 'connect', }; const readyHandler = () => { const endedSpans = memoryExporter.getFinishedSpans(); assert.strictEqual(tracer.getCurrentSpan(), span); - assert.strictEqual(endedSpans.length, 1); - assert.strictEqual(endedSpans[0].name, `info`); + assert.strictEqual(endedSpans.length, 2); + assert.strictEqual(endedSpans[0].name, `connect`); + assert.strictEqual(endedSpans[1].name, `info`); assertionUtils.assertPropagation(endedSpans[0], span); assertionUtils.assertSpan( @@ -107,12 +108,12 @@ describe('ioredis', () => { okStatus ); span.end(); - assert.strictEqual(endedSpans.length, 2); - assert.strictEqual(endedSpans[1].name, `test span`); + assert.strictEqual(endedSpans.length, 3); + assert.strictEqual(endedSpans[2].name, `test span`); client.quit(done); - assert.strictEqual(endedSpans.length, 3); - assert.strictEqual(endedSpans[2].name, `quit`); + assert.strictEqual(endedSpans.length, 4); + assert.strictEqual(endedSpans[3].name, `quit`); }; const errorHandler = (err: Error) => { assert.ifError(err); From 7b3a94e58baa5ef7a3025fde3e1cc29db4c1e422 Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 20 Dec 2019 19:37:39 -0500 Subject: [PATCH 64/67] test: pubsub --- .../test/ioredis.test.ts | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 5ba9aebac74..0277ac1e2ba 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -275,6 +275,58 @@ describe('ioredis', () => { }); }); + it('should create a child span for pubsub', async () => { + const span = tracer.startSpan('test span'); + await tracer.withSpan(span, async () => { + try { + const pub = new ioredis(URL); + const sub = new ioredis(URL); + await sub.subscribe('news', 'music'); + await pub.publish('news', 'Hello world!'); + await pub.publish('music', 'Hello again!'); + await sub.unsubscribe('news', 'music'); + await sub.disconnect(); // No span + await pub.disconnect(); // No span + const endedSpans = memoryExporter.getFinishedSpans(); + assert.strictEqual(endedSpans.length, 9); + span.end(); + assert.strictEqual(endedSpans.length, 10); + const spanNames = [ + 'connect', + 'connect', + 'subscribe', + 'info', + 'info', + 'subscribe', + 'publish', + 'publish', + 'unsubscribe', + 'test span', + ]; + let i = 0; + while (i < 10) { + assert.strictEqual(endedSpans[i].name, spanNames[i]); + i++; + } + + const attributes = { + ...DEFAULT_ATTRIBUTES, + [AttributeNames.DB_STATEMENT]: 'subscribe news music', + }; + assertionUtils.assertSpan( + endedSpans[5], + SpanKind.CLIENT, + attributes, + [], + okStatus + ); + assertionUtils.assertPropagation(endedSpans[0], span); + } catch (error) { + assert.ifError(error); + } + }); + }); + it(`should create a child span for lua`, done => { const attributes = { ...DEFAULT_ATTRIBUTES, From d7af1621b4fb3521d23000aded6bdbbe2797018d Mon Sep 17 00:00:00 2001 From: Naseem Date: Fri, 20 Dec 2019 19:50:01 -0500 Subject: [PATCH 65/67] fix: quit instead of disconnect --- .../test/ioredis.test.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 0277ac1e2ba..8915f9ee9df 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -285,12 +285,12 @@ describe('ioredis', () => { await pub.publish('news', 'Hello world!'); await pub.publish('music', 'Hello again!'); await sub.unsubscribe('news', 'music'); - await sub.disconnect(); // No span - await pub.disconnect(); // No span + await sub.quit(); + await pub.quit(); const endedSpans = memoryExporter.getFinishedSpans(); - assert.strictEqual(endedSpans.length, 9); + assert.strictEqual(endedSpans.length, 11); span.end(); - assert.strictEqual(endedSpans.length, 10); + assert.strictEqual(endedSpans.length, 12); const spanNames = [ 'connect', 'connect', @@ -301,10 +301,12 @@ describe('ioredis', () => { 'publish', 'publish', 'unsubscribe', + 'quit', + 'quit', 'test span', ]; let i = 0; - while (i < 10) { + while (i < 12) { assert.strictEqual(endedSpans[i].name, spanNames[i]); i++; } From 9cb26df08f675d30a3010776f685e40f2ccb0949 Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 23 Dec 2019 21:16:54 -0500 Subject: [PATCH 66/67] fix: update packages --- .../opentelemetry-plugin-ioredis/package.json | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/package.json b/packages/opentelemetry-plugin-ioredis/package.json index 0c7e88a2e40..f95400abf50 100644 --- a/packages/opentelemetry-plugin-ioredis/package.json +++ b/packages/opentelemetry-plugin-ioredis/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/plugin-ioredis", - "version": "0.2.0", + "version": "0.3.1", "description": "OpenTelemetry ioredis automatic instrumentation package.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -43,28 +43,29 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/node": "^0.3.0", - "@opentelemetry/tracing": "^0.3.0", - "@types/ioredis": "^4.14.2", + "@opentelemetry/node": "^0.3.1", + "@opentelemetry/test-utils": "^0.3.1", + "@opentelemetry/tracing": "^0.3.1", + "@types/ioredis": "^4.14.3", "@types/mocha": "^5.2.7", - "@types/node": "^12.12.17", + "@types/node": "^13.1.0", "@types/shimmer": "^1.0.1", "codecov": "^3.6.1", "cross-env": "^6.0.3", "gts": "^1.1.2", "ioredis": "^4.14.1", "mocha": "^6.2.2", - "nyc": "^14.1.1", + "nyc": "^15.0.0", "rimraf": "^3.0.0", "ts-mocha": "^6.0.0", "ts-node": "^8.5.4", "tslint-consistent-codestyle": "^1.16.0", "tslint-microsoft-contrib": "^6.2.0", - "typescript": "3.7.3" + "typescript": "3.7.4" }, "dependencies": { - "@opentelemetry/core": "^0.3.0", - "@opentelemetry/types": "^0.3.0", + "@opentelemetry/core": "^0.3.1", + "@opentelemetry/types": "^0.3.1", "shimmer": "^1.2.1" } } From 23462daaa391661fa4ed85c247541883bf4c16ac Mon Sep 17 00:00:00 2001 From: Naseem Date: Mon, 23 Dec 2019 21:17:09 -0500 Subject: [PATCH 67/67] feat: use test-utils package --- .../test/assertionUtils.ts | 76 ------------------- .../test/ioredis.test.ts | 47 ++++++------ .../test/testUtils.ts | 54 ------------- 3 files changed, 23 insertions(+), 154 deletions(-) delete mode 100644 packages/opentelemetry-plugin-ioredis/test/assertionUtils.ts delete mode 100644 packages/opentelemetry-plugin-ioredis/test/testUtils.ts diff --git a/packages/opentelemetry-plugin-ioredis/test/assertionUtils.ts b/packages/opentelemetry-plugin-ioredis/test/assertionUtils.ts deleted file mode 100644 index fdb517c7a09..00000000000 --- a/packages/opentelemetry-plugin-ioredis/test/assertionUtils.ts +++ /dev/null @@ -1,76 +0,0 @@ -/*! - * Copyright 2019, OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - SpanKind, - Attributes, - Event, - Span, - Status, -} from '@opentelemetry/types'; -import * as assert from 'assert'; -import { ReadableSpan } from '@opentelemetry/tracing'; -import { - hrTimeToMilliseconds, - hrTimeToMicroseconds, -} from '@opentelemetry/core'; - -export const assertSpan = ( - span: ReadableSpan, - kind: SpanKind, - attributes: Attributes, - events: Event[], - status: Status -) => { - assert.strictEqual(span.spanContext.traceId.length, 32); - assert.strictEqual(span.spanContext.spanId.length, 16); - assert.strictEqual(span.kind, kind); - - assert.ok(span.endTime); - assert.strictEqual(span.links.length, 0); - - assert.ok( - hrTimeToMicroseconds(span.startTime) < hrTimeToMicroseconds(span.endTime) - ); - assert.ok(hrTimeToMilliseconds(span.endTime) > 0); - - // attributes - assert.deepStrictEqual(span.attributes, attributes); - - // events - assert.deepStrictEqual(span.events, events); - - assert.strictEqual(span.status.code, status.code); - if (status.message) { - assert.strictEqual(span.status.message, status.message); - } -}; - -// Check if childSpan was propagated from parentSpan -export const assertPropagation = ( - childSpan: ReadableSpan, - parentSpan: Span -) => { - const targetSpanContext = childSpan.spanContext; - const sourceSpanContext = parentSpan.context(); - assert.strictEqual(targetSpanContext.traceId, sourceSpanContext.traceId); - assert.strictEqual(childSpan.parentSpanId, sourceSpanContext.spanId); - assert.strictEqual( - targetSpanContext.traceFlags, - sourceSpanContext.traceFlags - ); - assert.notStrictEqual(targetSpanContext.spanId, sourceSpanContext.spanId); -}; diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 8915f9ee9df..ba68af4dd47 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -23,8 +23,7 @@ import { NodeTracer } from '@opentelemetry/node'; import { plugin, IORedisPlugin } from '../src'; import * as ioredisTypes from 'ioredis'; import { NoopLogger } from '@opentelemetry/core'; -import * as dockerUtils from './testUtils'; -import * as assertionUtils from './assertionUtils'; +import * as testUtils from '@opentelemetry/test-utils'; import { SpanKind, Status, CanonicalCode } from '@opentelemetry/types'; import { AttributeNames } from '../src/enums'; @@ -65,7 +64,7 @@ describe('ioredis', () => { } if (shouldTestLocal) { - dockerUtils.startDocker(); + testUtils.startDocker('redis'); } ioredis = require('ioredis'); @@ -75,7 +74,7 @@ describe('ioredis', () => { after(() => { if (shouldTestLocal) { - dockerUtils.cleanUpDocker(); + testUtils.cleanUpDocker('redis'); } }); @@ -98,9 +97,9 @@ describe('ioredis', () => { assert.strictEqual(endedSpans.length, 2); assert.strictEqual(endedSpans[0].name, `connect`); assert.strictEqual(endedSpans[1].name, `info`); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, attributes, @@ -193,14 +192,14 @@ describe('ioredis', () => { const endedSpans = memoryExporter.getFinishedSpans(); assert.strictEqual(endedSpans.length, 2); assert.strictEqual(endedSpans[0].name, command.name); - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, attributes, [], okStatus ); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); done(); }); }); @@ -221,14 +220,14 @@ describe('ioredis', () => { const endedSpans = memoryExporter.getFinishedSpans(); assert.strictEqual(endedSpans.length, 2); assert.strictEqual(endedSpans[0].name, `hset`); - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, attributes, [], okStatus ); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); } catch (error) { assert.ifError(error); } @@ -259,14 +258,14 @@ describe('ioredis', () => { const endedSpans = memoryExporter.getFinishedSpans(); assert.strictEqual(endedSpans.length, 2); assert.strictEqual(endedSpans[0].name, `scan`); - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, attributes, [], okStatus ); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); done(); }) .on('error', err => { @@ -315,14 +314,14 @@ describe('ioredis', () => { ...DEFAULT_ATTRIBUTES, [AttributeNames.DB_STATEMENT]: 'subscribe news music', }; - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[5], SpanKind.CLIENT, attributes, [], okStatus ); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); } catch (error) { assert.ifError(error); } @@ -354,14 +353,14 @@ describe('ioredis', () => { assert.strictEqual(endedSpans[2].name, 'test span'); assert.strictEqual(endedSpans[1].name, 'eval'); assert.strictEqual(endedSpans[0].name, 'evalsha'); - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[1], SpanKind.CLIENT, attributes, [], okStatus ); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); done(); }); }); @@ -390,14 +389,14 @@ describe('ioredis', () => { assert.strictEqual(endedSpans[1].name, 'set'); assert.strictEqual(endedSpans[2].name, 'get'); assert.strictEqual(endedSpans[3].name, 'exec'); - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, attributes, [], okStatus ); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); done(); }); }); @@ -424,14 +423,14 @@ describe('ioredis', () => { assert.strictEqual(endedSpans[0].name, 'set'); assert.strictEqual(endedSpans[1].name, 'del'); assert.strictEqual(endedSpans[2].name, 'test span'); - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, attributes, [], okStatus ); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); done(); }); }); @@ -452,14 +451,14 @@ describe('ioredis', () => { const endedSpans = memoryExporter.getFinishedSpans(); assert.strictEqual(endedSpans.length, 2); assert.strictEqual(endedSpans[0].name, `get`); - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, attributes, [], okStatus ); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); } catch (error) { assert.ifError(error); } @@ -481,14 +480,14 @@ describe('ioredis', () => { const endedSpans = memoryExporter.getFinishedSpans(); assert.strictEqual(endedSpans.length, 2); assert.strictEqual(endedSpans[0].name, 'del'); - assertionUtils.assertSpan( + testUtils.assertSpan( endedSpans[0], SpanKind.CLIENT, attributes, [], okStatus ); - assertionUtils.assertPropagation(endedSpans[0], span); + testUtils.assertPropagation(endedSpans[0], span); } catch (error) { assert.ifError(error); } diff --git a/packages/opentelemetry-plugin-ioredis/test/testUtils.ts b/packages/opentelemetry-plugin-ioredis/test/testUtils.ts deleted file mode 100644 index a40b0b4aab0..00000000000 --- a/packages/opentelemetry-plugin-ioredis/test/testUtils.ts +++ /dev/null @@ -1,54 +0,0 @@ -/*! - * Copyright 2019, OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as childProcess from 'child_process'; -export function startDocker() { - const tasks = [ - run('docker run -d -p 63790:6379 --name otjsredis redis:alpine'), - ]; - - for (let i = 0; i < tasks.length; i++) { - const task = tasks[i]; - if (task && task.code !== 0) { - console.error('Failed to start container!'); - console.error(task.output); - return false; - } - } - return true; -} - -export function cleanUpDocker() { - run('docker stop otjsredis'); - run('docker rm otjsredis'); -} - -function run(cmd: string) { - try { - const proc = childProcess.spawnSync(cmd, { - shell: true, - }); - return { - code: proc.status, - output: proc.output - .map(v => String.fromCharCode.apply(null, v as any)) - .join(''), - }; - } catch (e) { - console.log(e); - return; - } -}