From 82f5a9766b168637788595a35bf205be2fa89f43 Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Fri, 26 Apr 2019 18:16:40 -0400 Subject: [PATCH 01/16] Adding 128 bit traceId support Signed-off-by: Paul Biccherai --- .gitignore | 1 + src/_flow/tracer.js | 1 + src/span_context.js | 71 ++++++++++++++++++++++++++++++++++---------- src/thrift.js | 9 +++--- src/tracer.js | 12 ++++++-- test/span.js | 1 + test/span_context.js | 51 +++++++++++++++++++++++++++---- test/thrift.js | 19 ++++++++++++ test/tracer.js | 31 +++++++++++++------ 9 files changed, 160 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index dc9c88d0a..ca1545c45 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ npm-debug.log dist/ .idea/ +.vscode/ *.iml crossdock/jaeger-docker-compose.yml diff --git a/src/_flow/tracer.js b/src/_flow/tracer.js index 4334332e4..4d4f9d666 100644 --- a/src/_flow/tracer.js +++ b/src/_flow/tracer.js @@ -20,4 +20,5 @@ declare type startSpanOptions = { references?: Array, tags?: any, startTime?: number, + traceId128bit?: boolean, }; diff --git a/src/span_context.js b/src/span_context.js index 64d6d1d7b..346befd10 100644 --- a/src/span_context.js +++ b/src/span_context.js @@ -15,7 +15,8 @@ import * as constants from './constants.js'; import Utils from './util.js'; export default class SpanContext { - _traceId: any; + _traceIdLow: any; + _traceIdHigh: any; _spanId: any; _parentId: any; _traceIdStr: ?string; @@ -43,7 +44,8 @@ export default class SpanContext { _samplingFinalized: boolean; constructor( - traceId: any, + traceIdLow: any, + traceIdHigh: any, spanId: any, parentId: any, traceIdStr: ?string, @@ -54,7 +56,8 @@ export default class SpanContext { debugId: ?string = '', samplingFinalized: boolean = false ) { - this._traceId = traceId; + this._traceIdLow = traceIdLow; + this._traceIdHigh = traceIdHigh; this._spanId = spanId; this._parentId = parentId; this._traceIdStr = traceIdStr; @@ -66,11 +69,32 @@ export default class SpanContext { this._samplingFinalized = samplingFinalized; } - get traceId(): any { - if (this._traceId == null && this._traceIdStr != null) { - this._traceId = Utils.encodeInt64(this._traceIdStr); + get traceIdLow(): any { + this._tryParseTraceIdStr(); + return this._traceIdLow; + } + + get traceIdHigh(): any { + this._tryParseTraceIdStr(); + return this._traceIdHigh; + } + + _tryParseTraceIdStr() { + if (this._traceIdLow == null && this._traceIdHigh == null && this._traceIdStr != null) { + if (this._traceIdStr.length > 16) { + const safeTraceIdStr = this._traceIdStr.length % 2 == 0 ? this._traceIdStr : '0' + this._traceIdStr; + const traceId = Buffer.from(safeTraceIdStr, 'hex'); + this._traceIdHigh = traceId.slice(-16, -8); + if (this._traceIdHigh.length != 8) { + const tmpBuffer = Buffer.alloc(8); + this._traceIdHigh.copy(tmpBuffer, 8 - this._traceIdHigh.length); + this._traceIdHigh = tmpBuffer; + } + this._traceIdLow = traceId.slice(-8); + } else { + this._traceIdLow = Utils.encodeInt64(this._traceIdStr); + } } - return this._traceId; } get spanId(): any { @@ -88,8 +112,14 @@ export default class SpanContext { } get traceIdStr(): ?string { - if (this._traceIdStr == null && this._traceId != null) { - this._traceIdStr = Utils.removeLeadingZeros(this._traceId.toString('hex')); + if (this._traceIdStr == null && this._traceIdLow != null) { + if (this._traceIdHigh != null) { + this._traceIdStr = Utils.removeLeadingZeros( + this._traceIdHigh.toString('hex') + this._traceIdLow.toString('hex') + ); + } else { + this._traceIdStr = Utils.removeLeadingZeros(this._traceIdLow.toString('hex')); + } } return this._traceIdStr; } @@ -124,8 +154,13 @@ export default class SpanContext { return this._samplingFinalized; } - set traceId(traceId: Buffer): void { - this._traceId = traceId; + set traceIdLow(traceIdLow: Buffer): void { + this._traceIdLow = traceIdLow; + this._traceIdStr = null; + } + + set traceIdHigh(traceIdHigh: Buffer): void { + this._traceIdHigh = traceIdHigh; this._traceIdStr = null; } @@ -152,7 +187,7 @@ export default class SpanContext { } get isValid(): boolean { - return !!((this._traceId || this._traceIdStr) && (this._spanId || this._spanIdStr)); + return !!((this._traceIdLow || this._traceIdStr) && (this._spanId || this._spanIdStr)); } finalizeSampling(): void { @@ -181,7 +216,8 @@ export default class SpanContext { let newBaggage = Utils.clone(this._baggage); newBaggage[key] = value; return new SpanContext( - this._traceId, + this._traceIdLow, + this._traceIdHigh, this._spanId, this._parentId, this._traceIdStr, @@ -238,7 +274,8 @@ export default class SpanContext { } static withBinaryIds( - traceId: any, + traceIdLow: any, + traceIdHigh: any, spanId: any, parentId: any, flags: number, @@ -246,7 +283,8 @@ export default class SpanContext { debugId: ?string = '' ): SpanContext { return new SpanContext( - traceId, + traceIdLow, + traceIdHigh, spanId, parentId, null, // traceIdStr: string, @@ -267,7 +305,8 @@ export default class SpanContext { debugId: ?string = '' ): SpanContext { return new SpanContext( - null, // traceId, + null, // traceIdLow, + null, // traceIdHigh, null, // spanId, null, // parentId, traceIdStr, diff --git a/src/thrift.js b/src/thrift.js index c0d83a2d6..bd1028261 100644 --- a/src/thrift.js +++ b/src/thrift.js @@ -104,8 +104,8 @@ export default class ThriftUtils { thriftRefs.push({ refType: refEnum, - traceIdLow: context.traceId, - traceIdHigh: ThriftUtils.emptyBuffer, + traceIdLow: context.traceIdLow, + traceIdHigh: context.traceIdHigh == null ? ThriftUtils.emptyBuffer : context.traceIdHigh, spanId: context.spanId, }); } @@ -119,8 +119,9 @@ export default class ThriftUtils { let unsigned = true; return { - traceIdLow: span._spanContext.traceId, - traceIdHigh: ThriftUtils.emptyBuffer, // TODO(oibe) implement 128 bit ids + traceIdLow: span._spanContext.traceIdLow, + traceIdHigh: + span._spanContext.traceIdHigh == null ? ThriftUtils.emptyBuffer : span._spanContext.traceIdHigh, spanId: span._spanContext.spanId, parentSpanId: span._spanContext.parentId || ThriftUtils.emptyBuffer, operationName: span._operationName, diff --git a/src/tracer.js b/src/tracer.js index 6f11c7aba..6aa0b671c 100644 --- a/src/tracer.js +++ b/src/tracer.js @@ -188,6 +188,8 @@ export default class Tracer { * the created Span object. The time should be specified in * milliseconds as Unix timestamp. Decimal value are supported * to represent time values with sub-millisecond accuracy. + * @param {number} [options.traceId128bit] - generate root span with a + * 128bit traceId. * @return {Span} - a new Span object. **/ startSpan(operationName: string, options: ?startSpanOptions): Span { @@ -239,12 +241,18 @@ export default class Tracer { ctx.baggage = parent.baggage; } - ctx.traceId = randomId; + if (options.traceId128bit) { + ctx.traceIdLow = randomId; + ctx.traceIdHigh = Utils.getRandom64(); + } else { + ctx.traceIdLow = randomId; + } ctx.spanId = randomId; ctx.parentId = null; ctx.flags = flags; } else { - ctx.traceId = parent.traceId; + ctx.traceIdLow = parent.traceIdLow; + ctx.traceIdHigh = parent.traceIdHigh; ctx.spanId = Utils.getRandom64(); ctx.parentId = parent.spanId; ctx.flags = parent.flags; diff --git a/test/span.js b/test/span.js index 779b64196..59665ffbe 100644 --- a/test/span.js +++ b/test/span.js @@ -35,6 +35,7 @@ describe('span should', () => { spanContext = SpanContext.withBinaryIds( Utils.encodeInt64(1), + null, Utils.encodeInt64(2), Utils.encodeInt64(3), constants.SAMPLED_MASK diff --git a/test/span_context.js b/test/span_context.js index 96208dc31..e051577d1 100644 --- a/test/span_context.js +++ b/test/span_context.js @@ -24,14 +24,31 @@ describe('SpanContext should', () => { }); it('return given values as they were set', () => { - let traceId = Utils.encodeInt64(1); + let traceIdLow = Utils.encodeInt64(1); let spanId = Utils.encodeInt64(2); let parentId = Utils.encodeInt64(3); let flags = 1; - let context = SpanContext.withBinaryIds(traceId, spanId, parentId, flags); + let context = SpanContext.withBinaryIds(traceIdLow, null, spanId, parentId, flags); - assert.deepEqual(traceId, context.traceId); + assert.deepEqual(traceIdLow, context.traceIdLow); + assert.deepEqual(spanId, context.spanId); + assert.deepEqual(parentId, context.parentId); + assert.equal(flags, context.flags); + }); + + it('return given values as they were set 128 bit', () => { + let traceIdLow = Utils.encodeInt64(1); + let traceIdHigh = Utils.encodeInt64(2); + let spanId = Utils.encodeInt64(3); + let parentId = Utils.encodeInt64(4); + let flags = 1; + + let context = SpanContext.withBinaryIds(traceIdLow, traceIdHigh, spanId, parentId, flags); + + assert.deepEqual(traceIdLow, context.traceIdLow); + assert.deepEqual(traceIdHigh, context.traceIdHigh); + assert.deepEqual('20000000000000001', context.traceIdStr); assert.deepEqual(spanId, context.spanId); assert.deepEqual(parentId, context.parentId); assert.equal(flags, context.flags); @@ -40,6 +57,7 @@ describe('SpanContext should', () => { it('return IsSampled properly', () => { let context = SpanContext.withBinaryIds( Utils.encodeInt64(1), + null, Utils.encodeInt64(2), Utils.encodeInt64(3), 3 @@ -53,11 +71,12 @@ describe('SpanContext should', () => { }); it('format strings properly with toString', () => { - let ctx1 = SpanContext.withBinaryIds(Utils.encodeInt64(0x100), Utils.encodeInt64(0x7f), null, 1); + let ctx1 = SpanContext.withBinaryIds(Utils.encodeInt64(0x100), null, Utils.encodeInt64(0x7f), null, 1); assert.equal(ctx1.toString(), '100:7f:0:1'); let ctx2 = SpanContext.withBinaryIds( Utils.encodeInt64(255 << 4), + null, Utils.encodeInt64(127), Utils.encodeInt64(256), 0 @@ -65,7 +84,7 @@ describe('SpanContext should', () => { assert.equal(ctx2.toString(), 'ff0:7f:100:0'); // test large numbers - let ctx3 = SpanContext.withBinaryIds(LARGEST_64_BUFFER, LARGEST_64_BUFFER, LARGEST_64_BUFFER, 0); + let ctx3 = SpanContext.withBinaryIds(LARGEST_64_BUFFER, null, LARGEST_64_BUFFER, LARGEST_64_BUFFER, 0); assert.equal(ctx3.toString(), 'ffffffffffffffff:ffffffffffffffff:ffffffffffffffff:0'); assert.equal('ffffffffffffffff', ctx3.traceIdStr); assert.equal('ffffffffffffffff', ctx3.spanIdStr); @@ -90,6 +109,28 @@ describe('SpanContext should', () => { assert.equal(context.flags, 0x1); }); + it('turn properly formatted strings into correct span contexts 128 bit', () => { + let context = SpanContext.fromString('20000000000000100:7f:0:1'); + + assert.deepEqual('20000000000000100', context.traceIdStr); + assert.deepEqual(Utils.encodeInt64(0x100), context.traceIdLow); + assert.deepEqual(Utils.encodeInt64(0x2), context.traceIdHigh); + assert.deepEqual(Utils.encodeInt64(0x7f), context.spanId); + assert.equal(null, context.parentId); + assert.equal(1, context.flags); + + // test large numbers + context = SpanContext.fromString('ffffffffffffffffffffffffffffffff:ffffffffffffffff:5:1'); + assert.equal('ffffffffffffffffffffffffffffffff', context.traceIdStr); + assert.equal('ffffffffffffffff', context.spanIdStr); + assert.deepEqual(LARGEST_64_BUFFER, context.traceIdLow); + assert.deepEqual(LARGEST_64_BUFFER, context.traceIdHigh); + assert.deepEqual(LARGEST_64_BUFFER, context.spanId); + assert.deepEqual(LARGEST_64_BUFFER, context.spanId); + assert.deepEqual(Utils.encodeInt64(0x5), context.parentId); + assert.equal(context.flags, 0x1); + }); + it('return null on malformed traces', () => { assert.equal(SpanContext.fromString('bad value'), null); assert.equal(SpanContext.fromString('1:1:1:1:1'), null, 'Too many colons'); diff --git a/test/thrift.js b/test/thrift.js index ec1b0f3cf..3f1829130 100644 --- a/test/thrift.js +++ b/test/thrift.js @@ -11,6 +11,7 @@ // the License. import { assert } from 'chai'; +import opentracing from 'opentracing'; import ConstSampler from '../src/samplers/const_sampler.js'; import InMemoryReporter from '../src/reporters/in_memory_reporter.js'; import Tracer from '../src/tracer.js'; @@ -64,4 +65,22 @@ describe('ThriftUtils', () => { assert.deepEqual(tSpan.duration, Utils.encodeInt64((123.678 - 123.456) * 1000)); assert.deepEqual(tSpan.logs[0].timestamp, Utils.encodeInt64(123567)); }); + + it('should convert span with 128 bit traceId', () => { + let reporter = new InMemoryReporter(); + let tracer = new Tracer('test-service-name', reporter, new ConstSampler(true)); + let span = tracer.startSpan('some operation', { traceId128bit: true }); + let childOfRef = new opentracing.Reference(opentracing.REFERENCE_CHILD_OF, span.context()); + let childSpan = tracer.startSpan('some child operation', { references: [childOfRef] }); + childSpan.finish(); + span.finish(); + tracer.close(); + let tSpan = ThriftUtils.spanToThrift(childSpan); + assert.deepEqual(tSpan.traceIdLow, childSpan.context().traceIdLow); + assert.deepEqual(tSpan.traceIdHigh, childSpan.context().traceIdHigh); + assert.deepEqual(tSpan.spanId, childSpan.context().spanId); + assert.deepEqual(tSpan.references[0].traceIdLow, span.context().traceIdLow); + assert.deepEqual(tSpan.references[0].traceIdHigh, span.context().traceIdHigh); + assert.deepEqual(tSpan.references[0].spanId, span.context().spanId); + }); }); diff --git a/test/tracer.js b/test/tracer.js index ac65b2c39..92fa77566 100644 --- a/test/tracer.js +++ b/test/tracer.js @@ -89,7 +89,7 @@ describe('tracer should', () => { let spanContext = tracer.extract(opentracing.FORMAT_TEXT_MAP, headers); let rootSpan = tracer.startSpan('fry', { childOf: spanContext }); - assert.isOk(rootSpan.context().traceId); + assert.isOk(rootSpan.context().traceIdLow); assert.isNotOk(rootSpan.context().parentId); assert.equal(rootSpan.context().flags, 1); assert.equal('Bender', rootSpan.getBaggageItem('robot')); @@ -98,11 +98,11 @@ describe('tracer should', () => { }); it('create a span correctly through _startInternalSpan', () => { - let traceId = Utils.encodeInt64(1); + let traceIdLow = Utils.encodeInt64(1); let spanId = Utils.encodeInt64(2); let parentId = Utils.encodeInt64(3); let flags = 1; - let context = SpanContext.withBinaryIds(traceId, spanId, parentId, flags); + let context = SpanContext.withBinaryIds(traceIdLow, null, spanId, parentId, flags); let start = 123.456; let rpcServer = false; let internalTags = []; @@ -122,7 +122,7 @@ describe('tracer should', () => { references ); - assert.deepEqual(span.context().traceId, traceId); + assert.deepEqual(span.context().traceIdLow, traceIdLow); assert.deepEqual(span.context().spanId, spanId); assert.deepEqual(span.context().parentId, parentId); assert.equal(span.context().flags, flags); @@ -151,7 +151,7 @@ describe('tracer should', () => { startTime: startTime, }); - assert.equal(span.context().traceId, span.context().spanId); + assert.equal(span.context().traceIdLow, span.context().spanId); assert.isNotOk(span.context().parentId); assert.isOk(span.context().isSampled()); assert.equal(span._startTime, startTime); @@ -160,13 +160,13 @@ describe('tracer should', () => { describe('start a child span represented as a separate span from parent, using childOf and references', () => { let nextId = 0; const getId = () => Utils.encodeInt64(nextId++); - const traceId = getId(); + const traceIdLow = getId(); const flags = 1; - const parentContext = SpanContext.withBinaryIds(traceId, getId(), null, flags); - const childOfContext = SpanContext.withBinaryIds(traceId, getId(), null, flags); + const parentContext = SpanContext.withBinaryIds(traceIdLow, getId(), null, flags); + const childOfContext = SpanContext.withBinaryIds(traceIdLow, getId(), null, flags); const childOfRef = new opentracing.Reference(opentracing.REFERENCE_CHILD_OF, childOfContext); - const followsFromContext = SpanContext.withBinaryIds(traceId, getId(), null, flags); + const followsFromContext = SpanContext.withBinaryIds(traceIdLow, getId(), null, flags); const followsFromRef = new opentracing.Reference(opentracing.REFERENCE_FOLLOWS_FROM, followsFromContext); const testCases = [ @@ -264,6 +264,7 @@ describe('tracer should', () => { }; let savedContext = SpanContext.withBinaryIds( Utils.encodeInt64(1), + null, Utils.encodeInt64(2), Utils.encodeInt64(3), constants.SAMPLED_MASK, @@ -293,6 +294,7 @@ describe('tracer should', () => { }; let savedContext = SpanContext.withBinaryIds( Utils.encodeInt64(1), + null, Utils.encodeInt64(2), Utils.encodeInt64(3), constants.SAMPLED_MASK, @@ -418,6 +420,17 @@ describe('tracer should', () => { }); }); + it('start a root span with 128 bit traceId', () => { + let span = tracer.startSpan('test-name', { + traceId128bit: true, + }); + + assert.equal(span.context().traceIdLow, span.context().spanId); + assert.isOk(span.context().traceIdHigh); + assert.isNotOk(span.context().parentId); + assert.isOk(span.context().isSampled()); + }); + it('should NOT mutate tags', () => { const tags = { robot: 'bender', From 678561f46251f2830af9ed06f7d4676dcf483186 Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Fri, 26 Apr 2019 19:38:01 -0400 Subject: [PATCH 02/16] fix node 0.10 issue Signed-off-by: Paul Biccherai --- src/span_context.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/span_context.js b/src/span_context.js index 346befd10..be4e8731e 100644 --- a/src/span_context.js +++ b/src/span_context.js @@ -83,10 +83,11 @@ export default class SpanContext { if (this._traceIdLow == null && this._traceIdHigh == null && this._traceIdStr != null) { if (this._traceIdStr.length > 16) { const safeTraceIdStr = this._traceIdStr.length % 2 == 0 ? this._traceIdStr : '0' + this._traceIdStr; - const traceId = Buffer.from(safeTraceIdStr, 'hex'); + const traceId = new Buffer(safeTraceIdStr, 'hex'); this._traceIdHigh = traceId.slice(-16, -8); if (this._traceIdHigh.length != 8) { - const tmpBuffer = Buffer.alloc(8); + const tmpBuffer = new Buffer(8); + tmpBuffer.fill(0); this._traceIdHigh.copy(tmpBuffer, 8 - this._traceIdHigh.length); this._traceIdHigh = tmpBuffer; } From cd7cf056b9d9aba4f28bd9b38517d1e4405d258a Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Thu, 9 May 2019 11:21:29 -0400 Subject: [PATCH 03/16] Replace node latest to lts in the travis config Signed-off-by: Paul Biccherai --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index a06341e18..a3a30b7c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,8 @@ matrix: - docker - env: TEST_NODE_VERSION=6 LINT=1 COVER=1 - env: TEST_NODE_VERSION=4 + - env: TEST_NODE_VERSION=--lts + allow_failures: - env: TEST_NODE_VERSION=node env: From 0c23d6fb140928141d5d3416c9c2d383956cf3dd Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Thu, 9 May 2019 12:54:31 -0400 Subject: [PATCH 04/16] Fix travis file, adding back build for node latest Signed-off-by: Paul Biccherai --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a3a30b7c2..e8abda726 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ matrix: - env: TEST_NODE_VERSION=6 LINT=1 COVER=1 - env: TEST_NODE_VERSION=4 - env: TEST_NODE_VERSION=--lts + - env: TEST_NODE_VERSION=node allow_failures: - env: TEST_NODE_VERSION=node From 16e1d9156a8b81a8f7ab9ad71279cfd022405bf5 Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Wed, 15 May 2019 19:03:20 -0400 Subject: [PATCH 05/16] Refactoring traceId Signed-off-by: Paul Biccherai --- src/span_context.js | 77 ++++++++++++-------------------------------- src/thrift.js | 25 +++++++++++--- src/tracer.js | 8 ++--- test/span.js | 1 - test/span_context.js | 27 +++++++--------- test/thrift.js | 8 ++--- test/tracer.js | 56 ++++++++++++++++++++++++-------- 7 files changed, 102 insertions(+), 100 deletions(-) diff --git a/src/span_context.js b/src/span_context.js index be4e8731e..3451848fb 100644 --- a/src/span_context.js +++ b/src/span_context.js @@ -15,8 +15,7 @@ import * as constants from './constants.js'; import Utils from './util.js'; export default class SpanContext { - _traceIdLow: any; - _traceIdHigh: any; + _traceId: any; _spanId: any; _parentId: any; _traceIdStr: ?string; @@ -44,8 +43,7 @@ export default class SpanContext { _samplingFinalized: boolean; constructor( - traceIdLow: any, - traceIdHigh: any, + traceId: any, spanId: any, parentId: any, traceIdStr: ?string, @@ -56,8 +54,7 @@ export default class SpanContext { debugId: ?string = '', samplingFinalized: boolean = false ) { - this._traceIdLow = traceIdLow; - this._traceIdHigh = traceIdHigh; + this._traceId = traceId; this._spanId = spanId; this._parentId = parentId; this._traceIdStr = traceIdStr; @@ -69,33 +66,16 @@ export default class SpanContext { this._samplingFinalized = samplingFinalized; } - get traceIdLow(): any { - this._tryParseTraceIdStr(); - return this._traceIdLow; - } - - get traceIdHigh(): any { - this._tryParseTraceIdStr(); - return this._traceIdHigh; - } - - _tryParseTraceIdStr() { - if (this._traceIdLow == null && this._traceIdHigh == null && this._traceIdStr != null) { - if (this._traceIdStr.length > 16) { - const safeTraceIdStr = this._traceIdStr.length % 2 == 0 ? this._traceIdStr : '0' + this._traceIdStr; - const traceId = new Buffer(safeTraceIdStr, 'hex'); - this._traceIdHigh = traceId.slice(-16, -8); - if (this._traceIdHigh.length != 8) { - const tmpBuffer = new Buffer(8); - tmpBuffer.fill(0); - this._traceIdHigh.copy(tmpBuffer, 8 - this._traceIdHigh.length); - this._traceIdHigh = tmpBuffer; - } - this._traceIdLow = traceId.slice(-8); - } else { - this._traceIdLow = Utils.encodeInt64(this._traceIdStr); - } + get traceId(): any { + if (this._traceId == null && this._traceIdStr != null) { + const safeTraceIdStr = this._traceIdStr.length % 2 == 0 ? this._traceIdStr : '0' + this._traceIdStr; + const tmpBuffer = new Buffer(safeTraceIdStr, 'hex'); + const size = tmpBuffer.length > 8 ? 16 : 8; + this._traceId = new Buffer(size); + this._traceId.fill(0); + tmpBuffer.copy(this._traceId, size - tmpBuffer.length); } + return this._traceId; } get spanId(): any { @@ -113,14 +93,8 @@ export default class SpanContext { } get traceIdStr(): ?string { - if (this._traceIdStr == null && this._traceIdLow != null) { - if (this._traceIdHigh != null) { - this._traceIdStr = Utils.removeLeadingZeros( - this._traceIdHigh.toString('hex') + this._traceIdLow.toString('hex') - ); - } else { - this._traceIdStr = Utils.removeLeadingZeros(this._traceIdLow.toString('hex')); - } + if (this._traceIdStr == null && this._traceId != null) { + this._traceIdStr = Utils.removeLeadingZeros(this._traceId.toString('hex')); } return this._traceIdStr; } @@ -155,13 +129,8 @@ export default class SpanContext { return this._samplingFinalized; } - set traceIdLow(traceIdLow: Buffer): void { - this._traceIdLow = traceIdLow; - this._traceIdStr = null; - } - - set traceIdHigh(traceIdHigh: Buffer): void { - this._traceIdHigh = traceIdHigh; + set traceId(traceId: Buffer): void { + this._traceId = traceId; this._traceIdStr = null; } @@ -188,7 +157,7 @@ export default class SpanContext { } get isValid(): boolean { - return !!((this._traceIdLow || this._traceIdStr) && (this._spanId || this._spanIdStr)); + return !!((this._traceId || this._traceIdStr) && (this._spanId || this._spanIdStr)); } finalizeSampling(): void { @@ -217,8 +186,7 @@ export default class SpanContext { let newBaggage = Utils.clone(this._baggage); newBaggage[key] = value; return new SpanContext( - this._traceIdLow, - this._traceIdHigh, + this._traceId, this._spanId, this._parentId, this._traceIdStr, @@ -275,8 +243,7 @@ export default class SpanContext { } static withBinaryIds( - traceIdLow: any, - traceIdHigh: any, + traceId: any, spanId: any, parentId: any, flags: number, @@ -284,8 +251,7 @@ export default class SpanContext { debugId: ?string = '' ): SpanContext { return new SpanContext( - traceIdLow, - traceIdHigh, + traceId, spanId, parentId, null, // traceIdStr: string, @@ -306,8 +272,7 @@ export default class SpanContext { debugId: ?string = '' ): SpanContext { return new SpanContext( - null, // traceIdLow, - null, // traceIdHigh, + null, // traceId, null, // spanId, null, // parentId, traceIdStr, diff --git a/src/thrift.js b/src/thrift.js index bd1028261..1aa0a95f8 100644 --- a/src/thrift.js +++ b/src/thrift.js @@ -104,8 +104,8 @@ export default class ThriftUtils { thriftRefs.push({ refType: refEnum, - traceIdLow: context.traceIdLow, - traceIdHigh: context.traceIdHigh == null ? ThriftUtils.emptyBuffer : context.traceIdHigh, + traceIdLow: ThriftUtils.getTraceIdLow(context.traceId), + traceIdHigh: ThriftUtils.getTraceIdHigh(context.traceId), spanId: context.spanId, }); } @@ -113,15 +113,30 @@ export default class ThriftUtils { return thriftRefs; } + static getTraceIdLow(traceId: Buffer) { + if (traceId != null) { + return traceId.slice(-8); + } + + return ThriftUtils.emptyBuffer; + } + + static getTraceIdHigh(traceId: Buffer) { + if (traceId != null && traceId.length > 8) { + return traceId.slice(-16, -8); + } + + return ThriftUtils.emptyBuffer; + } + static spanToThrift(span: Span): any { let tags = ThriftUtils.getThriftTags(span._tags); let logs = ThriftUtils.getThriftLogs(span._logs); let unsigned = true; return { - traceIdLow: span._spanContext.traceIdLow, - traceIdHigh: - span._spanContext.traceIdHigh == null ? ThriftUtils.emptyBuffer : span._spanContext.traceIdHigh, + traceIdLow: ThriftUtils.getTraceIdLow(span._spanContext.traceId), + traceIdHigh: ThriftUtils.getTraceIdHigh(span._spanContext.traceId), spanId: span._spanContext.spanId, parentSpanId: span._spanContext.parentId || ThriftUtils.emptyBuffer, operationName: span._operationName, diff --git a/src/tracer.js b/src/tracer.js index 6aa0b671c..fecaba638 100644 --- a/src/tracer.js +++ b/src/tracer.js @@ -242,17 +242,15 @@ export default class Tracer { } if (options.traceId128bit) { - ctx.traceIdLow = randomId; - ctx.traceIdHigh = Utils.getRandom64(); + ctx.traceId = new Buffer.concat([Utils.getRandom64(), randomId]); } else { - ctx.traceIdLow = randomId; + ctx.traceId = randomId; } ctx.spanId = randomId; ctx.parentId = null; ctx.flags = flags; } else { - ctx.traceIdLow = parent.traceIdLow; - ctx.traceIdHigh = parent.traceIdHigh; + ctx.traceId = parent.traceId; ctx.spanId = Utils.getRandom64(); ctx.parentId = parent.spanId; ctx.flags = parent.flags; diff --git a/test/span.js b/test/span.js index 59665ffbe..779b64196 100644 --- a/test/span.js +++ b/test/span.js @@ -35,7 +35,6 @@ describe('span should', () => { spanContext = SpanContext.withBinaryIds( Utils.encodeInt64(1), - null, Utils.encodeInt64(2), Utils.encodeInt64(3), constants.SAMPLED_MASK diff --git a/test/span_context.js b/test/span_context.js index e051577d1..954d33c95 100644 --- a/test/span_context.js +++ b/test/span_context.js @@ -24,30 +24,28 @@ describe('SpanContext should', () => { }); it('return given values as they were set', () => { - let traceIdLow = Utils.encodeInt64(1); + let traceId = Utils.encodeInt64(1); let spanId = Utils.encodeInt64(2); let parentId = Utils.encodeInt64(3); let flags = 1; - let context = SpanContext.withBinaryIds(traceIdLow, null, spanId, parentId, flags); + let context = SpanContext.withBinaryIds(traceId, spanId, parentId, flags); - assert.deepEqual(traceIdLow, context.traceIdLow); + assert.deepEqual(traceId, context.traceId); assert.deepEqual(spanId, context.spanId); assert.deepEqual(parentId, context.parentId); assert.equal(flags, context.flags); }); it('return given values as they were set 128 bit', () => { - let traceIdLow = Utils.encodeInt64(1); - let traceIdHigh = Utils.encodeInt64(2); + let traceId = Buffer.concat([Utils.encodeInt64(2), Utils.encodeInt64(1)]); let spanId = Utils.encodeInt64(3); let parentId = Utils.encodeInt64(4); let flags = 1; - let context = SpanContext.withBinaryIds(traceIdLow, traceIdHigh, spanId, parentId, flags); + let context = SpanContext.withBinaryIds(traceId, spanId, parentId, flags); - assert.deepEqual(traceIdLow, context.traceIdLow); - assert.deepEqual(traceIdHigh, context.traceIdHigh); + assert.deepEqual(traceId, context.traceId); assert.deepEqual('20000000000000001', context.traceIdStr); assert.deepEqual(spanId, context.spanId); assert.deepEqual(parentId, context.parentId); @@ -57,7 +55,6 @@ describe('SpanContext should', () => { it('return IsSampled properly', () => { let context = SpanContext.withBinaryIds( Utils.encodeInt64(1), - null, Utils.encodeInt64(2), Utils.encodeInt64(3), 3 @@ -71,12 +68,11 @@ describe('SpanContext should', () => { }); it('format strings properly with toString', () => { - let ctx1 = SpanContext.withBinaryIds(Utils.encodeInt64(0x100), null, Utils.encodeInt64(0x7f), null, 1); + let ctx1 = SpanContext.withBinaryIds(Utils.encodeInt64(0x100), Utils.encodeInt64(0x7f), null, 1); assert.equal(ctx1.toString(), '100:7f:0:1'); let ctx2 = SpanContext.withBinaryIds( Utils.encodeInt64(255 << 4), - null, Utils.encodeInt64(127), Utils.encodeInt64(256), 0 @@ -84,7 +80,7 @@ describe('SpanContext should', () => { assert.equal(ctx2.toString(), 'ff0:7f:100:0'); // test large numbers - let ctx3 = SpanContext.withBinaryIds(LARGEST_64_BUFFER, null, LARGEST_64_BUFFER, LARGEST_64_BUFFER, 0); + let ctx3 = SpanContext.withBinaryIds(LARGEST_64_BUFFER, LARGEST_64_BUFFER, LARGEST_64_BUFFER, 0); assert.equal(ctx3.toString(), 'ffffffffffffffff:ffffffffffffffff:ffffffffffffffff:0'); assert.equal('ffffffffffffffff', ctx3.traceIdStr); assert.equal('ffffffffffffffff', ctx3.spanIdStr); @@ -95,6 +91,7 @@ describe('SpanContext should', () => { let context = SpanContext.fromString('100:7f:0:1'); assert.deepEqual('100', context.traceIdStr); + assert.deepEqual(Utils.encodeInt64(0x100), context.traceId); assert.deepEqual(Utils.encodeInt64(0x7f), context.spanId); assert.equal(null, context.parentId); assert.equal(1, context.flags); @@ -113,8 +110,7 @@ describe('SpanContext should', () => { let context = SpanContext.fromString('20000000000000100:7f:0:1'); assert.deepEqual('20000000000000100', context.traceIdStr); - assert.deepEqual(Utils.encodeInt64(0x100), context.traceIdLow); - assert.deepEqual(Utils.encodeInt64(0x2), context.traceIdHigh); + assert.deepEqual(Buffer.concat([Utils.encodeInt64(0x2), Utils.encodeInt64(0x100)]), context.traceId); assert.deepEqual(Utils.encodeInt64(0x7f), context.spanId); assert.equal(null, context.parentId); assert.equal(1, context.flags); @@ -123,8 +119,7 @@ describe('SpanContext should', () => { context = SpanContext.fromString('ffffffffffffffffffffffffffffffff:ffffffffffffffff:5:1'); assert.equal('ffffffffffffffffffffffffffffffff', context.traceIdStr); assert.equal('ffffffffffffffff', context.spanIdStr); - assert.deepEqual(LARGEST_64_BUFFER, context.traceIdLow); - assert.deepEqual(LARGEST_64_BUFFER, context.traceIdHigh); + assert.deepEqual(Buffer.concat([LARGEST_64_BUFFER, LARGEST_64_BUFFER]), context.traceId); assert.deepEqual(LARGEST_64_BUFFER, context.spanId); assert.deepEqual(LARGEST_64_BUFFER, context.spanId); assert.deepEqual(Utils.encodeInt64(0x5), context.parentId); diff --git a/test/thrift.js b/test/thrift.js index 3f1829130..47087b2ee 100644 --- a/test/thrift.js +++ b/test/thrift.js @@ -76,11 +76,11 @@ describe('ThriftUtils', () => { span.finish(); tracer.close(); let tSpan = ThriftUtils.spanToThrift(childSpan); - assert.deepEqual(tSpan.traceIdLow, childSpan.context().traceIdLow); - assert.deepEqual(tSpan.traceIdHigh, childSpan.context().traceIdHigh); + assert.deepEqual(tSpan.traceIdLow, childSpan.context().traceId.slice(-8)); + assert.deepEqual(tSpan.traceIdHigh, childSpan.context().traceId.slice(-16, -8)); assert.deepEqual(tSpan.spanId, childSpan.context().spanId); - assert.deepEqual(tSpan.references[0].traceIdLow, span.context().traceIdLow); - assert.deepEqual(tSpan.references[0].traceIdHigh, span.context().traceIdHigh); + assert.deepEqual(tSpan.references[0].traceIdLow, span.context().traceId.slice(-8)); + assert.deepEqual(tSpan.references[0].traceIdHigh, span.context().traceId.slice(-16, -8)); assert.deepEqual(tSpan.references[0].spanId, span.context().spanId); }); }); diff --git a/test/tracer.js b/test/tracer.js index 92fa77566..7659e7897 100644 --- a/test/tracer.js +++ b/test/tracer.js @@ -89,7 +89,7 @@ describe('tracer should', () => { let spanContext = tracer.extract(opentracing.FORMAT_TEXT_MAP, headers); let rootSpan = tracer.startSpan('fry', { childOf: spanContext }); - assert.isOk(rootSpan.context().traceIdLow); + assert.isOk(rootSpan.context().traceId); assert.isNotOk(rootSpan.context().parentId); assert.equal(rootSpan.context().flags, 1); assert.equal('Bender', rootSpan.getBaggageItem('robot')); @@ -98,11 +98,11 @@ describe('tracer should', () => { }); it('create a span correctly through _startInternalSpan', () => { - let traceIdLow = Utils.encodeInt64(1); + let traceId = Utils.encodeInt64(1); let spanId = Utils.encodeInt64(2); let parentId = Utils.encodeInt64(3); let flags = 1; - let context = SpanContext.withBinaryIds(traceIdLow, null, spanId, parentId, flags); + let context = SpanContext.withBinaryIds(traceId, spanId, parentId, flags); let start = 123.456; let rpcServer = false; let internalTags = []; @@ -122,7 +122,7 @@ describe('tracer should', () => { references ); - assert.deepEqual(span.context().traceIdLow, traceIdLow); + assert.deepEqual(span.context().traceId, traceId); assert.deepEqual(span.context().spanId, spanId); assert.deepEqual(span.context().parentId, parentId); assert.equal(span.context().flags, flags); @@ -151,7 +151,7 @@ describe('tracer should', () => { startTime: startTime, }); - assert.equal(span.context().traceIdLow, span.context().spanId); + assert.equal(span.context().traceId, span.context().spanId); assert.isNotOk(span.context().parentId); assert.isOk(span.context().isSampled()); assert.equal(span._startTime, startTime); @@ -160,13 +160,13 @@ describe('tracer should', () => { describe('start a child span represented as a separate span from parent, using childOf and references', () => { let nextId = 0; const getId = () => Utils.encodeInt64(nextId++); - const traceIdLow = getId(); + const traceId = getId(); const flags = 1; - const parentContext = SpanContext.withBinaryIds(traceIdLow, getId(), null, flags); - const childOfContext = SpanContext.withBinaryIds(traceIdLow, getId(), null, flags); + const parentContext = SpanContext.withBinaryIds(traceId, getId(), null, flags); + const childOfContext = SpanContext.withBinaryIds(traceId, getId(), null, flags); const childOfRef = new opentracing.Reference(opentracing.REFERENCE_CHILD_OF, childOfContext); - const followsFromContext = SpanContext.withBinaryIds(traceIdLow, getId(), null, flags); + const followsFromContext = SpanContext.withBinaryIds(traceId, getId(), null, flags); const followsFromRef = new opentracing.Reference(opentracing.REFERENCE_FOLLOWS_FROM, followsFromContext); const testCases = [ @@ -264,7 +264,38 @@ describe('tracer should', () => { }; let savedContext = SpanContext.withBinaryIds( Utils.encodeInt64(1), - null, + Utils.encodeInt64(2), + Utils.encodeInt64(3), + constants.SAMPLED_MASK, + baggage + ); + + let assertByFormat = format => { + let carrier = {}; + tracer.inject(savedContext, format, carrier); + let extractedContext = tracer.extract(format, carrier); + + assert.deepEqual(savedContext.traceId, extractedContext.traceId); + assert.deepEqual(savedContext.spanId, extractedContext.spanId); + assert.deepEqual(savedContext.parentId, extractedContext.parentId); + assert.equal(savedContext.flags, extractedContext.flags); + assert.equal(savedContext.baggage[keyOne], extractedContext.baggage[keyOne]); + assert.equal(savedContext.baggage[keyTwo], extractedContext.baggage[keyTwo]); + }; + + assertByFormat(opentracing.FORMAT_TEXT_MAP); + assertByFormat(opentracing.FORMAT_HTTP_HEADERS); + }); + + it('inject plain text headers into carrier, and extract span context with the same value 128bits', () => { + let keyOne = 'keyOne'; + let keyTwo = 'keyTwo'; + let baggage = { + keyOne: 'leela', + keyTwo: 'bender', + }; + let savedContext = SpanContext.withBinaryIds( + Buffer.concat([Utils.encodeInt64(1), Utils.encodeInt64(2)]), Utils.encodeInt64(2), Utils.encodeInt64(3), constants.SAMPLED_MASK, @@ -294,7 +325,6 @@ describe('tracer should', () => { }; let savedContext = SpanContext.withBinaryIds( Utils.encodeInt64(1), - null, Utils.encodeInt64(2), Utils.encodeInt64(3), constants.SAMPLED_MASK, @@ -425,8 +455,8 @@ describe('tracer should', () => { traceId128bit: true, }); - assert.equal(span.context().traceIdLow, span.context().spanId); - assert.isOk(span.context().traceIdHigh); + assert.deepEqual(span.context().traceId.slice(-8), span.context().spanId); + assert.isOk(span.context().traceId); assert.isNotOk(span.context().parentId); assert.isOk(span.context().isSampled()); }); From d5aa139cc9b12b6734ff1efb0d0556fc7a3c9bbe Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Thu, 16 May 2019 10:03:13 -0400 Subject: [PATCH 06/16] Adding unit test Signed-off-by: Paul Biccherai --- test/thrift.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/thrift.js b/test/thrift.js index 47087b2ee..ac5446044 100644 --- a/test/thrift.js +++ b/test/thrift.js @@ -83,4 +83,16 @@ describe('ThriftUtils', () => { assert.deepEqual(tSpan.references[0].traceIdHigh, span.context().traceId.slice(-16, -8)); assert.deepEqual(tSpan.references[0].spanId, span.context().spanId); }); + + it('should convert extract traceIdLow and traceIdHigh', () => { + let traceIdLow = Utils.encodeInt64(1); + let traceIdHigh = Utils.encodeInt64(2); + let traceId = Buffer.concat([traceIdHigh, traceIdLow]); + + assert.deepEqual(ThriftUtils.getTraceIdLow(traceId), traceIdLow); + assert.deepEqual(ThriftUtils.getTraceIdHigh(traceId), traceIdHigh); + + assert.deepEqual(ThriftUtils.getTraceIdLow(null), ThriftUtils.emptyBuffer); + assert.deepEqual(ThriftUtils.getTraceIdHigh(null), ThriftUtils.emptyBuffer); + }); }); From eb918d36bfe4fd6664c927eac9e6a97b2a0d2c59 Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Wed, 12 Jun 2019 19:13:09 -0400 Subject: [PATCH 07/16] Fixed issues Signed-off-by: Paul Biccherai --- src/span_context.js | 5 ++--- src/tracer.js | 4 ++-- src/util.js | 35 +++++++++++++++++++++++++++++++++++ test/span_context.js | 1 - test/util.js | 20 ++++++++++++++++++++ 5 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/span_context.js b/src/span_context.js index 3451848fb..45416c17e 100644 --- a/src/span_context.js +++ b/src/span_context.js @@ -69,10 +69,9 @@ export default class SpanContext { get traceId(): any { if (this._traceId == null && this._traceIdStr != null) { const safeTraceIdStr = this._traceIdStr.length % 2 == 0 ? this._traceIdStr : '0' + this._traceIdStr; - const tmpBuffer = new Buffer(safeTraceIdStr, 'hex'); + const tmpBuffer = Utils.newBuffer(safeTraceIdStr, 'hex'); const size = tmpBuffer.length > 8 ? 16 : 8; - this._traceId = new Buffer(size); - this._traceId.fill(0); + this._traceId = Utils.newBuffer(size); tmpBuffer.copy(this._traceId, size - tmpBuffer.length); } return this._traceId; diff --git a/src/tracer.js b/src/tracer.js index fecaba638..ff7306ca6 100644 --- a/src/tracer.js +++ b/src/tracer.js @@ -188,7 +188,7 @@ export default class Tracer { * the created Span object. The time should be specified in * milliseconds as Unix timestamp. Decimal value are supported * to represent time values with sub-millisecond accuracy. - * @param {number} [options.traceId128bit] - generate root span with a + * @param {boolean} [options.traceId128bit] - generate root span with a * 128bit traceId. * @return {Span} - a new Span object. **/ @@ -242,7 +242,7 @@ export default class Tracer { } if (options.traceId128bit) { - ctx.traceId = new Buffer.concat([Utils.getRandom64(), randomId]); + ctx.traceId = Buffer.concat([Utils.getRandom64(), randomId]); } else { ctx.traceId = randomId; } diff --git a/src/util.js b/src/util.js index a46594c17..bc8e98393 100644 --- a/src/util.js +++ b/src/util.js @@ -16,6 +16,17 @@ import Int64 from 'node-int64'; import os from 'os'; import http from 'http'; +type BufferEncoding = + | 'ascii' + | 'utf8' + | 'utf-8' + | 'utf16le' + | 'ucs2' + | 'ucs-2' + | 'base64' + | 'latin1' + | 'binary' + | 'hex'; export default class Utils { /** * Determines whether a string contains a given prefix. @@ -148,4 +159,28 @@ export default class Utils { error(err); }); } + + /** + * @param {string|number} input - a string to store in the buffer + * or a number of octets to allocate. + * @param {string} encoding - identifies the character encoding. Default is 'utf8'. + * @return {Buffer} - returns a buffer representing the encoded string, or an empty buffer. + **/ + static newBuffer(input: string | number, encoding?: BufferEncoding): Buffer { + if (typeof input === 'string') { + if (Buffer.from && Buffer.from !== Uint8Array.from) { + return Buffer.from(input, encoding); + } + return new Buffer(input, encoding); + } else if (typeof input === 'number') { + if (Buffer.alloc) { + return Buffer.alloc(input); + } + const buffer = new Buffer(input); + buffer.fill(0); + return buffer; + } else { + throw new Error('The "input" argument must be a number or a string'); + } + } } diff --git a/test/span_context.js b/test/span_context.js index 954d33c95..a1ba592d0 100644 --- a/test/span_context.js +++ b/test/span_context.js @@ -121,7 +121,6 @@ describe('SpanContext should', () => { assert.equal('ffffffffffffffff', context.spanIdStr); assert.deepEqual(Buffer.concat([LARGEST_64_BUFFER, LARGEST_64_BUFFER]), context.traceId); assert.deepEqual(LARGEST_64_BUFFER, context.spanId); - assert.deepEqual(LARGEST_64_BUFFER, context.spanId); assert.deepEqual(Utils.encodeInt64(0x5), context.parentId); assert.equal(context.flags, 0x1); }); diff --git a/test/util.js b/test/util.js index d6d077de1..8eb99969c 100644 --- a/test/util.js +++ b/test/util.js @@ -44,4 +44,24 @@ describe('utils', () => { ]; assert.deepEqual(expectedTags, results); }); + + it('should create new empty buffer', () => { + let results = Utils.newBuffer(8); + assert.isNotNull(results); + assert.equal(results.length, 8); + assert.deepEqual(new Buffer([0, 0, 0, 0, 0, 0, 0, 0]), results); + }); + + it('should create new buffer from text', () => { + let expectedValue = 'test'; + let results = Utils.newBuffer(expectedValue, 'utf-8'); + assert.isNotNull(results); + assert.equal(expectedValue, results.toString('utf-8')); + }); + + it('should fail to create new buffer', () => { + assert.throw(() => { + Utils.newBuffer((undefined: any)); + }, 'The "input" argument must be a number or a string'); + }); }); From 4ec48e6f199b0e55a149eef97fa3ec5689d2a4ce Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Mon, 17 Jun 2019 16:32:34 -0400 Subject: [PATCH 08/16] Force travis to use ubuntu trusty Signed-off-by: Paul Biccherai --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 65350386b..c63ddb955 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ sudo: required +dist: trusty # Travis will use .nvmrc, by default language: node_js From a00710ef336e049a85daf0f9861b854cfa1c37c1 Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Mon, 24 Jun 2019 19:28:32 -0400 Subject: [PATCH 09/16] Fix issues Signed-off-by: Paul Biccherai --- src/_flow/tracer.js | 1 - src/span_context.js | 4 ++- src/tracer.js | 17 +++++++---- src/util.js | 69 +++++++++++++++++++++++++-------------------- test/thrift.js | 4 +-- test/tracer.js | 5 ++-- test/util.js | 14 +++------ 7 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/_flow/tracer.js b/src/_flow/tracer.js index 4d4f9d666..4334332e4 100644 --- a/src/_flow/tracer.js +++ b/src/_flow/tracer.js @@ -20,5 +20,4 @@ declare type startSpanOptions = { references?: Array, tags?: any, startTime?: number, - traceId128bit?: boolean, }; diff --git a/src/span_context.js b/src/span_context.js index 45416c17e..bed019376 100644 --- a/src/span_context.js +++ b/src/span_context.js @@ -68,8 +68,10 @@ export default class SpanContext { get traceId(): any { if (this._traceId == null && this._traceIdStr != null) { + // make sure that the HEX has an even number of digits, node is expecting 2 HEX character per byte + // https://github.com/nodejs/node/issues/21242 const safeTraceIdStr = this._traceIdStr.length % 2 == 0 ? this._traceIdStr : '0' + this._traceIdStr; - const tmpBuffer = Utils.newBuffer(safeTraceIdStr, 'hex'); + const tmpBuffer = Utils.newBufferFromHex(safeTraceIdStr); const size = tmpBuffer.length > 8 ? 16 : 8; this._traceId = Utils.newBuffer(size); tmpBuffer.copy(this._traceId, size - tmpBuffer.length); diff --git a/src/tracer.js b/src/tracer.js index ff7306ca6..ad10b1186 100644 --- a/src/tracer.js +++ b/src/tracer.js @@ -43,6 +43,7 @@ export default class Tracer { _baggageSetter: BaggageSetter; _debugThrottler: Throttler & ProcessSetter; _process: Process; + _traceId128bit: boolean; /** * @param {String} [serviceName] - name of the current service or application. @@ -56,6 +57,7 @@ export default class Tracer { * @param {Object} [options.baggageRestrictionManager] - a baggageRestrictionManager matching * @param {Object} [options.contextKey] - a name of the key to extract/inject context from headers * @param {Object} [options.baggagePrefix] - a name of the context baggage key prefix + * @param {boolean} [options.traceId128bit] - generate root span with a 128bit traceId. * BaggageRestrictionManager API from ./baggage.js. */ constructor( @@ -115,6 +117,8 @@ export default class Tracer { this._debugThrottler.setProcess(this._process); // TODO update reporter to implement ProcessSetter this._reporter.setProcess(this._process.serviceName, this._process.tags); + + this._traceId128bit = options.traceId128bit; } _startInternalSpan( @@ -188,8 +192,6 @@ export default class Tracer { * the created Span object. The time should be specified in * milliseconds as Unix timestamp. Decimal value are supported * to represent time values with sub-millisecond accuracy. - * @param {boolean} [options.traceId128bit] - generate root span with a - * 128bit traceId. * @return {Span} - a new Span object. **/ startSpan(operationName: string, options: ?startSpanOptions): Span { @@ -226,7 +228,6 @@ export default class Tracer { let ctx: SpanContext = new SpanContext(); let internalTags: any = {}; if (!parent || !parent.isValid) { - let randomId = Utils.getRandom64(); let flags = 0; if (this._sampler.isSampled(operationName, internalTags)) { flags |= constants.SAMPLED_MASK; @@ -241,12 +242,16 @@ export default class Tracer { ctx.baggage = parent.baggage; } - if (options.traceId128bit) { - ctx.traceId = Buffer.concat([Utils.getRandom64(), randomId]); + if (this._traceId128bit) { + let randomId = Utils.getRandom128(); + ctx.traceId = randomId; + ctx.spanId = randomId.slice(-8); } else { + let randomId = Utils.getRandom64(); ctx.traceId = randomId; + ctx.spanId = randomId; } - ctx.spanId = randomId; + ctx.parentId = null; ctx.flags = flags; } else { diff --git a/src/util.js b/src/util.js index bc8e98393..6bbc520ec 100644 --- a/src/util.js +++ b/src/util.js @@ -16,17 +16,6 @@ import Int64 from 'node-int64'; import os from 'os'; import http from 'http'; -type BufferEncoding = - | 'ascii' - | 'utf8' - | 'utf-8' - | 'utf16le' - | 'ucs2' - | 'ucs-2' - | 'base64' - | 'latin1' - | 'binary' - | 'hex'; export default class Utils { /** * Determines whether a string contains a given prefix. @@ -53,7 +42,7 @@ export default class Utils { } /** - * Determines whether a string contains a given prefix. + * Get a random buffer representing a random 64 bit. * * @return {Buffer} - returns a buffer representing a random 64 bit * number. @@ -66,6 +55,23 @@ export default class Utils { return buf; } + /** + * Get a random buffer representing a random 128 bit. + * + * @return {Buffer} - returns a buffer representing a random 128 bit + * number. + **/ + static getRandom128(): Buffer { + let randint1 = xorshift.randomint(); + let randint2 = xorshift.randomint(); + let buf = new Buffer(16); + buf.writeUInt32BE(randint1[0], 0); + buf.writeUInt32BE(randint1[1], 4); + buf.writeUInt32BE(randint2[0], 8); + buf.writeUInt32BE(randint2[1], 12); + return buf; + } + /** * @param {string|number} numberValue - a string or number to be encoded * as a 64 bit byte array. @@ -161,26 +167,27 @@ export default class Utils { } /** - * @param {string|number} input - a string to store in the buffer - * or a number of octets to allocate. - * @param {string} encoding - identifies the character encoding. Default is 'utf8'. - * @return {Buffer} - returns a buffer representing the encoded string, or an empty buffer. + * @param {string|number} input - a hex encoded string to store in the buffer. + * @return {Buffer} - returns a buffer representing the hex encoded string. **/ - static newBuffer(input: string | number, encoding?: BufferEncoding): Buffer { - if (typeof input === 'string') { - if (Buffer.from && Buffer.from !== Uint8Array.from) { - return Buffer.from(input, encoding); - } - return new Buffer(input, encoding); - } else if (typeof input === 'number') { - if (Buffer.alloc) { - return Buffer.alloc(input); - } - const buffer = new Buffer(input); - buffer.fill(0); - return buffer; - } else { - throw new Error('The "input" argument must be a number or a string'); + static newBufferFromHex(input: string): Buffer { + const encoding = 'hex'; + if (Buffer.from && Buffer.from !== Uint8Array.from) { + return Buffer.from(input, encoding); + } + return new Buffer(input, encoding); + } + + /** + * @param {number} input - a number of octets to allocate. + * @return {Buffer} - returns an empty buffer. + **/ + static newBuffer(size: number): Buffer { + if (Buffer.alloc) { + return Buffer.alloc(size); } + const buffer = new Buffer(size); + buffer.fill(0); + return buffer; } } diff --git a/test/thrift.js b/test/thrift.js index ac5446044..68b80499a 100644 --- a/test/thrift.js +++ b/test/thrift.js @@ -68,8 +68,8 @@ describe('ThriftUtils', () => { it('should convert span with 128 bit traceId', () => { let reporter = new InMemoryReporter(); - let tracer = new Tracer('test-service-name', reporter, new ConstSampler(true)); - let span = tracer.startSpan('some operation', { traceId128bit: true }); + let tracer = new Tracer('test-service-name', reporter, new ConstSampler(true), { traceId128bit: true }); + let span = tracer.startSpan('some operation'); let childOfRef = new opentracing.Reference(opentracing.REFERENCE_CHILD_OF, span.context()); let childSpan = tracer.startSpan('some child operation', { references: [childOfRef] }); childSpan.finish(); diff --git a/test/tracer.js b/test/tracer.js index 7659e7897..f0814961b 100644 --- a/test/tracer.js +++ b/test/tracer.js @@ -451,9 +451,8 @@ describe('tracer should', () => { }); it('start a root span with 128 bit traceId', () => { - let span = tracer.startSpan('test-name', { - traceId128bit: true, - }); + tracer = new Tracer('test-service-name', reporter, new ConstSampler(true), { traceId128bit: true }); + let span = tracer.startSpan('test-name'); assert.deepEqual(span.context().traceId.slice(-8), span.context().spanId); assert.isOk(span.context().traceId); diff --git a/test/util.js b/test/util.js index 8eb99969c..63907832c 100644 --- a/test/util.js +++ b/test/util.js @@ -52,16 +52,10 @@ describe('utils', () => { assert.deepEqual(new Buffer([0, 0, 0, 0, 0, 0, 0, 0]), results); }); - it('should create new buffer from text', () => { - let expectedValue = 'test'; - let results = Utils.newBuffer(expectedValue, 'utf-8'); + it('should create new buffer from hex', () => { + let expectedValue = 'deadbeef'; + let results = Utils.newBufferFromHex(expectedValue); assert.isNotNull(results); - assert.equal(expectedValue, results.toString('utf-8')); - }); - - it('should fail to create new buffer', () => { - assert.throw(() => { - Utils.newBuffer((undefined: any)); - }, 'The "input" argument must be a number or a string'); + assert.equal(expectedValue, results.toString('hex')); }); }); From 5059f19a405ead67a6b2edf81765fa8b5b0dc77b Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Mon, 8 Jul 2019 18:46:25 -0400 Subject: [PATCH 10/16] Refactoring Signed-off-by: Paul Biccherai --- src/span_context.js | 16 ++++++++++------ src/util.js | 2 ++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/span_context.js b/src/span_context.js index bed019376..3b034949f 100644 --- a/src/span_context.js +++ b/src/span_context.js @@ -68,13 +68,17 @@ export default class SpanContext { get traceId(): any { if (this._traceId == null && this._traceIdStr != null) { - // make sure that the HEX has an even number of digits, node is expecting 2 HEX character per byte + // make sure that the string is 32 or 16 characters long to generate the + // corresponding 64 or 128 bits buffer by padding the start with zeros if necessary + // https://github.com/jaegertracing/jaeger/issues/1657 + // At the same time this will enforce that the HEX has an even number of digits, node is expecting 2 HEX characters per byte // https://github.com/nodejs/node/issues/21242 - const safeTraceIdStr = this._traceIdStr.length % 2 == 0 ? this._traceIdStr : '0' + this._traceIdStr; - const tmpBuffer = Utils.newBufferFromHex(safeTraceIdStr); - const size = tmpBuffer.length > 8 ? 16 : 8; - this._traceId = Utils.newBuffer(size); - tmpBuffer.copy(this._traceId, size - tmpBuffer.length); + const traceIdExactLength = this._traceIdStr.length > 16 ? 32 : 16; + const safeTraceIdStr = + this._traceIdStr.length === traceIdExactLength + ? this._traceIdStr + : this._traceIdStr.padStart(traceIdExactLength, '0'); + this._traceId = Utils.newBufferFromHex(safeTraceIdStr); } return this._traceId; } diff --git a/src/util.js b/src/util.js index 6bbc520ec..2ccf04dcb 100644 --- a/src/util.js +++ b/src/util.js @@ -172,6 +172,8 @@ export default class Utils { **/ static newBufferFromHex(input: string): Buffer { const encoding = 'hex'; + // check that 'Buffer.from' exists based on node's documentation + // https://nodejs.org/en/docs/guides/buffer-constructor-deprecation/#variant-3 if (Buffer.from && Buffer.from !== Uint8Array.from) { return Buffer.from(input, encoding); } From 69ac16c0a5ada6e63d46efc60fd202316ce47a4b Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Mon, 8 Jul 2019 20:10:52 -0400 Subject: [PATCH 11/16] Fix node 0.10 compatibility Signed-off-by: Paul Biccherai --- src/span_context.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/span_context.js b/src/span_context.js index 3b034949f..2eaaede6c 100644 --- a/src/span_context.js +++ b/src/span_context.js @@ -74,11 +74,14 @@ export default class SpanContext { // At the same time this will enforce that the HEX has an even number of digits, node is expecting 2 HEX characters per byte // https://github.com/nodejs/node/issues/21242 const traceIdExactLength = this._traceIdStr.length > 16 ? 32 : 16; - const safeTraceIdStr = - this._traceIdStr.length === traceIdExactLength - ? this._traceIdStr - : this._traceIdStr.padStart(traceIdExactLength, '0'); - this._traceId = Utils.newBufferFromHex(safeTraceIdStr); + if (this._traceIdStr.length === traceIdExactLength) { + this._traceId = Utils.newBufferFromHex(this._traceIdStr); + } else { + const paddings = ['0000000000000000', '00000000000000000000000000000000']; + const paddingIndex = traceIdExactLength === 16 ? 0 : 1; + const safeTraceIdStr = (paddings[paddingIndex] + this._traceIdStr).slice(-traceIdExactLength); + this._traceId = Utils.newBufferFromHex(safeTraceIdStr); + } } return this._traceId; } From 50b49feebc25cdc442c1c3d77483b0d8f31e4bcd Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Thu, 11 Jul 2019 19:33:36 -0400 Subject: [PATCH 12/16] Increase timeout on some unit tests Signed-off-by: Paul Biccherai --- src/util.js | 4 ++-- test/http_sender.js | 2 +- test/udp_sender.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util.js b/src/util.js index 2ccf04dcb..432334f5e 100644 --- a/src/util.js +++ b/src/util.js @@ -49,7 +49,7 @@ export default class Utils { **/ static getRandom64(): Buffer { let randint = xorshift.randomint(); - let buf = new Buffer(8); + let buf = this.newBuffer(8); buf.writeUInt32BE(randint[0], 0); buf.writeUInt32BE(randint[1], 4); return buf; @@ -64,7 +64,7 @@ export default class Utils { static getRandom128(): Buffer { let randint1 = xorshift.randomint(); let randint2 = xorshift.randomint(); - let buf = new Buffer(16); + let buf = this.newBuffer(16); buf.writeUInt32BE(randint1[0], 0); buf.writeUInt32BE(randint1[1], 4); buf.writeUInt32BE(randint2[0], 8); diff --git a/test/http_sender.js b/test/http_sender.js index eff9663f6..7016ad8aa 100644 --- a/test/http_sender.js +++ b/test/http_sender.js @@ -253,7 +253,7 @@ describe('http sender', () => { expect(err).to.have.string('error sending spans over HTTP: Error: getaddrinfo ENOTFOUND'); tracer.close(done); }); - }); + }).timeout(5000); it('should handle HTTPS collectors', done => { // Make it ignore the fact that our cert isn't valid. diff --git a/test/udp_sender.js b/test/udp_sender.js index f4686bfc3..88b34bb7b 100644 --- a/test/udp_sender.js +++ b/test/udp_sender.js @@ -273,4 +273,4 @@ describe('udp sender', () => { } }); }); -}); +}).timeout(5000); From cfd324c8c25c8edbf4eaadaf272df7fe0d8e59d4 Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Thu, 11 Jul 2019 19:49:41 -0400 Subject: [PATCH 13/16] Increase timeout on some unit tests Signed-off-by: Paul Biccherai --- test/udp_sender.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/udp_sender.js b/test/udp_sender.js index 88b34bb7b..2255e70ba 100644 --- a/test/udp_sender.js +++ b/test/udp_sender.js @@ -272,5 +272,5 @@ describe('udp sender', () => { tracer.close(done); } }); - }); -}).timeout(5000); + }).timeout(5000); +}); From 1f02e9cd1dde8464d7787f1461f13a20ee172545 Mon Sep 17 00:00:00 2001 From: Paul Biccherai Date: Wed, 4 Sep 2019 10:56:49 -0400 Subject: [PATCH 14/16] Small refactor Signed-off-by: Paul Biccherai --- src/span_context.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/span_context.js b/src/span_context.js index 656cd97a4..9c1654fc4 100644 --- a/src/span_context.js +++ b/src/span_context.js @@ -60,9 +60,8 @@ export default class SpanContext { if (this._traceIdStr.length === traceIdExactLength) { this._traceId = Utils.newBufferFromHex(this._traceIdStr); } else { - const paddings = ['0000000000000000', '00000000000000000000000000000000']; - const paddingIndex = traceIdExactLength === 16 ? 0 : 1; - const safeTraceIdStr = (paddings[paddingIndex] + this._traceIdStr).slice(-traceIdExactLength); + const padding = traceIdExactLength === 16 ? '0000000000000000' : '00000000000000000000000000000000'; + const safeTraceIdStr = (padding + this._traceIdStr).slice(-traceIdExactLength); this._traceId = Utils.newBufferFromHex(safeTraceIdStr); } } From eee5ab63f61c77451e5d781d7fd69fe065faff01 Mon Sep 17 00:00:00 2001 From: Yuri Shkuro Date: Sun, 8 Sep 2019 19:43:26 -0400 Subject: [PATCH 15/16] Add one more test; create Buffer via Utils Signed-off-by: Yuri Shkuro --- src/reporters/udp_sender.js | 7 ++++--- src/thrift.js | 2 +- test/tracer.js | 25 ++++++++++++++++++++++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/reporters/udp_sender.js b/src/reporters/udp_sender.js index 999b01677..30bd7edc0 100644 --- a/src/reporters/udp_sender.js +++ b/src/reporters/udp_sender.js @@ -15,8 +15,9 @@ import dgram from 'dgram'; import fs from 'fs'; import path from 'path'; import { Thrift } from 'thriftrw'; -import NullLogger from '../logger.js'; -import SenderUtils from './sender_utils.js'; +import NullLogger from '../logger'; +import SenderUtils from './sender_utils'; +import Utils from '../util'; const HOST = 'localhost'; const PORT = 6832; @@ -128,7 +129,7 @@ export default class UDPSender { } const bufferLen = this._totalSpanBytes + this._emitSpanBatchOverhead; - const thriftBuffer = new Buffer(bufferLen); + const thriftBuffer = Utils.newBuffer(bufferLen); const writeResult = this._agentThrift.Agent.emitBatch.argumentsMessageRW.writeInto( this._convertBatchToThriftMessage(), thriftBuffer, diff --git a/src/thrift.js b/src/thrift.js index 1aa0a95f8..6fe116b1e 100644 --- a/src/thrift.js +++ b/src/thrift.js @@ -22,7 +22,7 @@ export default class ThriftUtils { source: fs.readFileSync(path.join(__dirname, './jaeger-idl/thrift/jaeger.thrift'), 'ascii'), allowOptionalArguments: true, }); - static emptyBuffer: Buffer = new Buffer([0, 0, 0, 0, 0, 0, 0, 0]); + static emptyBuffer: Buffer = Utils.newBuffer(8).fill(0); static getThriftTags(initialTags: Array): Array { let thriftTags = []; diff --git a/test/tracer.js b/test/tracer.js index f924a9fd4..ed1a3ed3b 100644 --- a/test/tracer.js +++ b/test/tracer.js @@ -467,9 +467,28 @@ describe('tracer should', () => { let span = tracer.startSpan('test-name'); assert.deepEqual(span.context().traceId.slice(-8), span.context().spanId); - assert.isOk(span.context().traceId); - assert.isNotOk(span.context().parentId); - assert.isOk(span.context().isSampled()); + assert.equal(16, span.context().traceId.length); + }); + + it('preserve 64bit traceId even when in 128bit mode', () => { + // NB: because we currently trim leading zeros, this test is not as effective as it could be. + // But once https://github.com/jaegertracing/jaeger-client-node/issues/391 is fixed, this test + // will be more useful as it can catch regression. + tracer = new Tracer('test-service-name', reporter, new ConstSampler(true), { traceId128bit: true }); + let span = tracer.startSpan('test-name'); + assert.equal(16, span.context().traceId.length, 'new traces use 128bit IDs'); + + let parent = SpanContext.fromString('100:7f:0:1'); + assert.equal(8, parent.traceId.length, 'respect 64bit length'); + + let child = tracer.startSpan('test-name', { childOf: parent }); + assert.equal(8, child.context().traceId.length, 'preserve 64bit length'); + + let carrier = {}; + tracer.inject(child.context(), opentracing.FORMAT_TEXT_MAP, carrier); + // Once https://github.com/jaegertracing/jaeger-client-node/issues/391 is fixed, the following + // asset will fail and will need to be changed to compare against '0000000000000100' string. + assert.equal('100:', carrier['uber-trace-id'].substring(0, 4), 'preserve 64bit length'); }); it('should NOT mutate tags', () => { From fb29d6936db1a108347a433cce3006ce948a9071 Mon Sep 17 00:00:00 2001 From: Yuri Shkuro Date: Sun, 8 Sep 2019 20:27:50 -0400 Subject: [PATCH 16/16] Avoid Buffer.fill() which is void on Node 0.10 Signed-off-by: Yuri Shkuro --- src/thrift.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thrift.js b/src/thrift.js index 6fe116b1e..0769808a7 100644 --- a/src/thrift.js +++ b/src/thrift.js @@ -22,7 +22,7 @@ export default class ThriftUtils { source: fs.readFileSync(path.join(__dirname, './jaeger-idl/thrift/jaeger.thrift'), 'ascii'), allowOptionalArguments: true, }); - static emptyBuffer: Buffer = Utils.newBuffer(8).fill(0); + static emptyBuffer: Buffer = Utils.newBuffer(8); static getThriftTags(initialTags: Array): Array { let thriftTags = [];