Skip to content

Commit fa583fd

Browse files
Mark Wolffmayurkale22
authored andcommitted
feat(plugin): implement redis plugin (#503)
* 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
1 parent 724f9cc commit fa583fd

File tree

11 files changed

+697
-11
lines changed

11 files changed

+697
-11
lines changed

.circleci/config.yml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
version: 2
22

3-
test_env: &test_env
3+
node_test_env: &node_test_env
44
RUN_POSTGRES_TESTS: 1
55
RUN_MONGODB_TESTS: 1
6+
RUN_REDIS_TESTS: 1
67
POSTGRES_USER: postgres
78
POSTGRES_DB: circle_database
89
POSTGRES_HOST: localhost
910
POSTGRES_PORT: 5432
11+
OPENTELEMETRY_REDIS_HOST: 'localhost'
12+
OPENTELEMETRY_REDIS_PORT: 6379
1013
MONGODB_HOST: localhost
1114
MONGODB_PORT: 27017
1215
MONGODB_DB: opentelemetry-tests
1316

1417
postgres_service: &postgres_service
1518
image: circleci/postgres:9.6-alpine
16-
environment: # env to pass to CircleCI, specified values must match test_env
19+
environment: # env to pass to CircleCI, specified values must match node_test_env
1720
POSTGRES_USER: postgres
1821
POSTGRES_DB: circle_database
22+
redis_service: &redis_service
23+
image: redis
1924

2025
mongo_service: &mongo_service
2126
image: mongo
@@ -150,22 +155,25 @@ jobs:
150155
node8:
151156
docker:
152157
- image: node:8
153-
environment: *test_env
158+
environment: *node_test_env
154159
- *postgres_service
160+
- *redis_service
155161
- *mongo_service
156162
<<: *node_unit_tests
157163
node10:
158164
docker:
159165
- image: node:10
160-
environment: *test_env
166+
environment: *node_test_env
161167
- *postgres_service
168+
- *redis_service
162169
- *mongo_service
163170
<<: *node_unit_tests
164171
node12:
165172
docker:
166173
- image: node:12
167-
environment: *test_env
174+
environment: *node_test_env
168175
- *postgres_service
176+
- *redis_service
169177
- *mongo_service
170178
<<: *node_unit_tests
171179
node12-browsers:

packages/opentelemetry-plugin-redis/README.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![devDependencies][devDependencies-image]][devDependencies-url]
55
[![Apache License][license-image]][license-image]
66

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

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

18+
### Supported Versions
19+
- `>=2.6.0`
20+
1821
## Usage
1922

23+
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).
24+
25+
To load a specific plugin (**redis** in this case), specify it in the Node Tracer's configuration
26+
```js
27+
const { NodeTracer } = require('@opentelemetry/node');
28+
29+
const tracer = new NodeTracer({
30+
plugins: {
31+
redis: {
32+
enabled: true,
33+
// You may use a package name or absolute path to the file.
34+
path: '@opentelemetry/plugin-redis',
35+
}
36+
}
37+
});
2038
```
21-
const opentelemetry = require('@opentelemetry/plugin-redis');
2239

23-
// TODO: DEMONSTRATE API
40+
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.
41+
```javascript
42+
const { NodeTracer } = require('@opentelemetry/node');
43+
44+
const tracer = new NodeTracer();
2445
```
2546

