diff --git a/packages/opencensus-propagation-jaeger/src/jaeger-format.ts b/packages/opencensus-propagation-jaeger/src/jaeger-format.ts index a42a4c7e3..baa05d638 100644 --- a/packages/opencensus-propagation-jaeger/src/jaeger-format.ts +++ b/packages/opencensus-propagation-jaeger/src/jaeger-format.ts @@ -22,7 +22,12 @@ import { } from '@opencensus/core'; import * as crypto from 'crypto'; import * as uuid from 'uuid'; -import { isValidSpanId, isValidTraceId } from './validators'; +import { + formatTraceId, + formatSpanId, + isValidSpanId, + isValidTraceId, +} from './validators'; // TRACER_STATE_HEADER_NAME is the header key used for a span's serialized // context. @@ -57,8 +62,8 @@ export class JaegerFormat implements Propagation { const tracerStateHeaderParts = tracerStateHeader.split(':'); if (tracerStateHeaderParts.length !== 4) return null; - const traceId = tracerStateHeaderParts[0]; - const spanId = tracerStateHeaderParts[1]; + const traceId = formatTraceId(tracerStateHeaderParts[0]); + const spanId = formatSpanId(tracerStateHeaderParts[1]); const jflags = Number( '0x' + (isNaN(Number(tracerStateHeaderParts[3])) diff --git a/packages/opencensus-propagation-jaeger/src/validators.ts b/packages/opencensus-propagation-jaeger/src/validators.ts index 3fbcb858f..72fc6176c 100644 --- a/packages/opencensus-propagation-jaeger/src/validators.ts +++ b/packages/opencensus-propagation-jaeger/src/validators.ts @@ -54,18 +54,25 @@ const compose = (...fns: ValidationFn[]): ValidationFn => { }; /** - * Determines if the given traceId is valid based on section 2.2.2.1 of the - * Trace Context spec. + * Compose a set of validation functions into a single validation call. + */ +const orCompose = (...fns: ValidationFn[]): ValidationFn => { + return (value: string) => { + return fns.reduce((isValid, fn) => isValid || fn(value), false); + }; +}; + +/** + * Determines if the given traceId is valid based on https://www.jaegertracing.io/docs/1.21/client-libraries/#value */ export const isValidTraceId = compose( isHex, isNotAllZeros, - isLength(32) + orCompose(isLength(32), isLength(16)) ); /** - * Determines if the given spanId is valid based on section 2.2.2.2 of the Trace - * Context spec. + * Determines if the given spanId is valid based on https://www.jaegertracing.io/docs/1.21/client-libraries/#value */ export const isValidSpanId = compose( isHex, @@ -74,10 +81,26 @@ export const isValidSpanId = compose( ); /** - * Determines if the given option is valid based on section 2.2.3 of the Trace - * Context spec. + * Determines if the given option is valid based on https://www.jaegertracing.io/docs/1.21/client-libraries/#value */ export const isValidOption = compose( isHex, isLength(2) ); + +/** + * Formats a traceId to 64Bit or 128Bit Hex and add leading zeroes + */ +export const formatTraceId = (id: string) => { + if (id.length > 16) { + return ('0000000000000000000000000000000' + id).substr(-32); + } + return ('000000000000000' + id).substr(-16); +}; + +/** + * Formats a spanId to 64Bit and add leading zeroes + */ +export const formatSpanId = (id: string) => { + return ('000000000000000' + id).substr(-16); +}; diff --git a/packages/opencensus-propagation-jaeger/test/test-jaeger-format.ts b/packages/opencensus-propagation-jaeger/test/test-jaeger-format.ts index a53df66da..3522cd0fa 100644 --- a/packages/opencensus-propagation-jaeger/test/test-jaeger-format.ts +++ b/packages/opencensus-propagation-jaeger/test/test-jaeger-format.ts @@ -62,6 +62,66 @@ describe('JaegerPropagation', () => { ); assert.deepStrictEqual(jaegerFormat.extract(getter), spanContext); }); + + it('should format traceId for 64Bit Hex id without leading zeros', () => { + const spanContext = jaegerFormat.generate(); + spanContext.traceId = '70c2f20bd65603bd'; + const getter = helperGetter( + `${spanContext.traceId}:${spanContext.spanId}::${spanContext.options}` + ); + assert.deepStrictEqual(jaegerFormat.extract(getter), spanContext); + }); + + it('should format traceId for 64Bit Hex id with leading zeros when needed', () => { + const spanContext = jaegerFormat.generate(); + spanContext.traceId = 'c2f20bd65603bd'; + const compareSpanContext = { ...spanContext }; + compareSpanContext.traceId = '00c2f20bd65603bd'; + const getter = helperGetter( + `${spanContext.traceId}:${spanContext.spanId}::${spanContext.options}` + ); + assert.deepStrictEqual(jaegerFormat.extract(getter), compareSpanContext); + }); + + it('should format traceId for 128Bit Hex id without leading zeros', () => { + const spanContext = jaegerFormat.generate(); + spanContext.traceId = '929985345ae64c35acddd590f13ffc82'; + const getter = helperGetter( + `${spanContext.traceId}:${spanContext.spanId}::${spanContext.options}` + ); + assert.deepStrictEqual(jaegerFormat.extract(getter), spanContext); + }); + + it('should format traceId for 128Bit Hex id with leading zeros when needed', () => { + const spanContext = jaegerFormat.generate(); + spanContext.traceId = '9985345ae64c35acddd590f13ffc82'; + const compareSpanContext = { ...spanContext }; + compareSpanContext.traceId = '009985345ae64c35acddd590f13ffc82'; + const getter = helperGetter( + `${spanContext.traceId}:${spanContext.spanId}::${spanContext.options}` + ); + assert.deepStrictEqual(jaegerFormat.extract(getter), compareSpanContext); + }); + }); + + it('should format spanId without leading zeros', () => { + const spanContext = jaegerFormat.generate(); + spanContext.spanId = '70c2f20bd65603bd'; + const getter = helperGetter( + `${spanContext.traceId}:${spanContext.spanId}::${spanContext.options}` + ); + assert.deepStrictEqual(jaegerFormat.extract(getter), spanContext); + }); + + it('should format spanId with leading zeros when needed', () => { + const spanContext = jaegerFormat.generate(); + spanContext.spanId = 'c2f20bd65603bd'; + const compareSpanContext = { ...spanContext }; + compareSpanContext.spanId = '00c2f20bd65603bd'; + const getter = helperGetter( + `${spanContext.traceId}:${spanContext.spanId}::${spanContext.options}` + ); + assert.deepStrictEqual(jaegerFormat.extract(getter), compareSpanContext); }); describe('inject', () => { @@ -104,6 +164,50 @@ describe('JaegerPropagation', () => { jaegerFormat.inject(setter, emptySpanContext); assert.deepStrictEqual(jaegerFormat.extract(getter), null); }); + + it('should inject spancontext with 64Bit traceID', () => { + const spanContext = { + traceId: '70c2f20bd65603bd', + spanId: '5ba4ceca5d0edd4c', + options: SAMPLED_VALUE, + }; + const headers: { [key: string]: string | string[] | undefined } = {}; + const setter: HeaderSetter = { + setHeader(name: string, value: string) { + headers[name] = value; + }, + }; + const getter: HeaderGetter = { + getHeader(name: string) { + return headers[name]; + }, + }; + + jaegerFormat.inject(setter, spanContext); + assert.deepStrictEqual(jaegerFormat.extract(getter), spanContext); + }); + + it('should inject spancontext with 128Bit traceID', () => { + const spanContext = { + traceId: '929985345ae64c35acddd590f13ffc82', + spanId: '5ba4ceca5d0edd4c', + options: SAMPLED_VALUE, + }; + const headers: { [key: string]: string | string[] | undefined } = {}; + const setter: HeaderSetter = { + setHeader(name: string, value: string) { + headers[name] = value; + }, + }; + const getter: HeaderGetter = { + getHeader(name: string) { + return headers[name]; + }, + }; + + jaegerFormat.inject(setter, spanContext); + assert.deepStrictEqual(jaegerFormat.extract(getter), spanContext); + }); }); // Same test as propagation-stackdriver.