Skip to content

Commit

Permalink
Adds zipkin-instrumentation-redis support (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
securingsincity authored and adriancole committed Oct 12, 2016
1 parent 7f3efe8 commit 3ff7b2d
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 1 deletion.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ script:

services:
- memcached
- redis

compiler: clang-3.6
env:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ At the time of writing, zipkin-js instruments these libraries:
- [fetch](packages/zipkin-instrumentation-fetch) (zipkin-instrumentation-fetch)
- [hapi](packages/zipkin-instrumentation-hapi) (zipkin-instrumentation-hapi)
- [memcached](packages/zipkin-instrumentation-memcached) (zipkin-instrumentation-memcached)
- [redis](packages/zipkin-instrumentation-redis) (zipkin-instrumentation-redis)
- [restify](packages/zipkin-instrumentation-restify) (zipkin-instrumentation-restify)

Every module has a README.md file that describes how to use it.
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
install:
- choco install nodejs memcached
- choco install nodejs memcached redis
- node --version
- npm --version
- npm install
Expand Down
22 changes: 22 additions & 0 deletions packages/zipkin-instrumentation-redis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# zipkin-instrumentation-redis

This library will wrap the [redis client](https://www.npmjs.com/package/redis).

## Usage

```javascript
const {Tracer} = require('zipkin');
const Redis = require('redis');
const zipkinClient = require('zipkin-instrumentation-redis');
const tracer = new Tracer({ctxImpl, recorder}); // configure your tracer properly here
const redisConnectionOptions = {
host: 'localhost',
port: '6379'
};
const redis = zipkinClient(tracer, Redis, redisConnectionOptions);

// Your application code here
redis.get('foo', (err, data) => {
console.log('got', data.foo);
});
```
19 changes: 19 additions & 0 deletions packages/zipkin-instrumentation-redis/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "zipkin-instrumentation-redis",
"version": "0.2.6",
"description": "Interceptor for redis clients",
"main": "src/zipkinClient.js",
"scripts": {
"test": "node ../../node_modules/mocha/bin/mocha --require ../../test/helper.js"
},
"author": "OpenZipkin <[email protected]>",
"license": "Apache-2.0",
"repository": "https://github.com/openzipkin/zipkin-js",
"devDependencies": {
"redis": "^2.6.2",
"zipkin": "^0.2.6"
},
"dependencies": {
"redis-commands": "^1.2.0"
}
}
53 changes: 53 additions & 0 deletions packages/zipkin-instrumentation-redis/src/zipkinClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const {Annotation} = require('zipkin');
const redisCommands = require('redis-commands');
module.exports = function zipkinClient(tracer, redis, options, serviceName = 'redis') {
function mkZipkinCallback(callback, id) {
return function zipkinCallback(...args) {
tracer.scoped(() => {
tracer.setId(id);
tracer.recordAnnotation(new Annotation.ClientRecv());
});
callback.apply(this, args);
};
}
function commonAnnotations(rpc) {
tracer.recordAnnotation(new Annotation.ClientSend());
tracer.recordServiceName(serviceName);
tracer.recordRpc(rpc);
}


const redisClient = redis.createClient(options);
const methodsToWrap = redisCommands.list;
const restrictedCommands = [
'ping',
'flushall',
'flushdb',
'select',
'auth',
'info',
'quit',
'slaveof',
'config',
'sentinel'];
methodsToWrap.forEach((method) => {
if (restrictedCommands.indexOf(method) > -1) {
return;
}
const actualFn = redisClient[method];
redisClient[method] = function(...args) {
const callback = args.pop();
let id;
tracer.scoped(() => {
id = tracer.createChildId();
tracer.setId(id);
commonAnnotations(method);
});
const wrapper = mkZipkinCallback(callback, id);
const newArgs = [...args, wrapper];
actualFn.apply(this, newArgs);
};
});

return redisClient;
};
8 changes: 8 additions & 0 deletions packages/zipkin-instrumentation-redis/test/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"env": {
"mocha": true
},
"globals": {
"expect": true
}
}
84 changes: 84 additions & 0 deletions packages/zipkin-instrumentation-redis/test/integrationTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const sinon = require('sinon');
const {Tracer, ExplicitContext} = require('zipkin');
const zipkinClient = require('../src/zipkinClient');

const redisConnectionOptions = {
host: 'localhost',
port: '6379'
};

const Redis = require('redis');

function getRedis(tracer) {
return zipkinClient(tracer, Redis, redisConnectionOptions);
}

describe('redis interceptor', () => {
it('should add zipkin annotations', (done) => {
const ctxImpl = new ExplicitContext();
const recorder = {record: sinon.spy()};
// const recorder = new ConsoleRecorder();
const tracer = new Tracer({ctxImpl, recorder});

const redis = getRedis(tracer);
redis.on('error', done);
tracer.setId(tracer.createRootId());
const ctx = ctxImpl.getContext();
redis.set('ping', 'pong', 10, () => {
ctxImpl.letContext(ctx, () => {
redis.get('ping', () => {
const annotations = recorder.record.args.map(args => args[0]);
const firstAnn = annotations[0];
expect(annotations).to.have.length(8);

function runTest(start, stop) {
let lastSpanId;
annotations.slice(start, stop).forEach((ann) => {
if (!lastSpanId) {
lastSpanId = ann.traceId.spanId;
}
expect(ann.traceId.spanId).to.equal(lastSpanId);
});
}

runTest(0, 4);
runTest(4, 8);

expect(
annotations[0].traceId.spanId
).not.to.equal(annotations[4].traceId.spanId);

annotations.forEach(ann => {
expect(ann.traceId.parentId).to.equal(firstAnn.traceId.traceId);
expect(ann.traceId.spanId).not.to.equal(firstAnn.traceId.traceId);
expect(ann.traceId.traceId).to.equal(firstAnn.traceId.traceId);
});

done();
});
});
});
});

it('should run redis calls', done => {
const ctxImpl = new ExplicitContext();
const recorder = {record: () => { }};
const tracer = new Tracer({ctxImpl, recorder});
const redis = getRedis(tracer);
redis.on('error', done);
redis.set('foo', 'bar', err => {
if (err) {
done(err);
} else {
redis.get('foo', (err2, data) => {
if (err2) {
done(err2);
} else {
expect(data).to.deep.equal('bar');
done();
}
});
}
});
});
});

0 comments on commit 3ff7b2d

Please sign in to comment.