Skip to content

Commit 8a03d75

Browse files
committed
fix: move tracing config into a new module
This also deprecates the top-level `authorizationHeader` option in favour of one under `tracing`. This is because we won't be using the same header for metrics.
1 parent 856030f commit 8a03d75

File tree

7 files changed

+350
-204
lines changed

7 files changed

+350
-204
lines changed

packages/opentelemetry/README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ An [OpenTelemetry](https://opentelemetry.io/docs/what-is-opentelemetry/) client
2020
* [`options.authorizationHeader`](#optionsauthorizationheader)
2121
* [`options.tracing`](#optionstracing)
2222
* [`options.tracing.endpoint`](#optionstracingendpoint)
23+
* [`options.tracing.authorizationHeader`](#optionstracingauthorizationheader)
2324
* [`options.tracing.samplePercentage`](#optionstracingsamplepercentage)
2425
* [`OTEL_` environment variables](#otel_-environment-variables)
2526
* [Contributing](#contributing)
@@ -209,10 +210,7 @@ setupOpenTelemetry({
209210

210211
#### `options.authorizationHeader`
211212

212-
Set the [`Authorization` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) in requests to the OpenTelemetry collector. Defaults to `undefined`.
213-
214-
**Environment variable:** `OPENTELEMETRY_AUTHORIZATION_HEADER`<br/>
215-
**Option:** `authorizationHeader` (`String`)
213+
**Deprecated**. This will still work but has been replaced with [`options.tracing.authorizationHeader`](#optionstracingauthorizationheader), which is now the preferred way to set this option.
216214

217215
#### `options.tracing`
218216

@@ -225,6 +223,13 @@ A URL to send OpenTelemetry traces to. E.g. `http://localhost:4318/v1/traces`. D
225223
**Environment variable:** `OPENTELEMETRY_TRACING_ENDPOINT`<br/>
226224
**Option:** `tracing.endpoint` (`String`)
227225

226+
#### `options.tracing.authorizationHeader`
227+
228+
Set the [`Authorization` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) in requests to the OpenTelemetry tracing collector. Defaults to `undefined`.
229+
230+
**Environment variable:** `OPENTELEMETRY_AUTHORIZATION_HEADER`<br/>
231+
**Option:** `tracing.authorizationHeader` (`String`)
232+
228233
#### `options.tracing.samplePercentage`
229234

230235
The percentage of traces to send to the exporter. Defaults to `5` which means that 5% of traces will be exported.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const {
2+
OTLPTraceExporter
3+
} = require('@opentelemetry/exporter-trace-otlp-proto');
4+
const {
5+
NoopSpanProcessor,
6+
TraceIdRatioBasedSampler
7+
} = require('@opentelemetry/sdk-trace-base');
8+
const logger = require('@dotcom-reliability-kit/logger');
9+
const { TRACING_USER_AGENT } = require('./user-agents');
10+
11+
const DEFAULT_SAMPLE_PERCENTAGE = 5;
12+
13+
/**
14+
* @typedef {object} TracingOptions
15+
* @property {string} [authorizationHeader]
16+
* The HTTP `Authorization` header to send with OpenTelemetry tracing requests.
17+
* @property {string} [endpoint]
18+
* The URL to send OpenTelemetry trace segments to, for example http://localhost:4318/v1/traces.
19+
* @property {number} [samplePercentage]
20+
* What percentage of traces should be sent onto the collector.
21+
*/
22+
23+
/**
24+
* Create an OpenTelemetry tracing configuration.
25+
*
26+
* @param {TracingOptions} options
27+
* @returns {Partial<import('@opentelemetry/sdk-node').NodeSDKConfiguration>}
28+
*/
29+
exports.createTracingConfig = function createTracingConfig(options) {
30+
/** @type {Partial<import('@opentelemetry/sdk-node').NodeSDKConfiguration>} */
31+
const config = {};
32+
33+
// If we have an OpenTelemetry tracing endpoint then set it up,
34+
// otherwise we pass a noop span processor so that nothing is exported
35+
if (options?.endpoint) {
36+
const headers = {
37+
'user-agent': TRACING_USER_AGENT
38+
};
39+
if (options.authorizationHeader) {
40+
headers.authorization = options.authorizationHeader;
41+
}
42+
config.traceExporter = new OTLPTraceExporter({
43+
url: options.endpoint,
44+
headers
45+
});
46+
47+
// Sample traces
48+
let samplePercentage = DEFAULT_SAMPLE_PERCENTAGE;
49+
if (options.samplePercentage && !Number.isNaN(options.samplePercentage)) {
50+
samplePercentage = options.samplePercentage;
51+
}
52+
const sampleRatio = samplePercentage / 100;
53+
config.sampler = new TraceIdRatioBasedSampler(sampleRatio);
54+
55+
logger.info({
56+
event: 'OTEL_TRACE_STATUS',
57+
message: `OpenTelemetry tracing is enabled and exporting to endpoint ${options.endpoint}`,
58+
enabled: true,
59+
endpoint: options.endpoint,
60+
samplePercentage
61+
});
62+
} else {
63+
logger.warn({
64+
event: 'OTEL_TRACE_STATUS',
65+
message:
66+
'OpenTelemetry tracing is disabled because no tracing endpoint was set',
67+
enabled: false,
68+
endpoint: null
69+
});
70+
config.spanProcessor = new NoopSpanProcessor();
71+
}
72+
73+
return config;
74+
};

packages/opentelemetry/lib/index.js

Lines changed: 20 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,32 @@
11
const { createInstrumentationConfig } = require('./config/instrumentations');
22
const { createResourceConfig } = require('./config/resource');
3+
const { createTracingConfig } = require('./config/tracing');
34
const { diag, DiagLogLevel } = require('@opentelemetry/api');
45
const opentelemetrySDK = require('@opentelemetry/sdk-node');
5-
const {
6-
OTLPTraceExporter
7-
} = require('@opentelemetry/exporter-trace-otlp-proto');
8-
const {
9-
NoopSpanProcessor,
10-
TraceIdRatioBasedSampler
11-
} = require('@opentelemetry/sdk-trace-base');
126
const logger = require('@dotcom-reliability-kit/logger');
13-
const { TRACING_USER_AGENT } = require('./config/user-agents');
147

15-
const DEFAULT_SAMPLE_PERCENTAGE = 5;
8+
/**
9+
* @typedef {import('./config/tracing').TracingOptions} TracingOptions
10+
*/
1611

1712
/**
1813
* @typedef {object} Options
1914
* @property {string} [authorizationHeader]
20-
* The HTTP `Authorization` header to send with OpenTelemetry requests.
15+
* [DEPRECATED] The HTTP `Authorization` header to send with OpenTelemetry requests. Use `tracing.authorizationHeader` instead.
2116
* @property {TracingOptions} [tracing]
2217
* Configuration options for OpenTelemetry tracing.
2318
*/
2419

25-
/**
26-
* @typedef {object} TracingOptions
27-
* @property {string} endpoint
28-
* The URL to send OpenTelemetry trace segments to, for example http://localhost:4318/v1/traces.
29-
* @property {number} [samplePercentage]
30-
* What percentage of traces should be sent onto the collector.
31-
*/
32-
3320
/**
3421
* Set up OpenTelemetry tracing.
3522
*
3623
* @param {Options} [options]
3724
* OpenTelemetry configuration options.
3825
*/
39-
function setupOpenTelemetry({ authorizationHeader, tracing } = {}) {
26+
function setupOpenTelemetry({
27+
authorizationHeader,
28+
tracing: tracingOptions
29+
} = {}) {
4030
// We don't support using the built-in `OTEL_`-prefixed environment variables. We
4131
// do want to know when these are used, though, so that we can easily spot when
4232
// an app's use of these environment variables might be interfering.
@@ -59,54 +49,18 @@ function setupOpenTelemetry({ authorizationHeader, tracing } = {}) {
5949
DiagLogLevel.INFO
6050
);
6151

62-
// Construct the OpenTelemetry SDK configuration
63-
/** @type {opentelemetrySDK.NodeSDKConfiguration} */
64-
const openTelemetryConfig = {};
65-
openTelemetryConfig.instrumentations = createInstrumentationConfig();
66-
openTelemetryConfig.resource = createResourceConfig();
67-
68-
// If we have an OpenTelemetry tracing endpoint then set it up,
69-
// otherwise we pass a noop span processor so that nothing is exported
70-
if (tracing?.endpoint) {
71-
const headers = {
72-
'user-agent': TRACING_USER_AGENT
73-
};
74-
if (authorizationHeader) {
75-
headers.authorization = authorizationHeader;
76-
}
77-
openTelemetryConfig.traceExporter = new OTLPTraceExporter({
78-
url: tracing.endpoint,
79-
headers
80-
});
81-
82-
// Sample traces
83-
let samplePercentage = DEFAULT_SAMPLE_PERCENTAGE;
84-
if (tracing.samplePercentage && !Number.isNaN(tracing.samplePercentage)) {
85-
samplePercentage = tracing.samplePercentage;
86-
}
87-
const sampleRatio = samplePercentage / 100;
88-
openTelemetryConfig.sampler = new TraceIdRatioBasedSampler(sampleRatio);
89-
90-
logger.info({
91-
event: 'OTEL_TRACE_STATUS',
92-
message: `OpenTelemetry tracing is enabled and exporting to endpoint ${tracing.endpoint}`,
93-
enabled: true,
94-
endpoint: tracing.endpoint,
95-
samplePercentage
96-
});
97-
} else {
98-
logger.warn({
99-
event: 'OTEL_TRACE_STATUS',
100-
message:
101-
'OpenTelemetry tracing is disabled because no tracing endpoint was set',
102-
enabled: false,
103-
endpoint: null
104-
});
105-
openTelemetryConfig.spanProcessor = new NoopSpanProcessor();
106-
}
107-
10852
// Set up and start OpenTelemetry
109-
const sdk = new opentelemetrySDK.NodeSDK(openTelemetryConfig);
53+
const sdk = new opentelemetrySDK.NodeSDK({
54+
// Configurations we set regardless of whether we're using tracing
55+
instrumentations: createInstrumentationConfig(),
56+
resource: createResourceConfig(),
57+
58+
// Add tracing-specific configurations
59+
...createTracingConfig({
60+
authorizationHeader,
61+
...tracingOptions
62+
})
63+
});
11064
sdk.start();
11165
}
11266

packages/opentelemetry/setup.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const setupOpenTelemetry = require('./lib/index.js');
44
let tracing = undefined;
55
if (process.env.OPENTELEMETRY_TRACING_ENDPOINT) {
66
tracing = {
7+
authorizationHeader: process.env.OPENTELEMETRY_AUTHORIZATION_HEADER,
78
endpoint: process.env.OPENTELEMETRY_TRACING_ENDPOINT,
89
samplePercentage: process.env.OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE
910
? Number(process.env.OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE)
@@ -12,6 +13,5 @@ if (process.env.OPENTELEMETRY_TRACING_ENDPOINT) {
1213
}
1314

1415
setupOpenTelemetry({
15-
authorizationHeader: process.env.OPENTELEMETRY_AUTHORIZATION_HEADER,
1616
tracing
1717
});
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
jest.mock('@opentelemetry/exporter-trace-otlp-proto');
2+
jest.mock('@opentelemetry/sdk-trace-base');
3+
jest.mock('@dotcom-reliability-kit/logger');
4+
jest.mock('../../../../lib/config/user-agents', () => ({
5+
TRACING_USER_AGENT: 'mock-tracing-user-agent'
6+
}));
7+
8+
const logger = require('@dotcom-reliability-kit/logger');
9+
const {
10+
OTLPTraceExporter
11+
} = require('@opentelemetry/exporter-trace-otlp-proto');
12+
const {
13+
NoopSpanProcessor,
14+
TraceIdRatioBasedSampler
15+
} = require('@opentelemetry/sdk-trace-base');
16+
const { createTracingConfig } = require('../../../../lib/config/tracing');
17+
18+
describe('@dotcom-reliability-kit/opentelemetry/lib/config/tracing', () => {
19+
it('exports a function', () => {
20+
expect(typeof createTracingConfig).toBe('function');
21+
});
22+
23+
describe('createTracingConfig(options)', () => {
24+
let config;
25+
26+
beforeAll(() => {
27+
config = createTracingConfig({
28+
authorizationHeader: 'mock-auth-header',
29+
endpoint: 'mock-endpoint',
30+
samplePercentage: 10
31+
});
32+
});
33+
34+
it('creates a trace exporter', () => {
35+
expect(OTLPTraceExporter).toHaveBeenCalledTimes(1);
36+
expect(OTLPTraceExporter).toHaveBeenCalledWith({
37+
url: 'mock-endpoint',
38+
headers: {
39+
authorization: 'mock-auth-header',
40+
'user-agent': 'mock-tracing-user-agent'
41+
}
42+
});
43+
});
44+
45+
it('creates a ratio-based trace sampler', () => {
46+
expect(TraceIdRatioBasedSampler).toHaveBeenCalledTimes(1);
47+
expect(TraceIdRatioBasedSampler).toHaveBeenCalledWith(0.1);
48+
});
49+
50+
it('does not create a noop span processor', () => {
51+
expect(NoopSpanProcessor).toHaveBeenCalledTimes(0);
52+
});
53+
54+
it('logs that tracing is enabled', () => {
55+
expect(logger.info).toHaveBeenCalledWith({
56+
enabled: true,
57+
endpoint: 'mock-endpoint',
58+
event: 'OTEL_TRACE_STATUS',
59+
message:
60+
'OpenTelemetry tracing is enabled and exporting to endpoint mock-endpoint',
61+
samplePercentage: 10
62+
});
63+
});
64+
65+
it('returns the configuration', () => {
66+
expect(config).toEqual({
67+
traceExporter: OTLPTraceExporter.mock.instances[0],
68+
sampler: TraceIdRatioBasedSampler.mock.instances[0]
69+
});
70+
});
71+
72+
describe('when options.authorizationHeader is not defined', () => {
73+
beforeAll(() => {
74+
OTLPTraceExporter.mockClear();
75+
config = createTracingConfig({
76+
endpoint: 'mock-endpoint',
77+
samplePercentage: 10
78+
});
79+
});
80+
81+
it('creates a trace exporter without an authorization header', () => {
82+
expect(OTLPTraceExporter).toHaveBeenCalledTimes(1);
83+
expect(OTLPTraceExporter).toHaveBeenCalledWith({
84+
url: 'mock-endpoint',
85+
headers: {
86+
'user-agent': 'mock-tracing-user-agent'
87+
}
88+
});
89+
});
90+
});
91+
92+
describe('when options.samplePercentage is not defined', () => {
93+
beforeAll(() => {
94+
TraceIdRatioBasedSampler.mockClear();
95+
config = createTracingConfig({
96+
authorizationHeader: 'mock-auth-header',
97+
endpoint: 'mock-endpoint'
98+
});
99+
});
100+
101+
it('creates a ratio-based trace sampler with a default sample ratio', () => {
102+
expect(TraceIdRatioBasedSampler).toHaveBeenCalledTimes(1);
103+
expect(TraceIdRatioBasedSampler).toHaveBeenCalledWith(0.05);
104+
});
105+
});
106+
107+
describe('when options.endpoint is not defined', () => {
108+
beforeAll(() => {
109+
OTLPTraceExporter.mockClear();
110+
TraceIdRatioBasedSampler.mockClear();
111+
config = createTracingConfig({});
112+
});
113+
114+
it('does not creates a trace exporter', () => {
115+
expect(OTLPTraceExporter).toHaveBeenCalledTimes(0);
116+
});
117+
118+
it('does not create a ratio-based trace sampler', () => {
119+
expect(TraceIdRatioBasedSampler).toHaveBeenCalledTimes(0);
120+
});
121+
122+
it('creates a noop span processor', () => {
123+
expect(NoopSpanProcessor).toHaveBeenCalledTimes(1);
124+
expect(NoopSpanProcessor).toHaveBeenCalledWith();
125+
});
126+
127+
it('logs that tracing is disabled', () => {
128+
expect(logger.warn).toHaveBeenCalledWith({
129+
enabled: false,
130+
endpoint: null,
131+
event: 'OTEL_TRACE_STATUS',
132+
message:
133+
'OpenTelemetry tracing is disabled because no tracing endpoint was set'
134+
});
135+
});
136+
137+
it('returns the configuration', () => {
138+
expect(config).toEqual({
139+
spanProcessor: NoopSpanProcessor.mock.instances[0]
140+
});
141+
});
142+
});
143+
});
144+
});

0 commit comments

Comments
 (0)