Skip to content

Commit

Permalink
chore: merge changes from #239
Browse files Browse the repository at this point in the history
  • Loading branch information
Flarna committed Jan 29, 2021
1 parent 7904b0e commit afde553
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 13 deletions.
16 changes: 15 additions & 1 deletion plugins/node/opentelemetry-instrumentation-ioredis/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {

const endSpan = (span: Span, err: NodeJS.ErrnoException | null | undefined) => {
if (err) {
span.recordException(err);
span.setStatus({
code: StatusCode.ERROR,
message: err.message,
Expand Down Expand Up @@ -116,7 +117,20 @@ export const traceSendCommand = (

try {
const result = original.apply(this, arguments);
endSpan(span, null);

const origResolve = cmd.resolve;
/* eslint-disable @typescript-eslint/no-explicit-any */
cmd.resolve = function (result: any) {
endSpan(span, null);
origResolve(result);
};

const origReject = cmd.reject;
cmd.reject = function (err: Error) {
endSpan(span, err);
origReject(err);
};

return result;
} catch (error) {
endSpan(span, error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
import * as testUtils from '@opentelemetry/test-utils';
import {
InMemorySpanExporter,
ReadableSpan,
SimpleSpanProcessor,
} from '@opentelemetry/tracing';
import * as assert from 'assert';
Expand All @@ -38,6 +39,7 @@ import {
} from '../src/types';
import {
DatabaseAttribute,
ExceptionAttribute,
GeneralAttribute,
} from '@opentelemetry/semantic-conventions';

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

const predictableStackTrace =
'-- Stack trace replaced by test to predictable value -- ';
const sanitizeEventForAssertion = (span: ReadableSpan) => {
span.events.forEach(e => {
// stack trace includes data such as /user/{userName}/repos/{projectName}
if (e.attributes?.[ExceptionAttribute.STACKTRACE]) {
e.attributes[ExceptionAttribute.STACKTRACE] = predictableStackTrace;
}

// since time will change on each test invocation, it is being replaced to predicable value
e.time = [0, 0];
});
};

describe('ioredis', () => {
const provider = new NodeTracerProvider();
let ioredis: typeof ioredisTypes;
Expand Down Expand Up @@ -138,9 +154,11 @@ describe('ioredis', () => {
assert.strictEqual(endedSpans.length, 3);
assert.strictEqual(endedSpans[2].name, 'test span');

client.quit(done);
assert.strictEqual(endedSpans.length, 4);
assert.strictEqual(endedSpans[3].name, 'quit');
client.quit(() => {
assert.strictEqual(endedSpans.length, 4);
assert.strictEqual(endedSpans[3].name, 'quit');
done();
});
};
const errorHandler = (err: Error) => {
assert.ifError(err);
Expand Down Expand Up @@ -270,6 +288,38 @@ describe('ioredis', () => {
});
});

it('should set span with error when redis return reject', async () => {
const span = provider.getTracer('ioredis-test').startSpan('test span');
await context.with(setSpan(context.active(), span), async () => {
await client.set('non-int-key', 'non-int-value');
try {
// should throw 'ReplyError: ERR value is not an integer or out of range'
// because the value im the key is not numeric and we try to increment it
await client.incr('non-int-key');
} catch (ex) {
const endedSpans = memoryExporter.getFinishedSpans();
assert.strictEqual(endedSpans.length, 2);
const ioredisSpan = endedSpans[1];
// redis 'incr' operation failed with exception, so span should indicate it
assert.strictEqual(ioredisSpan.status.code, StatusCode.ERROR);
const exceptionEvent = ioredisSpan.events[0];
assert.strictEqual(exceptionEvent.name, 'exception');
assert.strictEqual(
exceptionEvent.attributes?.[ExceptionAttribute.MESSAGE],
ex.message
);
assert.strictEqual(
exceptionEvent.attributes?.[ExceptionAttribute.STACKTRACE],
ex.stack
);
assert.strictEqual(
exceptionEvent.attributes?.[ExceptionAttribute.TYPE],
ex.name
);
}
});
});

it('should create a child span for streamify scanning', done => {
const attributes = {
...DEFAULT_ATTRIBUTES,
Expand Down Expand Up @@ -329,10 +379,10 @@ describe('ioredis', () => {
const spanNames = [
'connect',
'connect',
'subscribe',
'info',
'info',
'subscribe',
'subscribe',
'publish',
'publish',
'unsubscribe',
Expand Down Expand Up @@ -384,24 +434,48 @@ describe('ioredis', () => {

span.end();
const endedSpans = memoryExporter.getFinishedSpans();
const evalshaSpan = endedSpans[0];
// the script may be already cached on server therefore we get either 2 or 3 spans
if (endedSpans.length === 3) {
assert.strictEqual(endedSpans[2].name, 'test span');
assert.strictEqual(endedSpans[1].name, 'eval');
assert.strictEqual(endedSpans[0].name, 'evalsha');
// in this case, server returns NOSCRIPT error for evalsha,
// telling the client to use EVAL instead
sanitizeEventForAssertion(evalshaSpan);
testUtils.assertSpan(
evalshaSpan,
SpanKind.CLIENT,
attributes,
[
{
attributes: {
[ExceptionAttribute.MESSAGE]:
'NOSCRIPT No matching script. Please use EVAL.',
[ExceptionAttribute.STACKTRACE]: predictableStackTrace,
[ExceptionAttribute.TYPE]: 'ReplyError',
},
name: 'exception',
time: [0, 0],
},
],
{
code: StatusCode.ERROR,
}
);
} else {
assert.strictEqual(endedSpans.length, 2);
assert.strictEqual(endedSpans[1].name, 'test span');
assert.strictEqual(endedSpans[0].name, 'evalsha');
testUtils.assertSpan(
evalshaSpan,
SpanKind.CLIENT,
attributes,
[],
unsetStatus
);
}
testUtils.assertSpan(
endedSpans[0],
SpanKind.CLIENT,
attributes,
[],
unsetStatus
);
testUtils.assertPropagation(endedSpans[0], span);
testUtils.assertPropagation(evalshaSpan, span);
done();
});
});
Expand Down

0 comments on commit afde553

Please sign in to comment.