Skip to content

Commit b7d6e74

Browse files
EdZoudyladan
andauthored
Feat: Make ID generator configurable (#1331)
Co-authored-by: Daniel Dyla <[email protected]>
1 parent 023541e commit b7d6e74

File tree

11 files changed

+143
-94
lines changed

11 files changed

+143
-94
lines changed

packages/opentelemetry-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@ export * from './trace/sampler/ParentOrElseSampler';
3535
export * from './trace/sampler/ProbabilitySampler';
3636
export * from './trace/spancontext-utils';
3737
export * from './trace/TraceState';
38+
export * from './trace/IdGenerator';
3839
export * from './utils/url';
3940
export * from './utils/wrap';
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { IdGenerator } from '../../trace/IdGenerator';
17+
18+
type WindowWithMsCrypto = Window & {
19+
msCrypto?: Crypto;
20+
};
21+
const cryptoLib = window.crypto || (window as WindowWithMsCrypto).msCrypto;
22+
const SPAN_ID_BYTES = 8;
23+
const TRACE_ID_BYTES = 16;
24+
const randomBytesArray = new Uint8Array(TRACE_ID_BYTES);
25+
26+
export class RandomIdGenerator implements IdGenerator {
27+
/**
28+
* Returns a random 16-byte trace ID formatted/encoded as a 32 lowercase hex
29+
* characters corresponding to 128 bits.
30+
*/
31+
generateTraceId(): string {
32+
cryptoLib.getRandomValues(randomBytesArray);
33+
return this.toHex(randomBytesArray.slice(0, TRACE_ID_BYTES));
34+
}
35+
36+
/**
37+
* Returns a random 8-byte span ID formatted/encoded as a 16 lowercase hex
38+
* characters corresponding to 64 bits.
39+
*/
40+
generateSpanId(): string {
41+
cryptoLib.getRandomValues(randomBytesArray);
42+
return this.toHex(randomBytesArray.slice(0, SPAN_ID_BYTES));
43+
}
44+
45+
/**
46+
* Get the hex string representation of a byte array
47+
*
48+
* @param byteArray
49+
*/
50+
private toHex(byteArray: Uint8Array) {
51+
const chars: number[] = new Array(byteArray.length * 2);
52+
const alpha = 'a'.charCodeAt(0) - 10;
53+
const digit = '0'.charCodeAt(0);
54+
55+
let p = 0;
56+
for (let i = 0; i < byteArray.length; i++) {
57+
let nibble = (byteArray[i] >>> 4) & 0xf;
58+
chars[p++] = nibble > 9 ? nibble + alpha : nibble + digit;
59+
nibble = byteArray[i] & 0xf;
60+
chars[p++] = nibble > 9 ? nibble + alpha : nibble + digit;
61+
}
62+
return String.fromCharCode.apply(null, chars);
63+
}
64+
}

packages/opentelemetry-core/src/platform/browser/id.ts

Lines changed: 0 additions & 56 deletions
This file was deleted.

packages/opentelemetry-core/src/platform/browser/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
export * from './BasePlugin';
1818
export * from './environment';
1919
export * from './hex-to-base64';
20-
export * from './id';
20+
export * from './RandomIdGenerator';
2121
export * from './performance';
2222
export * from './sdk-info';
2323
export * from './timer-util';

packages/opentelemetry-core/src/platform/node/id.ts renamed to packages/opentelemetry-core/src/platform/node/RandomIdGenerator.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,25 @@
1515
*/
1616

1717
import * as crypto from 'crypto';
18+
import { IdGenerator } from '../../trace/IdGenerator';
1819

1920
const SPAN_ID_BYTES = 8;
2021
const TRACE_ID_BYTES = 16;
2122

22-
/**
23-
* Returns a random 16-byte trace ID formatted/encoded as a 32 lowercase hex
24-
* characters corresponding to 128 bits.
25-
*/
26-
export function randomTraceId(): string {
27-
return crypto.randomBytes(TRACE_ID_BYTES).toString('hex');
28-
}
23+
export class RandomIdGenerator implements IdGenerator {
24+
/**
25+
* Returns a random 16-byte trace ID formatted/encoded as a 32 lowercase hex
26+
* characters corresponding to 128 bits.
27+
*/
28+
generateTraceId(): string {
29+
return crypto.randomBytes(TRACE_ID_BYTES).toString('hex');
30+
}
2931

30-
/**
31-
* Returns a random 8-byte span ID formatted/encoded as a 16 lowercase hex
32-
* characters corresponding to 64 bits.
33-
*/
34-
export function randomSpanId(): string {
35-
return crypto.randomBytes(SPAN_ID_BYTES).toString('hex');
32+
/**
33+
* Returns a random 8-byte span ID formatted/encoded as a 16 lowercase hex
34+
* characters corresponding to 64 bits.
35+
*/
36+
generateSpanId(): string {
37+
return crypto.randomBytes(SPAN_ID_BYTES).toString('hex');
38+
}
3639
}

packages/opentelemetry-core/src/platform/node/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
export * from './BasePlugin';
1818
export * from './environment';
1919
export * from './hex-to-base64';
20-
export * from './id';
20+
export * from './RandomIdGenerator';
2121
export * from './performance';
2222
export * from './sdk-info';
2323
export * from './timer-util';
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/** IdGenerator provides an interface for generating Trace Id and Span Id */
18+
export interface IdGenerator {
19+
/** Returns a trace ID composed of 32 lowercase hex characters. */
20+
generateTraceId(): string;
21+
/** Returns a span ID composed of 16 lowercase hex characters. */
22+
generateSpanId(): string;
23+
}

packages/opentelemetry-core/test/context/composite.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ import * as assert from 'assert';
2525
import {
2626
CompositePropagator,
2727
HttpTraceContext,
28-
randomSpanId,
29-
randomTraceId,
28+
RandomIdGenerator,
3029
} from '../../src';
3130
import {
3231
getExtractedSpanContext,
@@ -49,8 +48,9 @@ describe('Composite Propagator', () => {
4948
let spanId: string;
5049

5150
beforeEach(() => {
52-
traceId = randomTraceId();
53-
spanId = randomSpanId();
51+
const idGenerator = new RandomIdGenerator();
52+
traceId = idGenerator.generateTraceId();
53+
spanId = idGenerator.generateSpanId();
5454
});
5555

5656
describe('inject', () => {

packages/opentelemetry-core/test/platform/id.test.ts renamed to packages/opentelemetry-core/test/platform/RandomIdGenerator.test.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,41 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
1716
import * as assert from 'assert';
18-
import { randomSpanId, randomTraceId } from '../../src/platform';
17+
import { RandomIdGenerator } from '../../src/platform';
18+
19+
const idGenerator = new RandomIdGenerator();
1920

2021
describe('randomTraceId', () => {
22+
let traceId1: string, traceId2: string;
23+
beforeEach(() => {
24+
traceId1 = idGenerator.generateTraceId();
25+
traceId2 = idGenerator.generateTraceId();
26+
});
27+
2128
it('returns 32 character hex strings', () => {
22-
const traceId = randomTraceId();
23-
assert.ok(traceId.match(/[a-f0-9]{32}/));
24-
assert.ok(!traceId.match(/^0+$/));
29+
assert.ok(traceId1.match(/[a-f0-9]{32}/));
30+
assert.ok(!traceId1.match(/^0+$/));
2531
});
2632

2733
it('returns different ids on each call', () => {
28-
const traceId1 = randomTraceId();
29-
const traceId2 = randomTraceId();
30-
3134
assert.notDeepStrictEqual(traceId1, traceId2);
3235
});
3336
});
3437

3538
describe('randomSpanId', () => {
39+
let spanId1: string, spanId2: string;
40+
beforeEach(() => {
41+
spanId1 = idGenerator.generateSpanId();
42+
spanId2 = idGenerator.generateSpanId();
43+
});
44+
3645
it('returns 16 character hex strings', () => {
37-
const spanId = randomSpanId();
38-
assert.ok(spanId.match(/[a-f0-9]{16}/));
39-
assert.ok(!spanId.match(/^0+$/));
46+
assert.ok(spanId1.match(/[a-f0-9]{16}/));
47+
assert.ok(!spanId1.match(/^0+$/));
4048
});
4149

4250
it('returns different ids on each call', () => {
43-
const spanId1 = randomSpanId();
44-
const spanId2 = randomSpanId();
45-
4651
assert.notDeepStrictEqual(spanId1, spanId2);
4752
});
4853
});

packages/opentelemetry-tracing/src/Tracer.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ import {
2222
InstrumentationLibrary,
2323
isValid,
2424
NoRecordingSpan,
25-
randomSpanId,
26-
randomTraceId,
25+
IdGenerator,
26+
RandomIdGenerator,
2727
setActiveSpan,
2828
} from '@opentelemetry/core';
2929
import { Resource } from '@opentelemetry/resources';
@@ -38,6 +38,7 @@ import { mergeConfig } from './utility';
3838
export class Tracer implements api.Tracer {
3939
private readonly _sampler: api.Sampler;
4040
private readonly _traceParams: TraceParams;
41+
private readonly _idGenerator: IdGenerator;
4142
readonly resource: Resource;
4243
readonly instrumentationLibrary: InstrumentationLibrary;
4344
readonly logger: api.Logger;
@@ -53,6 +54,7 @@ export class Tracer implements api.Tracer {
5354
const localConfig = mergeConfig(config);
5455
this._sampler = localConfig.sampler;
5556
this._traceParams = localConfig.traceParams;
57+
this._idGenerator = config.idGenerator || new RandomIdGenerator();
5658
this.resource = _tracerProvider.resource;
5759
this.instrumentationLibrary = instrumentationLibrary;
5860
this.logger = config.logger || new ConsoleLogger(config.logLevel);
@@ -68,12 +70,12 @@ export class Tracer implements api.Tracer {
6870
context = api.context.active()
6971
): api.Span {
7072
const parentContext = getParent(options, context);
71-
const spanId = randomSpanId();
73+
const spanId = this._idGenerator.generateSpanId();
7274
let traceId;
7375
let traceState;
7476
if (!parentContext || !isValid(parentContext)) {
7577
// New root span.
76-
traceId = randomTraceId();
78+
traceId = this._idGenerator.generateTraceId();
7779
} else {
7880
// New child span.
7981
traceId = parentContext.traceId;

0 commit comments

Comments
 (0)