47+
<!-- See [examples/redis](https://github.com/open-telemetry/opentelemetry-js/tree/master/examples/redis) for a short example. -->
48+
2649
## Useful links
2750
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
2851
- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>

packages/opentelemetry-plugin-redis/package.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
"name": "@opentelemetry/plugin-redis",
33
"version": "0.2.0",
44
"description": "OpenTelemetry redis automatic instrumentation package.",
5-
"private": true,
65
"main": "build/src/index.js",
76
"types": "build/src/index.d.ts",
87
"repository": "open-telemetry/opentelemetry-js",
98
"scripts": {
109
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.ts'",
10+
"test:debug": "cross-env RUN_REDIS_TESTS_LOCAL=true ts-mocha --inspect-brk --no-timeouts -p tsconfig.json 'test/**/*.test.ts'",
11+
"test:local": "cross-env RUN_REDIS_TESTS_LOCAL=true yarn test",
1112
"tdd": "yarn test -- --watch-extensions ts --watch",
1213
"clean": "rimraf build/*",
1314
"check": "gts check",
1415
"precompile": "tsc --version",
1516
"compile": "tsc -p .",
17+
"codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
1618
"fix": "gts fix",
1719
"prepare": "npm run compile"
1820
},
@@ -42,10 +44,16 @@
4244
"devDependencies": {
4345
"@types/mocha": "^5.2.7",
4446
"@types/node": "^12.6.9",
47+
"@types/redis": "^2.8.14",
48+
"@types/shimmer": "^1.0.1",
49+
"@opentelemetry/node": "^0.2.0",
50+
"@opentelemetry/tracing": "^0.2.0",
4551
"codecov": "^3.6.1",
52+
"cross-env": "^6.0.3",
4653
"gts": "^1.1.0",
4754
"mocha": "^6.2.0",
4855
"nyc": "^14.1.1",
56+
"redis": "^2.8.0",
4957
"rimraf": "^3.0.0",
5058
"ts-mocha": "^6.0.0",
5159
"ts-node": "^8.3.0",
@@ -55,7 +63,7 @@
5563
},
5664
"dependencies": {
5765
"@opentelemetry/core": "^0.2.0",
58-
"@opentelemetry/node": "^0.2.0",
59-
"@opentelemetry/types": "^0.2.0"
66+
"@opentelemetry/types": "^0.2.0",
67+
"shimmer": "^1.2.1"
6068
}
6169
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*!
2+
* Copyright 2019, OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export enum AttributeNames {
18+
// required by https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md#databases-client-calls
19+
COMPONENT = 'component',
20+
DB_TYPE = 'db.type',
21+
DB_INSTANCE = 'db.instance',
22+
DB_STATEMENT = 'db.statement',
23+
PEER_ADDRESS = 'peer.address',
24+
PEER_HOSTNAME = 'peer.host',
25+
26+
// optional
27+
DB_USER = 'db.user',
28+
PEER_PORT = 'peer.port',
29+
PEER_IPV4 = 'peer.ipv4',
30+
PEER_IPV6 = 'peer.ipv6',
31+
PEER_SERVICE = 'peer.service',
32+
}

packages/opentelemetry-plugin-redis/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
17+
export * from './redis';
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*!
2+
* Copyright 2019, OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { BasePlugin } from '@opentelemetry/core';
18+
import * as redisTypes from 'redis';
19+
import * as shimmer from 'shimmer';
20+
import {
21+
getTracedCreateClient,
22+
getTracedCreateStreamTrace,
23+
getTracedInternalSendCommand,
24+
} from './utils';
25+
26+
export class RedisPlugin extends BasePlugin<typeof redisTypes> {
27+
static readonly COMPONENT = 'redis';
28+
readonly supportedVersions = ['^2.6.0']; // equivalent to >= 2.6.0 <3
29+
30+
constructor(readonly moduleName: string) {
31+
super();
32+
}
33+
34+
protected patch() {
35+
if (this._moduleExports.RedisClient) {
36+
this._logger.debug(
37+
'Patching redis.RedisClient.prototype.internal_send_command'
38+
);
39+
shimmer.wrap(
40+
this._moduleExports.RedisClient.prototype,
41+
'internal_send_command',
42+
this._getPatchInternalSendCommand()
43+
);
44+
45+
this._logger.debug('patching redis.create_stream');
46+
shimmer.wrap(
47+
this._moduleExports.RedisClient.prototype,
48+
'create_stream',
49+
this._getPatchCreateStream()
50+
);
51+
52+
this._logger.debug('patching redis.createClient');
53+
shimmer.wrap(
54+
this._moduleExports,
55+
'createClient',
56+
this._getPatchCreateClient()
57+
);
58+
}
59+
return this._moduleExports;
60+
}
61+
62+
protected unpatch(): void {
63+
if (this._moduleExports) {
64+
shimmer.unwrap(
65+
this._moduleExports.RedisClient.prototype,
66+
'internal_send_command'
67+
);
68+
shimmer.unwrap(
69+
this._moduleExports.RedisClient.prototype,
70+
'create_stream'
71+
);
72+
shimmer.unwrap(this._moduleExports, 'createClient');
73+
}
74+
}
75+
76+
/**
77+
* Patch internal_send_command(...) to trace requests
78+
*/
79+
private _getPatchInternalSendCommand() {
80+
const tracer = this._tracer;
81+
return function internal_send_command(original: Function) {
82+
return getTracedInternalSendCommand(tracer, original);
83+
};
84+
}
85+
86+
private _getPatchCreateClient() {
87+
const tracer = this._tracer;
88+
return function createClient(original: Function) {
89+
return getTracedCreateClient(tracer, original);
90+
};
91+
}
92+
93+
private _getPatchCreateStream() {
94+
const tracer = this._tracer;
95+
return function createReadStream(original: Function) {
96+
return getTracedCreateStreamTrace(tracer, original);
97+
};
98+
}
99+
}
100+
101+
export const plugin = new RedisPlugin(RedisPlugin.COMPONENT);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*!
2+
* Copyright 2019, OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as redisTypes from 'redis';
18+
import { EventEmitter } from 'events';
19+
20+
// exported from
21+
// https://github.com/NodeRedis/node_redis/blob/master/lib/command.js
22+
export interface RedisCommand {
23+
command: string;
24+
args: string[];
25+
buffer_args: boolean;
26+
callback: redisTypes.Callback<unknown>;
27+
call_on_write: boolean;
28+
}
29+
30+
export interface RedisPluginClientTypes {
31+
options?: {
32+
host: string;
33+
port: string;
34+
};
35+
36+
address?: string;
37+
}
38+
39+
export interface RedisPluginStreamTypes {
40+
stream?: { get(): EventEmitter; set(val: EventEmitter): void };
41+
}

0 commit comments

Comments
 (0)