Skip to content

Commit

Permalink
feat(plugin): implement redis plugin (#503)
Browse files Browse the repository at this point in the history
* feat(plugin): implement redis plugin

* fix: circle redis testing

* fix: set span error status

* fix: run the redis service

* fix: linting

* feat: add redis error handling statuses

* fix: pr comments

* fix: redis linting

* refactor: move patches to utils for clarity

* fix: linting
  • Loading branch information
Mark Wolff authored and mayurkale22 committed Nov 19, 2019
1 parent 724f9cc commit fa583fd
Show file tree
Hide file tree
Showing 11 changed files with 697 additions and 11 deletions.
18 changes: 13 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
version: 2

test_env: &test_env
node_test_env: &node_test_env
RUN_POSTGRES_TESTS: 1
RUN_MONGODB_TESTS: 1
RUN_REDIS_TESTS: 1
POSTGRES_USER: postgres
POSTGRES_DB: circle_database
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
OPENTELEMETRY_REDIS_HOST: 'localhost'
OPENTELEMETRY_REDIS_PORT: 6379
MONGODB_HOST: localhost
MONGODB_PORT: 27017
MONGODB_DB: opentelemetry-tests

postgres_service: &postgres_service
image: circleci/postgres:9.6-alpine
environment: # env to pass to CircleCI, specified values must match test_env
environment: # env to pass to CircleCI, specified values must match node_test_env
POSTGRES_USER: postgres
POSTGRES_DB: circle_database
redis_service: &redis_service
image: redis

mongo_service: &mongo_service
image: mongo
Expand Down Expand Up @@ -150,22 +155,25 @@ jobs:
node8:
docker:
- image: node:8
environment: *test_env
environment: *node_test_env
- *postgres_service
- *redis_service
- *mongo_service
<<: *node_unit_tests
node10:
docker:
- image: node:10
environment: *test_env
environment: *node_test_env
- *postgres_service
- *redis_service
- *mongo_service
<<: *node_unit_tests
node12:
docker:
- image: node:12
environment: *test_env
environment: *node_test_env
- *postgres_service
- *redis_service
- *mongo_service
<<: *node_unit_tests
node12-browsers:
Expand Down
29 changes: 26 additions & 3 deletions packages/opentelemetry-plugin-redis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![devDependencies][devDependencies-image]][devDependencies-url]
[![Apache License][license-image]][license-image]

This module provides automatic instrumentation for [`redis`](https://github.com/NodeRedis/node_redis).
This module provides automatic instrumentation for [`redis@^2.6.0`](https://github.com/NodeRedis/node_redis).

For automatic instrumentation see the
[@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node) package.
Expand All @@ -15,14 +15,37 @@ For automatic instrumentation see the
npm install --save @opentelemetry/plugin-redis
```

### Supported Versions
- `>=2.6.0`

## Usage

OptenTelemetry Redis 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 [redis](https://www.npmjs.com/package/redis).

To load a specific plugin (**redis** in this case), specify it in the Node Tracer's configuration
```js
const { NodeTracer } = require('@opentelemetry/node');

const tracer = new NodeTracer({
plugins: {
redis: {
enabled: true,
// You may use a package name or absolute path to the file.
path: '@opentelemetry/plugin-redis',
}
}
});
```
const opentelemetry = require('@opentelemetry/plugin-redis');

// TODO: DEMONSTRATE API
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();
```

<!-- See [examples/redis](https://github.com/open-telemetry/opentelemetry-js/tree/master/examples/redis) for a short example. -->

## Useful links
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
Expand Down
14 changes: 11 additions & 3 deletions packages/opentelemetry-plugin-redis/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@
"name": "@opentelemetry/plugin-redis",
"version": "0.2.0",
"description": "OpenTelemetry redis automatic instrumentation package.",
"private": true,
"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"
},
Expand Down Expand Up @@ -42,10 +44,16 @@
"devDependencies": {
"@types/mocha": "^5.2.7",
"@types/node": "^12.6.9",
"@types/redis": "^2.8.14",
"@types/shimmer": "^1.0.1",
"@opentelemetry/node": "^0.2.0",
"@opentelemetry/tracing": "^0.2.0",
"codecov": "^3.6.1",
"cross-env": "^6.0.3",
"gts": "^1.1.0",
"mocha": "^6.2.0",
"nyc": "^14.1.1",
"redis": "^2.8.0",
"rimraf": "^3.0.0",
"ts-mocha": "^6.0.0",
"ts-node": "^8.3.0",
Expand All @@ -55,7 +63,7 @@
},
"dependencies": {
"@opentelemetry/core": "^0.2.0",
"@opentelemetry/node": "^0.2.0",
"@opentelemetry/types": "^0.2.0"
"@opentelemetry/types": "^0.2.0",
"shimmer": "^1.2.1"
}
}
32 changes: 32 additions & 0 deletions packages/opentelemetry-plugin-redis/src/enums.ts
Original file line number Diff line number Diff line change
@@ -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',
}
2 changes: 2 additions & 0 deletions packages/opentelemetry-plugin-redis/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from './redis';
101 changes: 101 additions & 0 deletions packages/opentelemetry-plugin-redis/src/redis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*!
* 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 redisTypes from 'redis';
import * as shimmer from 'shimmer';
import {
getTracedCreateClient,
getTracedCreateStreamTrace,
getTracedInternalSendCommand,
} from './utils';

