Skip to content

Commit afde553

Browse files
committed
chore: merge changes from #239
1 parent 7904b0e commit afde553

2 files changed

Lines changed: 101 additions & 13 deletions

File tree

plugins/node/opentelemetry-instrumentation-ioredis/src/utils.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636

3737
const endSpan = (span: Span, err: NodeJS.ErrnoException | null | undefined) => {
3838
if (err) {
39+
span.recordException(err);
3940
span.setStatus({
4041
code: StatusCode.ERROR,
4142
message: err.message,
@@ -116,7 +117,20 @@ export const traceSendCommand = (
116117

117118
try {
118119
const result = original.apply(this, arguments);
119-
endSpan(span, null);
120+
121+
const origResolve = cmd.resolve;
122+
/* eslint-disable @typescript-eslint/no-explicit-any */
123+
cmd.resolve = function (result: any) {
124+
endSpan(span, null);
125+
origResolve(result);
126+
};
127+
128+
const origReject = cmd.reject;
129+
cmd.reject = function (err: Error) {
130+
endSpan(span, err);
131+
origReject(err);
132+
};
133+
120134
return result;
121135
} catch (error) {
122136
endSpan(span, error);

plugins/node/opentelemetry-instrumentation-ioredis/test/ioredis.test.ts

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
2727
import * as testUtils from '@opentelemetry/test-utils';
2828
import {
2929
InMemorySpanExporter,
30+
ReadableSpan,
3031
SimpleSpanProcessor,
3132
} from '@opentelemetry/tracing';
3233
import * as assert from 'assert';
@@ -38,6 +39,7 @@ import {
3839
} from '../src/types';
3940
import {
4041
DatabaseAttribute,
42+
ExceptionAttribute,
4143
GeneralAttribute,
4244
} from '@opentelemetry/semantic-conventions';
4345

@@ -61,6 +63,20 @@ const unsetStatus: Status = {
6163
code: StatusCode.UNSET,
6264
};
6365

66+
const predictableStackTrace =
67+
'-- Stack trace replaced by test to predictable value -- ';
68+
const sanitizeEventForAssertion = (span: ReadableSpan) => {
69+
span.events.forEach(e => {
70+
// stack trace includes data such as /user/{userName}/repos/{projectName}
71+
if (e.attributes?.[ExceptionAttribute.STACKTRACE]) {
72+
e.attributes[ExceptionAttribute.STACKTRACE] = predictableStackTrace;
73+
}
74+
75+
// since time will change on each test invocation, it is being replaced to predicable value
76+
e.time = [0, 0];
77+
});
78+
};
79+
6480
describe('ioredis', () => {
6581
const provider = new NodeTracerProvider();
6682
let ioredis: typeof ioredisTypes;
@@ -138,9 +154,11 @@ describe('ioredis', () => {
138154
assert.strictEqual(endedSpans.length, 3);
139155
assert.strictEqual(endedSpans[2].name, 'test span');
140156

141-
client.quit(done);
142-
assert.strictEqual(endedSpans.length, 4);
143-
assert.strictEqual(endedSpans[3].name, 'quit');
157+
client.quit(() => {
158+
assert.strictEqual(endedSpans.length, 4);
159+
assert.strictEqual(endedSpans[3].name, 'quit');
160+
done();
161+
});
144162
};
145163
const errorHandler = (err: Error) => {
146164
assert.ifError(err);
@@ -270,6 +288,38 @@ describe('ioredis', () => {
270288
});
271289
});
272290

291+
it('should set span with error when redis return reject', async () => {
292+
const span = provider.getTracer('ioredis-test').startSpan('test span');
293+
await context.with(setSpan(context.active(), span), async () => {
294+
await client.set('non-int-key', 'non-int-value');
295+
try {
296+
// should throw 'ReplyError: ERR value is not an integer or out of range'
297+
// because the value im the key is not numeric and we try to increment it
298+
await client.incr('non-int-key');
299+
} catch (ex) {
300+
const endedSpans = memoryExporter.getFinishedSpans();
301+
assert.strictEqual(endedSpans.length, 2);
302+
const ioredisSpan = endedSpans[1];
303+
// redis 'incr' operation failed with exception, so span should indicate it
304+
assert.strictEqual(ioredisSpan.status.code, StatusCode.ERROR);
305+
const exceptionEvent = ioredisSpan.events[0];
306+
assert.strictEqual(exceptionEvent.name, 'exception');
307+
assert.strictEqual(
308+
exceptionEvent.attributes?.[ExceptionAttribute.MESSAGE],
309+
ex.message
310+
);
311+
assert.strictEqual(
312+
exceptionEvent.attributes?.[ExceptionAttribute.STACKTRACE],
313+
ex.stack
314+
);
315+
assert.strictEqual(
316+
exceptionEvent.attributes?.[ExceptionAttribute.TYPE],
317+
ex.name
318+
);
319+
}
320+
});
321+
});
322+
273323
it('should create a child span for streamify scanning', done => {
274324
const attributes = {
275325
...DEFAULT_ATTRIBUTES,
@@ -329,10 +379,10 @@ describe('ioredis', () => {
329379
const spanNames = [
330380
'connect',
331381
'connect',
332-
'subscribe',
333382
'info',
334383
'info',
335384
'subscribe',
385+
'subscribe',
336386
'publish',
337387
'publish',
338388
'unsubscribe',
@@ -384,24 +434,48 @@ describe('ioredis', () => {
384434

385435
span.end();
386436
const endedSpans = memoryExporter.getFinishedSpans();
437+
const evalshaSpan = endedSpans[0];
387438
// the script may be already cached on server therefore we get either 2 or 3 spans
388439
if (endedSpans.length === 3) {
389440
assert.strictEqual(endedSpans[2].name, 'test span');
390441
assert.strictEqual(endedSpans[1].name, 'eval');
391442
assert.strictEqual(endedSpans[0].name, 'evalsha');
443+
// in this case, server returns NOSCRIPT error for evalsha,
444+
// telling the client to use EVAL instead
445+
sanitizeEventForAssertion(evalshaSpan);
446+
testUtils.assertSpan(
447+
evalshaSpan,
448+
SpanKind.CLIENT,
449+
attributes,
450+
[
451+
{
452+
attributes: {
453+
[ExceptionAttribute.MESSAGE]:
454+
'NOSCRIPT No matching script. Please use EVAL.',
455+
[ExceptionAttribute.STACKTRACE]: predictableStackTrace,
456+
[ExceptionAttribute.TYPE]: 'ReplyError',
457+
},
458+
name: 'exception',
459+
time: [0, 0],
460+
},
461+
],
462+
{
463+
code: StatusCode.ERROR,
464+
}
465+
);
392466
} else {
393467
assert.strictEqual(endedSpans.length, 2);
394468
assert.strictEqual(endedSpans[1].name, 'test span');
395469
assert.strictEqual(endedSpans[0].name, 'evalsha');
470+
testUtils.assertSpan(
471+
evalshaSpan,
472+
SpanKind.CLIENT,
473+
attributes,
474+
[],
475+
unsetStatus
476+
);
396477
}
397-
testUtils.assertSpan(
398-
endedSpans[0],
399-
SpanKind.CLIENT,
400-
attributes,
401-
[],
402-
unsetStatus
403-
);
404-
testUtils.assertPropagation(endedSpans[0], span);
478+
testUtils.assertPropagation(evalshaSpan, span);
405479
done();
406480
});
407481
});

0 commit comments

Comments
 (0)