Skip to content

Commit 3dc8979

Browse files
maryliagpichlermarctrentm
authored
feat(opentelemetry-configuration): parse of environment variables on configuration file (#5949)
Co-authored-by: Marc Pichler <[email protected]> Co-authored-by: Trent Mick <[email protected]>
1 parent 1db6285 commit 3dc8979

File tree

5 files changed

+576
-3
lines changed

5 files changed

+576
-3
lines changed

experimental/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2
1515
* feat(opentelemetry-configuration): add more attributes to config model [#5826](https://github.com/open-telemetry/opentelemetry-js/pull/5826) @maryliag
1616
* feat(opentelemetry-configuration): Parse of Configuration File [#5875](https://github.com/open-telemetry/opentelemetry-js/pull/5875) @maryliag
1717
* feat(opentelemetry-configuration): parse of array objects on configuration file [#5947](https://github.com/open-telemetry/opentelemetry-js/pull/5947) @maryliag
18+
* feat(opentelemetry-configuration): parse of environment variables on configuration file [#5947](https://github.com/open-telemetry/opentelemetry-js/pull/5947) @maryliag
1819

1920
### :bug: Bug Fixes
2021

experimental/packages/opentelemetry-configuration/src/utils.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616
import { diag } from '@opentelemetry/api';
17+
import { getStringFromEnv } from '@opentelemetry/core';
1718
import { inspect } from 'util';
1819

1920
/**
@@ -26,7 +27,7 @@ import { inspect } from 'util';
2627
* @returns {boolean} - The boolean value or `false` if the environment variable is unset empty, unset, or contains only whitespace.
2728
*/
2829
export function getBooleanFromConfigFile(value: unknown): boolean | undefined {
29-
const raw = String(value)?.trim().toLowerCase();
30+
const raw = envVariableSubstitution(value)?.trim().toLowerCase();
3031
if (raw === 'true') {
3132
return true;
3233
} else if (raw === 'false') {
@@ -77,7 +78,7 @@ export function getBooleanListFromConfigFile(
7778
* @returns {number | undefined} - The number value or `undefined`.
7879
*/
7980
export function getNumberFromConfigFile(value: unknown): number | undefined {
80-
const raw = String(value)?.trim();
81+
const raw = envVariableSubstitution(value)?.trim();
8182
if (raw == null || raw.trim() === '') {
8283
return undefined;
8384
}
@@ -127,7 +128,7 @@ export function getNumberListFromConfigFile(
127128
* @returns {string | undefined} - The string value or `undefined`.
128129
*/
129130
export function getStringFromConfigFile(value: unknown): string | undefined {
130-
const raw = String(value)?.trim();
131+
const raw = envVariableSubstitution(value)?.trim();
131132
if (value == null || raw === '') {
132133
return undefined;
133134
}
@@ -148,8 +149,28 @@ export function getStringFromConfigFile(value: unknown): string | undefined {
148149
export function getStringListFromConfigFile(
149150
value: unknown
150151
): string[] | undefined {
152+
value = envVariableSubstitution(value);
151153
return getStringFromConfigFile(value)
152154
?.split(',')
153155
.map(v => v.trim())
154156
.filter(s => s !== '');
155157
}
158+
159+
export function envVariableSubstitution(value: unknown): string | undefined {
160+
if (value == null) {
161+
return undefined;
162+
}
163+
164+
const matches = String(value).match(/\$\{[a-zA-Z0-9_:.-]*\}/g);
165+
if (matches) {
166+
let stringValue = String(value);
167+
for (const match of matches) {
168+
const v = match.substring(2, match.length - 1).split(':-');
169+
const defaultValue = v.length === 2 ? v[1] : '';
170+
const replacement = getStringFromEnv(v[0]) || defaultValue;
171+
stringValue = stringValue.replace(match, replacement);
172+
}
173+
return stringValue;
174+
}
175+
return String(value);
176+
}

experimental/packages/opentelemetry-configuration/test/ConfigProvider.test.ts

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,102 @@ const configFromFile: Configuration = {
245245
},
246246
};
247247

248+
const defaultConfigFromFileWithEnvVariables: Configuration = {
249+
disabled: false,
250+
log_level: DiagLogLevel.INFO,
251+
node_resource_detectors: ['all'],
252+
resource: {
253+
attributes: [
254+
{
255+
name: 'service.name',
256+
value: 'unknown_service',
257+
type: 'string',
258+
},
259+
],
260+
},
261+
attribute_limits: {
262+
attribute_count_limit: 128,
263+
},
264+
propagator: {
265+
composite: ['tracecontext', 'baggage'],
266+
composite_list: 'tracecontext,baggage',
267+
},
268+
tracer_provider: {
269+
processors: [
270+
{
271+
batch: {
272+
schedule_delay: 5000,
273+
export_timeout: 30000,
274+
max_queue_size: 2048,
275+
max_export_batch_size: 512,
276+
exporter: {
277+
otlp_http: {
278+
endpoint: 'http://localhost:4318/v1/traces',
279+
timeout: 10000,
280+
},
281+
},
282+
},
283+
},
284+
],
285+
limits: {
286+
attribute_count_limit: 128,
287+
event_count_limit: 128,
288+
link_count_limit: 128,
289+
event_attribute_count_limit: 128,
290+
link_attribute_count_limit: 128,
291+
},
292+
sampler: {
293+
parent_based: {
294+
root: 'always_on',
295+
remote_parent_sampled: 'always_on',
296+
remote_parent_not_sampled: 'always_off',
297+
local_parent_sampled: 'always_on',
298+
local_parent_not_sampled: 'always_off',
299+
},
300+
},
301+
},
302+
meter_provider: {
303+
readers: [
304+
{
305+
periodic: {
306+
interval: 60000,
307+
timeout: 30000,
308+
exporter: {
309+
otlp_http: {
310+
endpoint: 'http://localhost:4318/v1/metrics',
311+
timeout: 10000,
312+
temporality_preference: 'cumulative',
313+
default_histogram_aggregation: 'explicit_bucket_histogram',
314+
},
315+
},
316+
},
317+
},
318+
],
319+
exemplar_filter: 'trace_based',
320+
},
321+
logger_provider: {
322+
processors: [
323+
{
324+
batch: {
325+
schedule_delay: 1000,
326+
export_timeout: 30000,
327+
max_queue_size: 2048,
328+
max_export_batch_size: 512,
329+
exporter: {
330+
otlp_http: {
331+
endpoint: 'http://localhost:4318/v1/logs',
332+
timeout: 10000,
333+
},
334+
},
335+
},
336+
},
337+
],
338+
limits: {
339+
attribute_count_limit: 128,
340+
},
341+
},
342+
};
343+
248344
describe('ConfigProvider', function () {
249345
describe('get values from environment variables', function () {
250346
afterEach(function () {
@@ -602,6 +698,56 @@ describe('ConfigProvider', function () {
602698
afterEach(function () {
603699
delete process.env.OTEL_EXPERIMENTAL_CONFIG_FILE;
604700
delete process.env.OTEL_NODE_RESOURCE_DETECTORS;
701+
delete process.env.OTEL_SDK_DISABLED;
702+
delete process.env.OTEL_LOG_LEVEL;
703+
delete process.env.OTEL_SERVICE_NAME;
704+
delete process.env.OTEL_RESOURCE_ATTRIBUTES;
705+
delete process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT;
706+
delete process.env.OTEL_ATTRIBUTE_COUNT_LIMIT;
707+
delete process.env.OTEL_PROPAGATORS;
708+
delete process.env.OTEL_BSP_SCHEDULE_DELAY;
709+
delete process.env.OTEL_BSP_EXPORT_TIMEOUT;
710+
delete process.env.OTEL_BSP_MAX_QUEUE_SIZE;
711+
delete process.env.OTEL_BSP_MAX_EXPORT_BATCH_SIZE;
712+
delete process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;
713+
delete process.env.OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE;
714+
delete process.env.OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY;
715+
delete process.env.OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE;
716+
delete process.env.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION;
717+
delete process.env.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT;
718+
delete process.env.OTEL_EXPORTER_OTLP_TRACES_HEADERS;
719+
delete process.env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
720+
delete process.env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT;
721+
delete process.env.OTEL_SPAN_EVENT_COUNT_LIMIT;
722+
delete process.env.OTEL_SPAN_LINK_COUNT_LIMIT;
723+
delete process.env.OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT;
724+
delete process.env.OTEL_LINK_ATTRIBUTE_COUNT_LIMIT;
725+
delete process.env.OTEL_METRIC_EXPORT_INTERVAL;
726+
delete process.env.OTEL_METRIC_EXPORT_TIMEOUT;
727+
delete process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT;
728+
delete process.env.OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE;
729+
delete process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY;
730+
delete process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE;
731+
delete process.env.OTEL_EXPORTER_OTLP_METRICS_COMPRESSION;
732+
delete process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT;
733+
delete process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS;
734+
delete process.env.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE;
735+
delete process.env
736+
.OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION;
737+
delete process.env.OTEL_METRICS_EXEMPLAR_FILTER;
738+
delete process.env.OTEL_BLRP_SCHEDULE_DELAY;
739+
delete process.env.OTEL_BLRP_EXPORT_TIMEOUT;
740+
delete process.env.OTEL_BLRP_MAX_QUEUE_SIZE;
741+
delete process.env.OTEL_BLRP_MAX_EXPORT_BATCH_SIZE;
742+
delete process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;
743+
delete process.env.OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE;
744+
delete process.env.OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY;
745+
delete process.env.OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE;
746+
delete process.env.OTEL_EXPORTER_OTLP_LOGS_COMPRESSION;
747+
delete process.env.OTEL_EXPORTER_OTLP_LOGS_TIMEOUT;
748+
delete process.env.OTEL_EXPORTER_OTLP_LOGS_HEADERS;
749+
delete process.env.OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT;
750+
delete process.env.OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT;
605751
});
606752

607753
it('should initialize config with default values from valid config file', function () {
@@ -655,5 +801,94 @@ describe('ConfigProvider', function () {
655801
defaultConfig
656802
);
657803
});
804+
805+
it('should initialize config with config file that contains environment variables', function () {
806+
process.env.OTEL_EXPERIMENTAL_CONFIG_FILE =
807+
'test/fixtures/sdk-migration-config.yaml';
808+
process.env.OTEL_SDK_DISABLED = 'false';
809+
process.env.OTEL_LOG_LEVEL = 'debug';
810+
process.env.OTEL_SERVICE_NAME = 'custom-name';
811+
process.env.OTEL_RESOURCE_ATTRIBUTES = 'attributes';
812+
process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = '23';
813+
process.env.OTEL_ATTRIBUTE_COUNT_LIMIT = '7';
814+
process.env.OTEL_PROPAGATORS = 'prop';
815+
process.env.OTEL_BSP_SCHEDULE_DELAY = '123';
816+
process.env.OTEL_BSP_EXPORT_TIMEOUT = '456';
817+
process.env.OTEL_BSP_MAX_QUEUE_SIZE = '789';
818+
process.env.OTEL_BSP_MAX_EXPORT_BATCH_SIZE = '1011';
819+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'trace-endpoint';
820+
process.env.OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE = 'trace-certificate';
821+
process.env.OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY = 'trace-client-key';
822+
process.env.OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE =
823+
'trace-client-certificate';
824+
process.env.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = 'trace-compression';
825+
process.env.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT = '1213';
826+
process.env.OTEL_EXPORTER_OTLP_TRACES_HEADERS = 'trace-headers';
827+
process.env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT = '14';
828+
process.env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT = '15';
829+
process.env.OTEL_SPAN_EVENT_COUNT_LIMIT = '16';
830+
process.env.OTEL_SPAN_LINK_COUNT_LIMIT = '17';
831+
process.env.OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT = '18';
832+
process.env.OTEL_LINK_ATTRIBUTE_COUNT_LIMIT = '19';
833+
process.env.OTEL_METRIC_EXPORT_INTERVAL = '20';
834+
process.env.OTEL_METRIC_EXPORT_TIMEOUT = '21';
835+
process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'metric-endpoint';
836+
process.env.OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE = 'metric-certificate';
837+
process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY = 'metric-client-key';
838+
process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE =
839+
'metric-client-certificate';
840+
process.env.OTEL_EXPORTER_OTLP_METRICS_COMPRESSION = 'metric-compression';
841+
process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = '22';
842+
process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS = 'metric-header';
843+
process.env.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE =
844+
'metric-temporality';
845+
process.env.OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION =
846+
'metric-hist-agg';
847+
process.env.OTEL_METRICS_EXEMPLAR_FILTER = 'metric-exemplar-filter';
848+
process.env.OTEL_BLRP_SCHEDULE_DELAY = '23';
849+
process.env.OTEL_BLRP_EXPORT_TIMEOUT = '24';
850+
process.env.OTEL_BLRP_MAX_QUEUE_SIZE = '25';
851+
process.env.OTEL_BLRP_MAX_EXPORT_BATCH_SIZE = '26';
852+
process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'logs-endpoint';
853+
process.env.OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE = 'logs-certificate';
854+
process.env.OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY = 'logs-client-key';
855+
process.env.OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE =
856+
'logs-client-certificate';
857+
process.env.OTEL_EXPORTER_OTLP_LOGS_COMPRESSION = 'logs-compression';
858+
process.env.OTEL_EXPORTER_OTLP_LOGS_TIMEOUT = '27';
859+
process.env.OTEL_EXPORTER_OTLP_LOGS_HEADERS = 'logs-header';
860+
process.env.OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT = '28';
861+
process.env.OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT = '29';
862+
const configProvider = createConfigProvider();
863+
const expectedConfig: Configuration = {
864+
...defaultConfig,
865+
resource: {
866+
attributes_list: 'attributes',
867+
attributes: [
868+
{
869+
name: 'service.name',
870+
value: 'custom-name',
871+
type: 'string',
872+
},
873+
],
874+
},
875+
};
876+
877+
assert.deepStrictEqual(
878+
configProvider.getInstrumentationConfig(),
879+
expectedConfig
880+
);
881+
});
882+
883+
it('should initialize config with fallbacks defined in config file when corresponding environment variables are not defined', function () {
884+
process.env.OTEL_EXPERIMENTAL_CONFIG_FILE =
885+
'test/fixtures/sdk-migration-config.yaml';
886+
887+
const configProvider = createConfigProvider();
888+
assert.deepStrictEqual(
889+
configProvider.getInstrumentationConfig(),
890+
defaultConfigFromFileWithEnvVariables
891+
);
892+
});
658893
});
659894
});

0 commit comments

Comments
 (0)