export class RedisPlugin extends BasePlugin<typeof redisTypes> {
static readonly COMPONENT = 'redis';
readonly supportedVersions = ['^2.6.0']; // equivalent to >= 2.6.0 <3

constructor(readonly moduleName: string) {
super();
}

protected patch() {
if (this._moduleExports.RedisClient) {
this._logger.debug(
'Patching redis.RedisClient.prototype.internal_send_command'
);
shimmer.wrap(
this._moduleExports.RedisClient.prototype,
'internal_send_command',
this._getPatchInternalSendCommand()
);

this._logger.debug('patching redis.create_stream');
shimmer.wrap(
this._moduleExports.RedisClient.prototype,
'create_stream',
this._getPatchCreateStream()
);

this._logger.debug('patching redis.createClient');
shimmer.wrap(
this._moduleExports,
'createClient',
this._getPatchCreateClient()
);
}
return this._moduleExports;
}

protected unpatch(): void {
if (this._moduleExports) {
shimmer.unwrap(
this._moduleExports.RedisClient.prototype,
'internal_send_command'
);
shimmer.unwrap(
this._moduleExports.RedisClient.prototype,
'create_stream'
);
shimmer.unwrap(this._moduleExports, 'createClient');
}
}

/**
* Patch internal_send_command(...) to trace requests
*/
private _getPatchInternalSendCommand() {
const tracer = this._tracer;
return function internal_send_command(original: Function) {
return getTracedInternalSendCommand(tracer, original);
};
}

private _getPatchCreateClient() {
const tracer = this._tracer;
return function createClient(original: Function) {
return getTracedCreateClient(tracer, original);
};
}

private _getPatchCreateStream() {
const tracer = this._tracer;
return function createReadStream(original: Function) {
return getTracedCreateStreamTrace(tracer, original);
};
}
}

export const plugin = new RedisPlugin(RedisPlugin.COMPONENT);
41 changes: 41 additions & 0 deletions packages/opentelemetry-plugin-redis/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*!
* 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 redisTypes from 'redis';
import { EventEmitter } from 'events';

// exported from
// https://github.com/NodeRedis/node_redis/blob/master/lib/command.js
export interface RedisCommand {
command: string;
args: string[];
buffer_args: boolean;
callback: redisTypes.Callback<unknown>;
call_on_write: boolean;
}

export interface RedisPluginClientTypes {
options?: {
host: string;
port: string;
};

address?: string;
}

export interface RedisPluginStreamTypes {
stream?: { get(): EventEmitter; set(val: EventEmitter): void };
}
Loading

0 comments on commit fa583fd

Please sign in to comment.