Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
245d7ee
feat(instrumentation-ioredis): add moduleVersionAttributeName config …
Apr 7, 2021
576233f
Merge remote-tracking branch 'upstream/main' into ioredis-module-vers…
Apr 18, 2021
12ba869
chore(ioredis): type of moduleVersion
Apr 18, 2021
da2eb31
Merge remote-tracking branch 'upstream/main' into ioredis-module-vers…
Apr 25, 2021
ce35c7a
feat(instrumentation-ioredis): convert module version capturing to hook
Apr 25, 2021
077973e
style(instrumentation-ioredis): moduleVersion optional type
Apr 25, 2021
3d06618
fix(instrumentation-ioredis): no assert.match in node 10
Apr 25, 2021
e7db198
docs(instrumentation-ioredis): add request hook signuature to README
Apr 26, 2021
5fbfa93
docs(instrumentation-ioredis): lighter hook params documentation
Apr 26, 2021
0435644
docs(instrumentation-ioredis): add request hook example
Apr 26, 2021
ea1d7b9
docs(instrumentation-ioredis): improve request hook example
Apr 26, 2021
acd1789
Update plugins/node/opentelemetry-instrumentation-ioredis/src/types.ts
blumamir Apr 26, 2021
1dde769
Merge remote-tracking branch 'upstream/main' into ioredis-module-vers…
Apr 26, 2021
14ca036
docs(instrumentation-ioredis): double to single quotes
Apr 26, 2021
4d0a1be
fix(instrumentation-ioredis): update changed interface name everywhere
Apr 26, 2021
a509313
Merge remote-tracking branch 'upstream/main' into ioredis-module-vers…
Apr 26, 2021
2924b56
docs(instrumentation-ioredis): revert change of badge
Apr 26, 2021
b89242e
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
Apr 26, 2021
ec1e3d1
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
Apr 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ IORedis instrumentation has few options available to choose from. You can set th
| Options | Type | Description |
| ------- | ---- | ----------- |
| `dbStatementSerializer` | `DbStatementSerializer` | IORedis instrumentation will serialize db.statement using the specified function. |
| `requestHook` | `RedisRequestCustomAttributeFunction` | Function for adding custom attributes on db request |
| `responseHook` | `RedisResponseCustomAttributeFunction` | Function for adding custom attributes on db response |
| `requireParentSpan` | `boolean` | Require parent to create ioredis span, default when unset is true |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ export class IORedisInstrumentation extends InstrumentationBase<
new InstrumentationNodeModuleDefinition<typeof ioredisTypes>(
'ioredis',
['>1 <5'],
moduleExports => {
(moduleExports, moduleVersion?: string) => {
diag.debug('Applying patch for ioredis');
if (isWrapped(moduleExports.prototype.sendCommand)) {
this._unwrap(moduleExports.prototype, 'sendCommand');
}
this._wrap(
moduleExports.prototype,
'sendCommand',
this._patchSendCommand()
this._patchSendCommand(moduleVersion)
);
if (isWrapped(moduleExports.prototype.connect)) {
this._unwrap(moduleExports.prototype, 'connect');
Expand All @@ -80,9 +80,14 @@ export class IORedisInstrumentation extends InstrumentationBase<
/**
* Patch send command internal to trace requests
*/
private _patchSendCommand() {
private _patchSendCommand(moduleVersion?: string) {
return (original: Function) => {
return traceSendCommand(this.tracer, original, this._config);
return traceSendCommand(
this.tracer,
original,
this._config,
moduleVersion
);
};
}

Expand Down
13 changes: 13 additions & 0 deletions plugins/node/opentelemetry-instrumentation-ioredis/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ export type DbStatementSerializer = (
cmdArgs: IORedisCommand['args']
) => string;

export interface IoRedisRequestHookInformation {
moduleVersion?: string;
cmdName: IORedisCommand['name'];
cmdArgs: IORedisCommand['args'];
}

export interface RedisRequestCustomAttributeFunction {
(span: Span, requestInfo: IoRedisRequestHookInformation): void;
}

/**
* Function that can be used to add custom attributes to span on response from redis server
* @param span - The span created for the redis command, on which attributes can be set
Expand All @@ -66,6 +76,9 @@ export interface IORedisInstrumentationConfig extends InstrumentationConfig {
/** Custom serializer function for the db.statement tag */
dbStatementSerializer?: DbStatementSerializer;

/** Function for adding custom attributes on db request */
requestHook?: RedisRequestCustomAttributeFunction;

/** Function for adding custom attributes on db response */
responseHook?: RedisResponseCustomAttributeFunction;

Expand Down
22 changes: 20 additions & 2 deletions plugins/node/opentelemetry-instrumentation-ioredis/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ const defaultDbStatementSerializer: DbStatementSerializer = (
export const traceSendCommand = (
tracer: Tracer,
original: Function,
config?: IORedisInstrumentationConfig
config?: IORedisInstrumentationConfig,
moduleVersion?: string
) => {
const dbStatementSerializer =
config?.dbStatementSerializer || defaultDbStatementSerializer;
Expand All @@ -107,6 +108,23 @@ export const traceSendCommand = (
},
});

if (config?.requestHook) {
safeExecuteInTheMiddle(
() =>
config?.requestHook!(span, {
moduleVersion,
cmdName: cmd.name,
cmdArgs: cmd.args,
}),
e => {
if (e) {
diag.error('ioredis instrumentation: request hook failed', e);
}
},
true
);
}

const { host, port } = this.options;

span.setAttributes({
Expand All @@ -125,7 +143,7 @@ export const traceSendCommand = (
() => config?.responseHook?.(span, cmd.name, cmd.args, result),
e => {
if (e) {
diag.error('ioredis response hook failed', e);
diag.error('ioredis instrumentation: response hook failed', e);
}
},
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { IORedisInstrumentation } from '../src';
import {
IORedisInstrumentationConfig,
DbStatementSerializer,
IoRedisRequestHookInformation,
} from '../src/types';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';

Expand Down Expand Up @@ -226,6 +227,13 @@ describe('ioredis', () => {
});

describe('Instrumenting query operations', () => {
before(() => {
instrumentation.disable();
instrumentation = new IORedisInstrumentation();
instrumentation.setTracerProvider(provider);
require('ioredis');
});

IOREDIS_CALLBACK_OPERATIONS.forEach(command => {
it(`should create a child span for cb style ${command.description}`, done => {
const attributes = {
Expand Down Expand Up @@ -762,7 +770,74 @@ describe('ioredis', () => {
});
});

describe('Instrumenting with a custom responseHook', () => {
describe('Instrumenting with a custom hooks', () => {
it('should call requestHook when set in config', async () => {
instrumentation.disable();
const config: IORedisInstrumentationConfig = {
requestHook: (
span: Span,
requestInfo: IoRedisRequestHookInformation
) => {
assert.ok(
/\d{1,4}\.\d{1,4}\.\d{1,5}.*/.test(
requestInfo.moduleVersion as string
)
);
assert.strictEqual(requestInfo.cmdName, 'incr');
assert.deepStrictEqual(requestInfo.cmdArgs, ['request-hook-test']);

span.setAttribute(
'attribute key from request hook',
'custom value from request hook'
);
},
};
instrumentation = new IORedisInstrumentation(config);
instrumentation.setTracerProvider(provider);
require('ioredis');

const span = provider.getTracer('ioredis-test').startSpan('test span');
await context.with(setSpan(context.active(), span), async () => {
await client.incr('request-hook-test');
const endedSpans = memoryExporter.getFinishedSpans();
assert.strictEqual(endedSpans.length, 1);
assert.strictEqual(
endedSpans[0].attributes['attribute key from request hook'],
'custom value from request hook'
);
});
});

it('should ignore requestHook which throws exception', async () => {
instrumentation.disable();
const config: IORedisInstrumentationConfig = {
requestHook: (
span: Span,
_requestInfo: IoRedisRequestHookInformation
) => {
span.setAttribute(
'attribute key BEFORE exception',
'this attribute is added to span BEFORE exception is thrown thus we can expect it'
);
throw Error('error thrown in requestHook');
},
};
instrumentation = new IORedisInstrumentation(config);
instrumentation.setTracerProvider(provider);
require('ioredis');

const span = provider.getTracer('ioredis-test').startSpan('test span');
await context.with(setSpan(context.active(), span), async () => {
await client.incr('request-hook-throw-test');
const endedSpans = memoryExporter.getFinishedSpans();
assert.strictEqual(endedSpans.length, 1);
assert.strictEqual(
endedSpans[0].attributes['attribute key BEFORE exception'],
'this attribute is added to span BEFORE exception is thrown thus we can expect it'
);
});
});

it('should call responseHook when set in config', async () => {
instrumentation.disable();
const config: IORedisInstrumentationConfig = {
Expand All @@ -772,13 +847,17 @@ describe('ioredis', () => {
_cmdArgs: Array<string | Buffer | number>,
response: unknown
) => {
assert.strictEqual(cmdName, 'incr');
// the command is 'incr' on a key which does not exist, thus it increase 0 by 1 and respond 1
assert.strictEqual(response, 1);
span.setAttribute(
'attribute key from hook',
'custom value from hook'
);
try {
assert.strictEqual(cmdName, 'incr');
// the command is 'incr' on a key which does not exist, thus it increase 0 by 1 and respond 1
assert.strictEqual(response, 1);
span.setAttribute(
'attribute key from hook',
'custom value from hook'
);
} catch (err) {
console.log(err);
}
},
};
instrumentation = new IORedisInstrumentation(config);
Expand All @@ -787,7 +866,7 @@ describe('ioredis', () => {

const span = provider.getTracer('ioredis-test').startSpan('test span');
await context.with(setSpan(context.active(), span), async () => {
await client.incr('new-key');
await client.incr('response-hook-test');
const endedSpans = memoryExporter.getFinishedSpans();
assert.strictEqual(endedSpans.length, 1);
assert.strictEqual(
Expand Down Expand Up @@ -815,7 +894,7 @@ describe('ioredis', () => {

const span = provider.getTracer('ioredis-test').startSpan('test span');
await context.with(setSpan(context.active(), span), async () => {
await client.incr('some-key');
await client.incr('response-hook-throw-test');
const endedSpans = memoryExporter.getFinishedSpans();

// hook throw exception, but span should not be affected
Expand Down