@opentelemetry/configuration 0.215.0 → 0.217.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +122 -13
  2. package/build/src/ConfigFactory.js +3 -1
  3. package/build/src/ConfigFactory.js.map +1 -1
  4. package/build/src/EnvironmentConfigFactory.d.ts +1 -1
  5. package/build/src/EnvironmentConfigFactory.js +195 -307
  6. package/build/src/EnvironmentConfigFactory.js.map +1 -1
  7. package/build/src/FileConfigFactory.d.ts +2 -17
  8. package/build/src/FileConfigFactory.js +269 -1054
  9. package/build/src/FileConfigFactory.js.map +1 -1
  10. package/build/src/IConfigFactory.d.ts +2 -2
  11. package/build/src/IConfigFactory.js.map +1 -1
  12. package/build/src/generated/types.d.ts +1652 -0
  13. package/build/src/generated/types.js +13 -0
  14. package/build/src/generated/types.js.map +1 -0
  15. package/build/src/generated/validator.d.ts +17 -0
  16. package/build/src/generated/validator.js +8412 -0
  17. package/build/src/generated/validator.js.map +1 -0
  18. package/build/src/index.d.ts +1 -4
  19. package/build/src/index.js.map +1 -1
  20. package/build/src/utils.d.ts +6 -66
  21. package/build/src/utils.js +68 -151
  22. package/build/src/utils.js.map +1 -1
  23. package/build/src/version.d.ts +1 -1
  24. package/build/src/version.js +1 -1
  25. package/build/src/version.js.map +1 -1
  26. package/package.json +6 -3
  27. package/build/src/models/commonModel.d.ts +0 -183
  28. package/build/src/models/commonModel.js +0 -40
  29. package/build/src/models/commonModel.js.map +0 -1
  30. package/build/src/models/configModel.d.ts +0 -77
  31. package/build/src/models/configModel.js +0 -21
  32. package/build/src/models/configModel.js.map +0 -1
  33. package/build/src/models/loggerProviderModel.d.ts +0 -134
  34. package/build/src/models/loggerProviderModel.js +0 -18
  35. package/build/src/models/loggerProviderModel.js.map +0 -1
  36. package/build/src/models/meterProviderModel.d.ts +0 -459
  37. package/build/src/models/meterProviderModel.js +0 -50
  38. package/build/src/models/meterProviderModel.js.map +0 -1
  39. package/build/src/models/resourceModel.d.ts +0 -72
  40. package/build/src/models/resourceModel.js +0 -7
  41. package/build/src/models/resourceModel.js.map +0 -1
  42. package/build/src/models/tracerProviderModel.d.ts +0 -256
  43. package/build/src/models/tracerProviderModel.js +0 -30
  44. package/build/src/models/tracerProviderModel.js.map +0 -1
@@ -4,1119 +4,334 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.setLoggerProvider = exports.getSeverity = exports.setMeterProvider = exports.getTemporalityPreference = exports.setTracerProvider = exports.setPropagator = exports.setAttributeLimits = exports.setResourceAttributes = exports.parseConfigFile = exports.hasValidConfigFile = exports.FileConfigFactory = void 0;
7
+ exports.parseConfigFile = exports.FileConfigFactory = void 0;
8
8
  const core_1 = require("@opentelemetry/core");
9
- const configModel_1 = require("./models/configModel");
10
9
  const fs = require("fs");
11
10
  const yaml = require("yaml");
12
11
  const utils_1 = require("./utils");
13
- const commonModel_1 = require("./models/commonModel");
14
- const tracerProviderModel_1 = require("./models/tracerProviderModel");
15
- const loggerProviderModel_1 = require("./models/loggerProviderModel");
16
- const meterProviderModel_1 = require("./models/meterProviderModel");
17
- const api_1 = require("@opentelemetry/api");
12
+ // Pre-compiled ajv validator — eliminates runtime schema compilation on cold start.
13
+ // Generated by `npm run generate:config`; do not import ajv directly here.
14
+ const validateConfig = require("./generated/validator");
18
15
  class FileConfigFactory {
19
16
  _config;
20
17
  constructor() {
21
- this._config = (0, configModel_1.initializeDefaultConfiguration)();
22
- parseConfigFile(this._config);
18
+ this._config = parseConfigFile();
23
19
  }
24
20
  getConfigModel() {
25
21
  return this._config;
26
22
  }
27
23
  }
28
24
  exports.FileConfigFactory = FileConfigFactory;
29
- function hasValidConfigFile() {
30
- const configFile = (0, core_1.getStringFromEnv)('OTEL_CONFIG_FILE');
31
- if (configFile) {
32
- if (!(configFile.endsWith('.yaml') || configFile.endsWith('.yml')) ||
33
- !fs.existsSync(configFile)) {
34
- api_1.diag.warn(`Config file ${configFile} set on OTEL_CONFIG_FILE is not valid`);
35
- return false;
36
- }
37
- return true;
38
- }
39
- return false;
40
- }
41
- exports.hasValidConfigFile = hasValidConfigFile;
42
- function parseConfigFile(config) {
43
- const supportedFileVersions = ['1.0-rc.3'];
25
+ function parseConfigFile() {
26
+ const supportedFileVersionPattern = /^1\.0$/;
44
27
  const configFile = (0, core_1.getStringFromEnv)('OTEL_CONFIG_FILE') || '';
45
28
  const file = fs.readFileSync(configFile, 'utf8');
46
- const parsedContent = yaml.parse(file);
47
- if (parsedContent['file_format'] &&
48
- supportedFileVersions.includes(parsedContent['file_format'])) {
49
- const disabled = (0, utils_1.getBooleanFromConfigFile)(parsedContent['disabled']);
50
- if (disabled !== undefined) {
51
- config.disabled = disabled;
52
- }
53
- const logLevel = (0, utils_1.getNumberFromConfigFile)((0, core_1.diagLogLevelFromString)(parsedContent['log_level']));
54
- if (logLevel) {
55
- config.log_level = logLevel;
56
- }
57
- if (parsedContent['resource']) {
58
- if (config.resource == null) {
59
- config.resource = {};
60
- }
61
- const schemaUrl = (0, utils_1.getStringFromConfigFile)(parsedContent['resource']?.['schema_url']);
62
- if (schemaUrl) {
63
- config.resource.schema_url = schemaUrl;
64
- }
65
- }
66
- setResourceAttributes(config, parsedContent['resource']?.['attributes'], parsedContent['resource']?.['attributes_list']);
67
- const detectionConfig = parsedContent['resource']?.['detection/development'];
68
- if (detectionConfig) {
69
- config.resource['detection/development'] =
70
- parseDetectionDevelopment(detectionConfig);
71
- }
72
- setAttributeLimits(config, parsedContent['attribute_limits']);
73
- setPropagator(config, parsedContent['propagator']);
74
- setTracerProvider(config, parsedContent['tracer_provider']);
75
- setMeterProvider(config, parsedContent['meter_provider']);
76
- setLoggerProvider(config, parsedContent['logger_provider']);
29
+ // Apply env var substitution to all string values before schema parsing
30
+ const rawParsed = yaml.parse(file);
31
+ const processed = substituteEnvVars(rawParsed);
32
+ const fileFormat = processed?.file_format;
33
+ if (!fileFormat || !supportedFileVersionPattern.test(String(fileFormat))) {
34
+ throw new Error(`${configFile}: Unsupported file_format: "${fileFormat}". Must match ${supportedFileVersionPattern}.`);
77
35
  }
78
- else {
79
- api_1.diag.warn(`Unsupported File Format: ${parsedContent['file_format']}. It must be one of the following: ${supportedFileVersions}`);
36
+ // Normalize YAML null type-tag values to {} before validation
37
+ normalizeYamlNulls(processed);
38
+ const valid = validateConfig(processed);
39
+ if (!valid) {
40
+ const firstError = validateConfig.errors?.[0];
41
+ const detail = firstError
42
+ ? `${firstError.instancePath} ${firstError.message}`
43
+ : 'unknown error';
44
+ throw new Error(`Invalid OpenTelemetry config file: ${detail.trim()}`);
80
45
  }
46
+ const data = processed;
47
+ // Strip file_format from output — it's a meta-field, not a config value
48
+ delete data['file_format'];
49
+ // The generated TypeScript types omit `| null` from unions (see
50
+ // generate-config.js) because consumers expect `T | undefined`, not
51
+ // `T | null`. To match, we delete any properties still set to `null`
52
+ // after YAML parsing so that accessing them returns `undefined`.
53
+ // Runs after normalizeYamlNulls, which has already converted type-tag
54
+ // nulls (e.g. `console:`) to `{}`.
55
+ stripNulls(processed);
56
+ applyConfigDefaults(data);
57
+ mergeAttributesList(data);
58
+ mergeCompositeList(data);
59
+ applyBatchProcessorDefaults(data);
60
+ applyPeriodicReaderDefaults(data);
61
+ applyOtlpHttpEncodingDefaults(data);
62
+ return data;
81
63
  }
82
64
  exports.parseConfigFile = parseConfigFile;
83
- function setResourceAttributes(config, attributes, attributeList) {
84
- if (attributes || attributeList) {
85
- const addedKeys = [];
86
- if (config.resource == null) {
87
- config.resource = {};
88
- }
89
- if ((0, utils_1.getStringFromConfigFile)(attributeList)) {
90
- config.resource.attributes_list = (0, utils_1.getStringFromConfigFile)(attributeList);
91
- }
92
- const list = (0, utils_1.getStringListFromConfigFile)(attributeList);
93
- if ((list && list.length > 0) || (attributes && attributes.length > 0)) {
94
- config.resource.attributes = [];
95
- if (attributes) {
96
- for (let i = 0; i < attributes.length; i++) {
97
- const att = attributes[i];
98
- let value = att['value'];
99
- switch (att['type']) {
100
- case 'bool':
101
- value = (0, utils_1.getBooleanFromConfigFile)(value);
102
- break;
103
- case 'bool_array':
104
- value = (0, utils_1.getBooleanListFromConfigFile)(value);
105
- break;
106
- case 'int':
107
- case 'double':
108
- value = (0, utils_1.getNumberFromConfigFile)(value);
109
- break;
110
- case 'int_array':
111
- case 'double_array':
112
- value = (0, utils_1.getNumberListFromConfigFile)(value);
113
- break;
114
- case 'string_array':
115
- value = (0, utils_1.getStringListFromConfigFile)(value);
116
- break;
117
- default:
118
- value = (0, utils_1.getStringFromConfigFile)(value);
119
- break;
120
- }
121
- const key = (0, utils_1.getStringFromConfigFile)(att['name']) ?? '';
122
- config.resource.attributes.push({
123
- name: key,
124
- value: value,
125
- type: att['type'] ?? 'string',
126
- });
127
- addedKeys.push(key);
128
- }
129
- }
130
- if (list) {
131
- for (let i = 0; i < list.length; i++) {
132
- const element = list[i].split('=');
133
- if (!addedKeys.includes(element[0])) {
134
- config.resource.attributes.push({
135
- name: element[0],
136
- value: element[1],
137
- type: 'string',
138
- });
139
- }
140
- }
141
- }
65
+ /**
66
+ * Apply default encoding ('protobuf') to all otlp_http exporters in the config
67
+ * where encoding was not explicitly specified. The spec defines protobuf as the
68
+ * default encoding for OTLP HTTP exporters.
69
+ */
70
+ function applyOtlpHttpEncodingDefaults(data) {
71
+ const applyEncoding = (exporter) => {
72
+ if (exporter && exporter.encoding == null) {
73
+ exporter.encoding = 'protobuf';
142
74
  }
75
+ };
76
+ for (const processor of data.tracer_provider?.processors ?? []) {
77
+ applyEncoding(processor.batch?.exporter?.otlp_http);
78
+ applyEncoding(processor.simple?.exporter?.otlp_http);
143
79
  }
144
- }
145
- exports.setResourceAttributes = setResourceAttributes;
146
- function parseDetectionDevelopment(
147
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
148
- detection) {
149
- const result = {};
150
- if (detection['attributes']) {
151
- result.attributes = {};
152
- const included = detection['attributes']['included'];
153
- if (Array.isArray(included)) {
154
- result.attributes.included = included.filter(
155
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
- (v) => typeof v === 'string');
157
- }
158
- const excluded = detection['attributes']['excluded'];
159
- if (Array.isArray(excluded)) {
160
- result.attributes.excluded = excluded.filter(
161
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
162
- (v) => typeof v === 'string');
163
- }
80
+ for (const reader of data.meter_provider?.readers ?? []) {
81
+ applyEncoding(reader.periodic?.exporter?.otlp_http);
164
82
  }
165
- if (Array.isArray(detection['detectors'])) {
166
- result.detectors = [];
167
- for (let i = 0; i < detection['detectors'].length; i++) {
168
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
169
- const d = detection['detectors'][i];
170
- if (typeof d !== 'object' || d === null) {
171
- continue;
172
- }
173
- const detector = {};
174
- if ('container' in d)
175
- detector.container = d.container ?? {};
176
- if ('env' in d)
177
- detector.env = d.env ?? {};
178
- if ('host' in d)
179
- detector.host = d.host ?? {};
180
- if ('os' in d)
181
- detector.os = d.os ?? {};
182
- if ('process' in d)
183
- detector.process = d.process ?? {};
184
- if ('service' in d)
185
- detector.service = d.service ?? {};
186
- result.detectors.push(detector);
187
- }
83
+ for (const processor of data.logger_provider?.processors ?? []) {
84
+ applyEncoding(processor.batch?.exporter?.otlp_http);
85
+ applyEncoding(processor.simple?.exporter?.otlp_http);
188
86
  }
189
- return result;
190
87
  }
191
- function setAttributeLimits(config, attrLimits) {
192
- if (attrLimits) {
193
- if (config.attribute_limits == null) {
194
- config.attribute_limits = {
195
- attribute_count_limit: 128,
196
- };
197
- }
198
- const lengthLimit = (0, utils_1.getNumberFromConfigFile)(attrLimits['attribute_value_length_limit']);
199
- if (lengthLimit) {
200
- config.attribute_limits.attribute_value_length_limit = lengthLimit;
201
- }
202
- const countLimit = (0, utils_1.getNumberFromConfigFile)(attrLimits['attribute_count_limit']);
203
- if (countLimit) {
204
- config.attribute_limits.attribute_count_limit = countLimit;
205
- }
88
+ /**
89
+ * Merge resource.attributes_list (comma-separated key=value pairs) into
90
+ * resource.attributes, with entries already in attributes taking precedence.
91
+ */
92
+ function mergeAttributesList(data) {
93
+ const resource = data.resource;
94
+ const list = resource?.attributes_list;
95
+ if (typeof list !== 'string' || !list.trim())
96
+ return;
97
+ if (resource.attributes == null) {
98
+ resource.attributes = [];
206
99
  }
207
- }
208
- exports.setAttributeLimits = setAttributeLimits;
209
- function setPropagator(config, propagator) {
210
- if (propagator && (propagator.composite || propagator.composite_list)) {
211
- const auxList = [];
212
- const composite = [];
213
- if (propagator.composite) {
214
- for (let i = 0; i < propagator.composite.length; i++) {
215
- const key = Object.keys(propagator.composite[i])[0];
216
- auxList.push(key);
217
- composite.push({ [key]: null });
100
+ const existingKeys = new Set(resource.attributes.map((a) => a.name));
101
+ for (const pair of list.split(',')) {
102
+ const eqIdx = pair.indexOf('=');
103
+ if (eqIdx > 0) {
104
+ const key = pair.slice(0, eqIdx).trim();
105
+ const value = pair.slice(eqIdx + 1).trim();
106
+ if (key && !existingKeys.has(key)) {
107
+ resource.attributes.push({ name: key, value, type: 'string' });
218
108
  }
219
109
  }
220
- const compositeList = (0, utils_1.getStringListFromConfigFile)(propagator['composite_list']);
221
- if (compositeList) {
222
- for (let i = 0; i < compositeList.length; i++) {
223
- if (!auxList.includes(compositeList[i])) {
224
- composite.push({ [compositeList[i]]: null });
225
- }
226
- }
227
- }
228
- if (composite.length > 0) {
229
- if (config.propagator == null) {
230
- config.propagator = {};
231
- }
232
- config.propagator.composite = composite;
233
- }
234
- const compositeListString = (0, utils_1.getStringFromConfigFile)(propagator['composite_list']);
235
- if (compositeListString) {
236
- config.propagator.composite_list = compositeListString;
237
- }
238
- else if (auxList.length > 0) {
239
- // Generate composite_list from the composite entries
240
- config.propagator.composite_list = auxList.join(',');
241
- }
242
110
  }
243
111
  }
244
- exports.setPropagator = setPropagator;
245
- function parseComposableSampler(sampler) {
246
- const samplerType = Object.keys(sampler)[0];
247
- let parsedSampler = {};
248
- switch (samplerType) {
249
- case 'always_on':
250
- parsedSampler = { always_on: sampler['always_on'] ?? undefined };
251
- break;
252
- case 'always_off':
253
- parsedSampler = { always_off: sampler['always_off'] ?? undefined };
254
- break;
255
- case 'parent_threshold': {
256
- const s = sampler['parent_threshold'];
257
- if (s?.root) {
258
- parsedSampler = {
259
- parent_threshold: { root: parseComposableSampler(s.root) },
260
- };
261
- }
262
- break;
263
- }
264
- case 'probability': {
265
- const s = sampler['probability'];
266
- parsedSampler = {
267
- probability: {
268
- ratio: (0, utils_1.getNumberFromConfigFile)(s?.ratio) ?? 1.0,
269
- },
270
- };
271
- break;
272
- }
273
- case 'rule_based': {
274
- const rb = sampler['rule_based'];
275
- if (rb) {
276
- parsedSampler = { rule_based: {} };
277
- if (rb.rules) {
278
- parsedSampler.rule_based.rules = rb.rules.map(rule => ({
279
- ...rule,
280
- sampler: rule.sampler ? parseComposableSampler(rule.sampler) : {},
281
- }));
282
- }
283
- }
284
- break;
285
- }
286
- }
287
- return parsedSampler;
288
- }
289
- function parseSampler(sampler) {
290
- const samplerType = Object.keys(sampler)[0];
291
- let parsedSampler = {};
292
- switch (samplerType) {
293
- case 'always_on':
294
- parsedSampler = { always_on: sampler['always_on'] ?? undefined };
295
- break;
296
- case 'always_off':
297
- parsedSampler = { always_off: sampler['always_off'] ?? undefined };
298
- break;
299
- case 'trace_id_ratio_based': {
300
- const s = sampler['trace_id_ratio_based'];
301
- parsedSampler = {
302
- trace_id_ratio_based: {
303
- ratio: (0, utils_1.getNumberFromConfigFile)(s?.ratio) ?? 1.0,
304
- },
305
- };
306
- break;
307
- }
308
- case 'parent_based': {
309
- const s = sampler['parent_based'];
310
- if (s) {
311
- parsedSampler = { parent_based: {} };
312
- if (s.root) {
313
- parsedSampler.parent_based.root = parseSampler(s.root);
314
- }
315
- if (s.remote_parent_sampled) {
316
- parsedSampler.parent_based.remote_parent_sampled = parseSampler(s.remote_parent_sampled);
317
- }
318
- if (s.remote_parent_not_sampled) {
319
- parsedSampler.parent_based.remote_parent_not_sampled = parseSampler(s.remote_parent_not_sampled);
320
- }
321
- if (s.local_parent_sampled) {
322
- parsedSampler.parent_based.local_parent_sampled = parseSampler(s.local_parent_sampled);
323
- }
324
- if (s.local_parent_not_sampled) {
325
- parsedSampler.parent_based.local_parent_not_sampled = parseSampler(s.local_parent_not_sampled);
326
- }
327
- }
328
- break;
329
- }
330
- case 'probability/development': {
331
- const s = sampler['probability/development'];
332
- parsedSampler = {
333
- 'probability/development': {
334
- ratio: (0, utils_1.getNumberFromConfigFile)(s?.ratio) ?? 1.0,
335
- },
336
- };
337
- break;
338
- }
339
- case 'composite/development': {
340
- const s = sampler['composite/development'];
341
- if (s) {
342
- parsedSampler = {
343
- 'composite/development': parseComposableSampler(s),
344
- };
345
- }
346
- break;
347
- }
112
+ /**
113
+ * Merge propagator.composite_list (comma-separated propagator names) into
114
+ * propagator.composite, with entries already in composite taking precedence.
115
+ */
116
+ function mergeCompositeList(data) {
117
+ const propagator = data.propagator;
118
+ const list = propagator?.composite_list;
119
+ if (typeof list !== 'string' || !list.trim())
120
+ return;
121
+ if (propagator.composite == null) {
122
+ propagator.composite = [];
348
123
  }
349
- return parsedSampler;
350
- }
351
- function getConfigHeaders(h) {
352
- if (h) {
353
- const headers = [];
354
- for (let i = 0; i < h.length; i++) {
355
- const element = h[i];
356
- headers.push({
357
- name: element['name'],
358
- value: element['value'],
124
+ const existingNames = new Set(propagator.composite.map(entry => Object.keys(entry)[0]));
125
+ for (const name of list.split(',')) {
126
+ const trimmed = name.trim();
127
+ if (trimmed && !existingNames.has(trimmed)) {
128
+ propagator.composite.push({
129
+ [trimmed]: {},
359
130
  });
360
131
  }
361
- if (headers.length > 0) {
362
- return headers;
363
- }
364
132
  }
365
- return null;
366
133
  }
367
- var ProviderType;
368
- (function (ProviderType) {
369
- ProviderType[ProviderType["TRACER"] = 0] = "TRACER";
370
- ProviderType[ProviderType["METER"] = 1] = "METER";
371
- ProviderType[ProviderType["LOGGER"] = 2] = "LOGGER";
372
- })(ProviderType || (ProviderType = {}));
373
- function parseConfigSpanOrLogRecordExporter(exporter, providerType) {
374
- const exporterType = Object.keys(exporter)[0];
375
- let parsedExporter = {};
376
- let e;
377
- let certFile;
378
- let clientCertFile;
379
- let clientKeyFile;
380
- let compression;
381
- let headers;
382
- let headersList;
383
- let insecure;
384
- let endpoint;
385
- switch (providerType) {
386
- case ProviderType.TRACER:
387
- endpoint = 'http://localhost:4318/v1/traces';
388
- parsedExporter = parsedExporter;
389
- break;
390
- case ProviderType.LOGGER:
391
- endpoint = 'http://localhost:4318/v1/logs';
392
- parsedExporter = parsedExporter;
393
- break;
134
+ /**
135
+ * Apply spec-defined defaults for batch span/log-record processor fields that
136
+ * are not encoded in the JSON schema.
137
+ */
138
+ function applyBatchProcessorDefaults(data) {
139
+ const applyDefaults = (batch) => {
140
+ if (batch.schedule_delay == null)
141
+ batch.schedule_delay = 5000;
142
+ if (batch.export_timeout == null)
143
+ batch.export_timeout = 30000;
144
+ if (batch.max_queue_size == null)
145
+ batch.max_queue_size = 2048;
146
+ if (batch.max_export_batch_size == null)
147
+ batch.max_export_batch_size = 512;
148
+ };
149
+ for (const processor of data.tracer_provider?.processors ?? []) {
150
+ if (processor.batch)
151
+ applyDefaults(processor.batch);
394
152
  }
395
- switch (exporterType) {
396
- case 'otlp_http':
397
- e = exporter['otlp_http'];
398
- if (e) {
399
- parsedExporter = {
400
- otlp_http: {
401
- endpoint: (0, utils_1.getStringFromConfigFile)(e['endpoint']) ?? endpoint,
402
- timeout: (0, utils_1.getNumberFromConfigFile)(e['timeout']) ?? 10000,
403
- encoding: (0, utils_1.getStringFromConfigFile)(e['encoding']) === 'json'
404
- ? commonModel_1.OtlpHttpEncoding.JSON
405
- : commonModel_1.OtlpHttpEncoding.Protobuf,
406
- },
407
- };
408
- compression = (0, utils_1.getStringFromConfigFile)(e['compression']);
409
- if (compression && parsedExporter.otlp_http) {
410
- parsedExporter.otlp_http.compression = compression;
411
- }
412
- headersList = (0, utils_1.getStringFromConfigFile)(e['headers_list']);
413
- if (headersList && parsedExporter.otlp_http) {
414
- parsedExporter.otlp_http.headers_list = headersList;
415
- }
416
- headers = getConfigHeaders(e['headers']);
417
- if (headers && parsedExporter.otlp_http) {
418
- parsedExporter.otlp_http.headers = headers;
419
- }
420
- if (e['tls']) {
421
- certFile = (0, utils_1.getStringFromConfigFile)(e['tls']['ca_file']);
422
- clientCertFile = (0, utils_1.getStringFromConfigFile)(e['tls']['cert_file']);
423
- clientKeyFile = (0, utils_1.getStringFromConfigFile)(e['tls']['key_file']);
424
- const tls = (0, utils_1.getHttpTlsConfig)(certFile, clientKeyFile, clientCertFile);
425
- if (tls) {
426
- parsedExporter.otlp_http.tls = tls;
427
- }
428
- }
429
- }
430
- break;
431
- case 'otlp_grpc':
432
- e = exporter['otlp_grpc'];
433
- if (e) {
434
- parsedExporter = {
435
- otlp_grpc: {
436
- endpoint: (0, utils_1.getStringFromConfigFile)(e['endpoint']) ?? 'http://localhost:4317',
437
- timeout: (0, utils_1.getNumberFromConfigFile)(e['timeout']) ?? 10000,
438
- },
439
- };
440
- compression = (0, utils_1.getStringFromConfigFile)(e['compression']);
441
- if (compression && parsedExporter.otlp_grpc) {
442
- parsedExporter.otlp_grpc.compression = compression;
443
- }
444
- headersList = (0, utils_1.getStringFromConfigFile)(e['headers_list']);
445
- if (headersList && parsedExporter.otlp_grpc) {
446
- parsedExporter.otlp_grpc.headers_list = headersList;
447
- }
448
- headers = getConfigHeaders(e['headers']);
449
- if (headers && parsedExporter.otlp_grpc) {
450
- parsedExporter.otlp_grpc.headers = headers;
451
- }
452
- if (e['tls']) {
453
- certFile = (0, utils_1.getStringFromConfigFile)(e['tls']['ca_file']);
454
- clientCertFile = (0, utils_1.getStringFromConfigFile)(e['tls']['cert_file']);
455
- clientKeyFile = (0, utils_1.getStringFromConfigFile)(e['tls']['key_file']);
456
- insecure = (0, utils_1.getBooleanFromConfigFile)(e['tls']['insecure']);
457
- const tls = (0, utils_1.getGrpcTlsConfig)(certFile, clientKeyFile, clientCertFile, insecure);
458
- if (tls) {
459
- parsedExporter.otlp_grpc.tls = tls;
460
- }
461
- }
462
- }
463
- break;
464
- case 'otlp_file/development':
465
- e = exporter['otlp_file/development'];
466
- if (e) {
467
- parsedExporter = {
468
- 'otlp_file/development': {
469
- output_stream: (0, utils_1.getStringFromConfigFile)(e['output_stream']) ?? 'stdout',
470
- },
471
- };
472
- }
473
- break;
474
- case 'console':
475
- parsedExporter = {
476
- console: {},
477
- };
478
- break;
153
+ for (const processor of data.logger_provider?.processors ?? []) {
154
+ if (processor.batch)
155
+ applyDefaults(processor.batch);
479
156
  }
480
- return parsedExporter;
481
157
  }
482
- function setTracerProvider(config, tracerProvider) {
483
- if (tracerProvider && tracerProvider.processors?.length > 0) {
484
- config.tracer_provider = (0, tracerProviderModel_1.initializeDefaultTracerProviderConfiguration)();
485
- // Limits
486
- if (tracerProvider['limits']) {
487
- const attributeValueLengthLimit = (0, utils_1.getNumberFromConfigFile)(tracerProvider['limits']['attribute_value_length_limit']);
488
- if (attributeValueLengthLimit) {
489
- config.tracer_provider.limits.attribute_value_length_limit =
490
- attributeValueLengthLimit;
491
- }
492
- const attributeCountLimit = (0, utils_1.getNumberFromConfigFile)(tracerProvider['limits']['attribute_count_limit']);
493
- if (attributeCountLimit) {
494
- config.tracer_provider.limits.attribute_count_limit =
495
- attributeCountLimit;
496
- }
497
- const eventCountLimit = (0, utils_1.getNumberFromConfigFile)(tracerProvider['limits']['event_count_limit']);
498
- if (eventCountLimit) {
499
- config.tracer_provider.limits.event_count_limit = eventCountLimit;
500
- }
501
- const linkCountLimit = (0, utils_1.getNumberFromConfigFile)(tracerProvider['limits']['link_count_limit']);
502
- if (linkCountLimit) {
503
- config.tracer_provider.limits.link_count_limit = linkCountLimit;
504
- }
505
- const eventAttributeCountLimit = (0, utils_1.getNumberFromConfigFile)(tracerProvider['limits']['event_attribute_count_limit']);
506
- if (eventAttributeCountLimit) {
507
- config.tracer_provider.limits.event_attribute_count_limit =
508
- eventAttributeCountLimit;
509
- }
510
- const linkAttributeCountLimit = (0, utils_1.getNumberFromConfigFile)(tracerProvider['limits']['link_attribute_count_limit']);
511
- if (linkAttributeCountLimit) {
512
- config.tracer_provider.limits.link_attribute_count_limit =
513
- linkAttributeCountLimit;
514
- }
515
- }
516
- // Sampler
517
- if (tracerProvider['sampler']) {
518
- config.tracer_provider.sampler = parseSampler(tracerProvider['sampler']);
519
- }
520
- // Processors
521
- for (let i = 0; i < tracerProvider['processors'].length; i++) {
522
- const processorType = Object.keys(tracerProvider['processors'][i])[0];
523
- if (processorType === 'batch') {
524
- const element = tracerProvider['processors'][i]['batch'];
525
- if (element) {
526
- const parsedExporter = parseConfigSpanOrLogRecordExporter(element['exporter'], ProviderType.TRACER);
527
- const batchConfig = {
528
- batch: {
529
- schedule_delay: (0, utils_1.getNumberFromConfigFile)(element['schedule_delay']) ?? 5000,
530
- export_timeout: (0, utils_1.getNumberFromConfigFile)(element['export_timeout']) ?? 30000,
531
- max_queue_size: (0, utils_1.getNumberFromConfigFile)(element['max_queue_size']) ?? 2048,
532
- max_export_batch_size: (0, utils_1.getNumberFromConfigFile)(element['max_export_batch_size']) ??
533
- 512,
534
- exporter: parsedExporter,
535
- },
536
- };
537
- config.tracer_provider.processors.push(batchConfig);
538
- }
539
- }
540
- else if (processorType === 'simple') {
541
- const element = tracerProvider['processors'][i]['simple'];
542
- if (element) {
543
- const parsedExporter = parseConfigSpanOrLogRecordExporter(element['exporter'], ProviderType.TRACER);
544
- const simpleConfig = {
545
- simple: {
546
- exporter: parsedExporter,
547
- },
548
- };
549
- config.tracer_provider.processors.push(simpleConfig);
550
- }
551
- }
158
+ /**
159
+ * Apply spec-defined defaults for periodic metric reader fields.
160
+ */
161
+ function applyPeriodicReaderDefaults(data) {
162
+ for (const reader of data.meter_provider?.readers ?? []) {
163
+ const periodic = reader.periodic;
164
+ if (!periodic)
165
+ continue;
166
+ if (periodic.interval == null)
167
+ periodic.interval = 60000;
168
+ if (periodic.timeout == null)
169
+ periodic.timeout = 30000;
170
+ if (periodic.cardinality_limits == null) {
171
+ periodic.cardinality_limits = { default: 2000 };
552
172
  }
553
173
  }
554
- else if (tracerProvider &&
555
- (tracerProvider.processors == null ||
556
- tracerProvider.processors.length === 0)) {
557
- api_1.diag.warn('TracerProvider must have at least one processor configured');
558
- }
559
174
  }
560
- exports.setTracerProvider = setTracerProvider;
561
- function getCardinalityLimits(limits) {
562
- if (limits == null) {
563
- limits = {};
175
+ /**
176
+ * Apply spec-defined defaults that are not encoded in the JSON schema.
177
+ * Both FileConfigFactory and EnvironmentConfigFactory apply the same defaults
178
+ * so consumers see consistent behaviour regardless of config source.
179
+ */
180
+ function applyConfigDefaults(data) {
181
+ if (data.disabled == null) {
182
+ data.disabled = false;
564
183
  }
565
- const defaultLimit = (0, utils_1.getNumberFromConfigFile)(limits['default']) ?? 2000;
566
- return {
567
- default: defaultLimit,
568
- counter: (0, utils_1.getNumberFromConfigFile)(limits['counter']) ?? defaultLimit,
569
- gauge: (0, utils_1.getNumberFromConfigFile)(limits['gauge']) ?? defaultLimit,
570
- histogram: (0, utils_1.getNumberFromConfigFile)(limits['histogram']) ?? defaultLimit,
571
- observable_counter: (0, utils_1.getNumberFromConfigFile)(limits['observable_counter']) ?? defaultLimit,
572
- observable_gauge: (0, utils_1.getNumberFromConfigFile)(limits['observable_gauge']) ?? defaultLimit,
573
- observable_up_down_counter: (0, utils_1.getNumberFromConfigFile)(limits['observable_up_down_counter']) ??
574
- defaultLimit,
575
- up_down_counter: (0, utils_1.getNumberFromConfigFile)(limits['up_down_counter']) ?? defaultLimit,
576
- };
577
- }
578
- function getProducers(producers) {
579
- const parsedProducers = [];
580
- if (producers) {
581
- for (let j = 0; j < producers.length; j++) {
582
- const producer = producers[j];
583
- if (Object.keys(producer)[0] === 'opencensus') {
584
- parsedProducers.push({ opencensus: {} });
585
- }
586
- }
184
+ if (data.log_level == null) {
185
+ data.log_level = 'info';
587
186
  }
588
- return parsedProducers;
589
- }
590
- function getTemporalityPreference(temporalityPreference) {
591
- const temporalityPreferenceType = (0, utils_1.getStringFromConfigFile)(temporalityPreference);
592
- switch (temporalityPreferenceType) {
593
- case 'cumulative':
594
- return meterProviderModel_1.ExporterTemporalityPreference.Cumulative;
595
- case 'delta':
596
- return meterProviderModel_1.ExporterTemporalityPreference.Delta;
597
- case 'low_memory':
598
- return meterProviderModel_1.ExporterTemporalityPreference.LowMemory;
599
- default:
600
- return meterProviderModel_1.ExporterTemporalityPreference.Cumulative;
187
+ if (data.attribute_limits == null) {
188
+ data.attribute_limits = { attribute_count_limit: 128 };
601
189
  }
602
- }
603
- exports.getTemporalityPreference = getTemporalityPreference;
604
- function getDefaultHistogramAggregation(defaultHistogramAggregation) {
605
- const defaultHistogramAggregationType = (0, utils_1.getStringFromConfigFile)(defaultHistogramAggregation);
606
- switch (defaultHistogramAggregationType) {
607
- case 'explicit_bucket_histogram':
608
- return meterProviderModel_1.ExporterDefaultHistogramAggregation.ExplicitBucketHistogram;
609
- case 'base2_exponential_bucket_histogram':
610
- return meterProviderModel_1.ExporterDefaultHistogramAggregation.Base2ExponentialBucketHistogram;
611
- default:
612
- return meterProviderModel_1.ExporterDefaultHistogramAggregation.ExplicitBucketHistogram;
190
+ else if (data.attribute_limits.attribute_count_limit == null) {
191
+ data.attribute_limits.attribute_count_limit = 128;
613
192
  }
614
193
  }
615
- function parseMetricExporter(exporter) {
616
- const exporterType = Object.keys(exporter)[0];
617
- let parsedExporter = {};
618
- let e;
619
- let certFile;
620
- let clientCertFile;
621
- let clientKeyFile;
622
- let compression;
623
- let headers;
624
- let headersList;
625
- let insecure;
626
- switch (exporterType) {
627
- case 'otlp_http':
628
- e = exporter['otlp_http'];
629
- if (e) {
630
- parsedExporter = {
631
- otlp_http: {
632
- endpoint: (0, utils_1.getStringFromConfigFile)(e['endpoint']) ??
633
- 'http://localhost:4318/v1/metrics',
634
- timeout: (0, utils_1.getNumberFromConfigFile)(e['timeout']) ?? 10000,
635
- encoding: (0, utils_1.getStringFromConfigFile)(e['encoding']) === 'json'
636
- ? commonModel_1.OtlpHttpEncoding.JSON
637
- : commonModel_1.OtlpHttpEncoding.Protobuf,
638
- temporality_preference: getTemporalityPreference(e['temporality_preference']),
639
- default_histogram_aggregation: getDefaultHistogramAggregation(e['default_histogram_aggregation']),
640
- },
641
- };
642
- compression = (0, utils_1.getStringFromConfigFile)(e['compression']);
643
- if (compression && parsedExporter.otlp_http) {
644
- parsedExporter.otlp_http.compression = compression;
645
- }
646
- headersList = (0, utils_1.getStringFromConfigFile)(e['headers_list']);
647
- if (headersList && parsedExporter.otlp_http) {
648
- parsedExporter.otlp_http.headers_list = headersList;
649
- }
650
- headers = getConfigHeaders(e['headers']);
651
- if (headers && parsedExporter.otlp_http) {
652
- parsedExporter.otlp_http.headers = headers;
653
- }
654
- if (e['tls']) {
655
- certFile = (0, utils_1.getStringFromConfigFile)(e['tls']['ca_file']);
656
- clientCertFile = (0, utils_1.getStringFromConfigFile)(e['tls']['cert_file']);
657
- clientKeyFile = (0, utils_1.getStringFromConfigFile)(e['tls']['key_file']);
658
- const tls = (0, utils_1.getHttpTlsConfig)(certFile, clientKeyFile, clientCertFile);
659
- if (tls) {
660
- parsedExporter.otlp_http.tls = tls;
661
- }
662
- }
663
- }
664
- break;
665
- case 'otlp_grpc':
666
- e = exporter['otlp_grpc'];
667
- if (e) {
668
- parsedExporter = {
669
- otlp_grpc: {
670
- endpoint: (0, utils_1.getStringFromConfigFile)(e['endpoint']) ?? 'http://localhost:4317',
671
- timeout: (0, utils_1.getNumberFromConfigFile)(e['timeout']) ?? 10000,
672
- temporality_preference: getTemporalityPreference(e['temporality_preference']),
673
- default_histogram_aggregation: getDefaultHistogramAggregation(e['default_histogram_aggregation']),
674
- },
675
- };
676
- compression = (0, utils_1.getStringFromConfigFile)(e['compression']);
677
- if (compression && parsedExporter.otlp_grpc) {
678
- parsedExporter.otlp_grpc.compression = compression;
679
- }
680
- headersList = (0, utils_1.getStringFromConfigFile)(e['headers_list']);
681
- if (headersList && parsedExporter.otlp_grpc) {
682
- parsedExporter.otlp_grpc.headers_list = headersList;
683
- }
684
- headers = getConfigHeaders(e['headers']);
685
- if (headers && parsedExporter.otlp_grpc) {
686
- parsedExporter.otlp_grpc.headers = headers;
687
- }
688
- if (e['tls']) {
689
- certFile = (0, utils_1.getStringFromConfigFile)(e['tls']['ca_file']);
690
- clientCertFile = (0, utils_1.getStringFromConfigFile)(e['tls']['cert_file']);
691
- clientKeyFile = (0, utils_1.getStringFromConfigFile)(e['tls']['key_file']);
692
- insecure = (0, utils_1.getBooleanFromConfigFile)(e['tls']['insecure']);
693
- const tls = (0, utils_1.getGrpcTlsConfig)(certFile, clientKeyFile, clientCertFile, insecure);
694
- if (tls) {
695
- parsedExporter.otlp_grpc.tls = tls;
696
- }
697
- }
698
- }
699
- break;
700
- case 'otlp_file/development':
701
- e = exporter['otlp_file/development'];
702
- if (e) {
703
- parsedExporter = {
704
- 'otlp_file/development': {
705
- output_stream: (0, utils_1.getStringFromConfigFile)(e['output_stream']) ?? 'stdout',
706
- temporality_preference: getTemporalityPreference(e['temporality_preference']),
707
- default_histogram_aggregation: getDefaultHistogramAggregation(e['default_histogram_aggregation']),
708
- },
709
- };
194
+ /**
195
+ * Recursively delete object properties whose value is `null`.
196
+ *
197
+ * YAML `key:` (no value) parses to `{ key: null }`. The generated TypeScript
198
+ * types use `?: T` (not `T | null`) because downstream consumers like sdk-node
199
+ * assign config values to interfaces that only accept `T | undefined`. The
200
+ * codegen script (generate-config.js) strips `| null` from type unions; this
201
+ * function makes the runtime data match by deleting null-valued keys so that
202
+ * property access returns `undefined` instead of `null`.
203
+ *
204
+ * Must run AFTER normalizeYamlNulls, which converts type-tag nulls
205
+ * (e.g. `console: null` → `console: {}`) — those are already `{}` by the
206
+ * time this function runs and won't be affected.
207
+ */
208
+ function stripNulls(value) {
209
+ if (value == null || typeof value !== 'object')
210
+ return;
211
+ if (Array.isArray(value)) {
212
+ for (const item of value) {
213
+ stripNulls(item);
214
+ }
215
+ }
216
+ else {
217
+ for (const [key, val] of Object.entries(value)) {
218
+ if (val === null) {
219
+ delete value[key];
710
220
  }
711
- break;
712
- case 'console':
713
- e = exporter['console'];
714
- if (e) {
715
- parsedExporter = {
716
- console: {
717
- temporality_preference: getTemporalityPreference(e['temporality_preference']),
718
- default_histogram_aggregation: getDefaultHistogramAggregation(e['default_histogram_aggregation']),
719
- },
720
- };
221
+ else {
222
+ stripNulls(val);
721
223
  }
722
- break;
224
+ }
723
225
  }
724
- return parsedExporter;
725
226
  }
726
- function setMeterProvider(config, meterProvider) {
727
- if (meterProvider && meterProvider.readers?.length > 0) {
728
- config.meter_provider = (0, meterProviderModel_1.initializeDefaultMeterProviderConfiguration)();
729
- const exemplarFilter = (0, utils_1.getStringFromConfigFile)(meterProvider['exemplar_filter']);
730
- if (exemplarFilter) {
731
- switch (exemplarFilter) {
732
- case 'trace_based':
733
- config.meter_provider.exemplar_filter = meterProviderModel_1.ExemplarFilter.TraceBased;
734
- break;
735
- case 'always_on':
736
- config.meter_provider.exemplar_filter = meterProviderModel_1.ExemplarFilter.AlwaysOn;
737
- break;
738
- case 'always_off':
739
- config.meter_provider.exemplar_filter = meterProviderModel_1.ExemplarFilter.AlwaysOff;
740
- break;
741
- default:
742
- config.meter_provider.exemplar_filter = meterProviderModel_1.ExemplarFilter.TraceBased;
743
- break;
744
- }
745
- }
746
- for (let i = 0; i < meterProvider.readers.length; i++) {
747
- const readerType = Object.keys(meterProvider['readers'][i])[0];
748
- if (readerType === 'pull') {
749
- const element = meterProvider['readers'][i]['pull'];
750
- if (element) {
751
- const exporter = {
752
- 'prometheus/development': {
753
- host: (0, utils_1.getStringFromConfigFile)(element['exporter']['prometheus/development']['host']) ?? 'localhost',
754
- port: (0, utils_1.getNumberFromConfigFile)(element['exporter']['prometheus/development']['port']) ?? 9464,
755
- without_scope_info: (0, utils_1.getBooleanFromConfigFile)(element['exporter']['prometheus/development']['without_scope_info']) ?? false,
756
- without_target_info: (0, utils_1.getBooleanFromConfigFile)(element['exporter']['prometheus/development']['without_target_info']) ?? false,
757
- },
758
- };
759
- if (element['exporter']['prometheus/development']['with_resource_constant_labels']) {
760
- exporter['prometheus/development'].with_resource_constant_labels =
761
- {
762
- included: (0, utils_1.getStringListFromConfigFile)(element['exporter']['prometheus/development']['with_resource_constant_labels']?.['included']) ?? [],
763
- excluded: (0, utils_1.getStringListFromConfigFile)(element['exporter']['prometheus/development']['with_resource_constant_labels']?.['excluded']) ?? [],
764
- };
765
- }
766
- if (element['exporter']['prometheus/development']['translation_strategy']) {
767
- const ts = (0, utils_1.getStringFromConfigFile)(element['exporter']['prometheus/development']['translation_strategy']);
768
- switch (ts) {
769
- case 'underscore_escaping_with_suffixes':
770
- exporter['prometheus/development'].translation_strategy =
771
- meterProviderModel_1.ExperimentalPrometheusTranslationStrategy.UnderscoreEscapingWithSuffixes;
772
- break;
773
- case 'underscore_escaping_without_suffixes':
774
- exporter['prometheus/development'].translation_strategy =
775
- meterProviderModel_1.ExperimentalPrometheusTranslationStrategy.UnderscoreEscapingWithoutSuffixes;
776
- break;
777
- case 'no_utf8_escaping_with_suffixes':
778
- exporter['prometheus/development'].translation_strategy =
779
- meterProviderModel_1.ExperimentalPrometheusTranslationStrategy.NoUtf8EscapingWithSuffixes;
780
- break;
781
- case 'no_translation':
782
- exporter['prometheus/development'].translation_strategy =
783
- meterProviderModel_1.ExperimentalPrometheusTranslationStrategy.NoTranslation;
784
- break;
785
- }
786
- }
787
- const pullReader = {
788
- pull: {
789
- exporter: exporter,
790
- cardinality_limits: getCardinalityLimits(element['cardinality_limits']),
791
- },
792
- };
793
- const p = getProducers(element['producers']);
794
- if (p.length > 0 && pullReader.pull) {
795
- pullReader.pull.producers = p;
796
- }
797
- config.meter_provider.readers.push(pullReader);
798
- }
799
- }
800
- else if (readerType === 'periodic') {
801
- const element = meterProvider['readers'][i]['periodic'];
802
- if (element) {
803
- const parsedExporter = parseMetricExporter(element['exporter']);
804
- const periodicReader = {
805
- periodic: {
806
- exporter: parsedExporter,
807
- cardinality_limits: getCardinalityLimits(element['cardinality_limits']),
808
- interval: (0, utils_1.getNumberFromConfigFile)(element['interval']) ?? 60000,
809
- timeout: (0, utils_1.getNumberFromConfigFile)(element['timeout']) ?? 30000,
810
- },
811
- };
812
- const p = getProducers(element['producers']);
813
- if (p.length > 0 && periodicReader.periodic) {
814
- periodicReader.periodic.producers = p;
227
+ /**
228
+ * Object type-tag keys that may appear as nested (non-array-element) properties.
229
+ * YAML `console:` (no value) parses to `{ console: null }`, but the SDK checks
230
+ * `if (exporter.console)` — truthy — so null must become {}.
231
+ *
232
+ * We exclude keys that are also used as nullable primitives in other contexts
233
+ * (e.g. `host` is a string field in prometheus config; detectors use it as a
234
+ * type-tag but only as array elements, which are already handled below).
235
+ */
236
+ const NESTED_OBJECT_TYPE_TAGS = new Set([
237
+ // Exporter type discriminators (nested inside simple/batch processor or reader)
238
+ 'console',
239
+ 'otlp_http',
240
+ 'otlp_grpc',
241
+ // Sampler type discriminators (nested inside tracer_provider.sampler)
242
+ 'always_on',
243
+ 'always_off',
244
+ 'trace_id_ratio_based',
245
+ 'parent_based',
246
+ 'jaeger_remote',
247
+ // Propagator type discriminators (array elements, but covered here for completeness)
248
+ 'tracecontext',
249
+ 'baggage',
250
+ 'b3',
251
+ 'b3multi',
252
+ 'xray',
253
+ // Telemetry producer type discriminators
254
+ 'opencensus',
255
+ ]);
256
+ /**
257
+ * Normalizes YAML null values to empty objects for discriminated-union type-tag fields.
258
+ *
259
+ * YAML `key:` (no value) parses to `{ key: null }`, but the SDK checks `if (obj.key)`
260
+ * to determine which type to instantiate — null fails that check. Two cases:
261
+ *
262
+ * 1. Array element properties: convert ALL null-valued keys to {} (handles detectors,
263
+ * composite propagators, and most exporter/processor discriminators).
264
+ * 2. Nested NESTED_OBJECT_TYPE_TAGS: convert known type-discriminator keys to {} when
265
+ * encountered anywhere in the tree (handles sampler types, console exporter nested
266
+ * inside simple/batch, etc.).
267
+ *
268
+ * Top-level nullable primitive fields (disabled, log_level, endpoint, ca_file, etc.)
269
+ * are NOT in NESTED_OBJECT_TYPE_TAGS and are left as-is; applyConfigDefaults()
270
+ * handles the boolean/string ones that need defaults.
271
+ */
272
+ function normalizeYamlNulls(value) {
273
+ if (value == null || typeof value !== 'object')
274
+ return;
275
+ if (Array.isArray(value)) {
276
+ for (const item of value) {
277
+ if (item != null && typeof item === 'object' && !Array.isArray(item)) {
278
+ // All null-valued properties of array elements are type-tag discriminators.
279
+ for (const key of Object.keys(item)) {
280
+ if (item[key] === null) {
281
+ item[key] = {};
815
282
  }
816
- config.meter_provider.readers.push(periodicReader);
817
283
  }
818
284
  }
285
+ normalizeYamlNulls(item);
819
286
  }
820
- if (meterProvider['views'] && meterProvider['views'].length > 0) {
821
- config.meter_provider.views = [];
822
- for (let j = 0; j < meterProvider['views'].length; j++) {
823
- const element = meterProvider['views'][j];
824
- const view = {};
825
- if (element['selector']) {
826
- const selector = {};
827
- const instrumentName = (0, utils_1.getStringFromConfigFile)(element['selector']['instrument_name']);
828
- if (instrumentName) {
829
- selector.instrument_name = instrumentName;
830
- }
831
- const unit = (0, utils_1.getStringFromConfigFile)(element['selector']['unit']);
832
- if (unit) {
833
- selector.unit = unit;
834
- }
835
- const meterName = (0, utils_1.getStringFromConfigFile)(element['selector']['meter_name']);
836
- if (meterName) {
837
- selector.meter_name = meterName;
838
- }
839
- const meterVersion = (0, utils_1.getStringFromConfigFile)(element['selector']['meter_version']);
840
- if (meterVersion) {
841
- selector.meter_version = meterVersion;
842
- }
843
- const meterSchemaUrl = (0, utils_1.getStringFromConfigFile)(element['selector']['meter_schema_url']);
844
- if (meterSchemaUrl) {
845
- selector.meter_schema_url = meterSchemaUrl;
846
- }
847
- const instrumentType = (0, utils_1.getStringFromConfigFile)(element['selector']['instrument_type']);
848
- if (instrumentType) {
849
- switch (instrumentType) {
850
- case 'counter':
851
- selector.instrument_type = meterProviderModel_1.InstrumentType.Counter;
852
- break;
853
- case 'gauge':
854
- selector.instrument_type = meterProviderModel_1.InstrumentType.Gauge;
855
- break;
856
- case 'histogram':
857
- selector.instrument_type = meterProviderModel_1.InstrumentType.Histogram;
858
- break;
859
- case 'observable_counter':
860
- selector.instrument_type = meterProviderModel_1.InstrumentType.ObservableCounter;
861
- break;
862
- case 'observable_gauge':
863
- selector.instrument_type = meterProviderModel_1.InstrumentType.ObservableGauge;
864
- break;
865
- case 'observable_up_down_counter':
866
- selector.instrument_type =
867
- meterProviderModel_1.InstrumentType.ObservableUpDownCounter;
868
- break;
869
- case 'up_down_counter':
870
- selector.instrument_type = meterProviderModel_1.InstrumentType.UpDownCounter;
871
- break;
872
- }
873
- }
874
- if (Object.keys(selector).length > 0) {
875
- view.selector = selector;
876
- }
877
- }
878
- if (element['stream']) {
879
- const stream = {};
880
- const name = (0, utils_1.getStringFromConfigFile)(element['stream']['name']);
881
- if (name) {
882
- stream.name = name;
883
- }
884
- const description = (0, utils_1.getStringFromConfigFile)(element['stream']['description']);
885
- if (description) {
886
- stream.description = description;
887
- }
888
- const aggregationCardinalityLimit = (0, utils_1.getNumberFromConfigFile)(element['stream']['aggregation_cardinality_limit']);
889
- if (aggregationCardinalityLimit) {
890
- stream.aggregation_cardinality_limit = aggregationCardinalityLimit;
891
- }
892
- if (element['stream']['attribute_keys']) {
893
- stream.attribute_keys = {
894
- included: (0, utils_1.getStringListFromConfigFile)(element['stream']['attribute_keys']['included']) ?? [],
895
- excluded: (0, utils_1.getStringListFromConfigFile)(element['stream']['attribute_keys']['excluded']) ?? [],
896
- };
897
- }
898
- const rawAgg = element['stream']['aggregation'];
899
- if (rawAgg) {
900
- const aggregation = {};
901
- if (rawAgg['default']) {
902
- aggregation.default = {};
903
- }
904
- if (rawAgg['drop']) {
905
- aggregation.drop = {};
906
- }
907
- if (rawAgg['last_value']) {
908
- aggregation.last_value = {};
909
- }
910
- if (rawAgg['sum']) {
911
- aggregation.sum = {};
912
- }
913
- if (rawAgg['explicit_bucket_histogram']) {
914
- aggregation.explicit_bucket_histogram = {
915
- boundaries: (0, utils_1.getNumberListFromConfigFile)(rawAgg['explicit_bucket_histogram']['boundaries']) ?? [
916
- 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000,
917
- 7500, 10000,
918
- ],
919
- record_min_max: (0, utils_1.getBooleanFromConfigFile)(rawAgg['explicit_bucket_histogram']['record_min_max']) === false
920
- ? false
921
- : true,
922
- };
923
- }
924
- if (rawAgg['base2_exponential_bucket_histogram']) {
925
- aggregation.base2_exponential_bucket_histogram = {
926
- record_min_max: (0, utils_1.getBooleanFromConfigFile)(rawAgg['base2_exponential_bucket_histogram']['record_min_max']) === false
927
- ? false
928
- : true,
929
- };
930
- const maxScale = (0, utils_1.getNumberFromConfigFile)(rawAgg['base2_exponential_bucket_histogram']['max_scale']);
931
- if (maxScale) {
932
- aggregation.base2_exponential_bucket_histogram.max_scale =
933
- maxScale;
934
- }
935
- const maxSize = (0, utils_1.getNumberFromConfigFile)(rawAgg['base2_exponential_bucket_histogram']['max_size']);
936
- if (maxSize) {
937
- aggregation.base2_exponential_bucket_histogram.max_size =
938
- maxSize;
939
- }
940
- }
941
- stream.aggregation = aggregation;
942
- }
943
- if (Object.keys(stream).length > 0) {
944
- view.stream = stream;
945
- }
946
- }
947
- config.meter_provider.views.push(view);
287
+ }
288
+ else {
289
+ for (const [key, val] of Object.entries(value)) {
290
+ if (val === null &&
291
+ // Known type-tag keys, or slash-qualified experimental type-tags
292
+ // (e.g. otlp_file/development, prometheus/development)
293
+ (NESTED_OBJECT_TYPE_TAGS.has(key) || key.includes('/'))) {
294
+ value[key] = {};
295
+ }
296
+ else {
297
+ normalizeYamlNulls(val);
948
298
  }
949
299
  }
950
300
  }
951
- else if (meterProvider &&
952
- (meterProvider.readers == null || meterProvider.readers.length === 0)) {
953
- api_1.diag.warn('MeterProvider must have at least one reader configured');
954
- }
955
301
  }
956
- exports.setMeterProvider = setMeterProvider;
957
- function getSeverity(severity) {
958
- const severityType = (0, utils_1.getStringFromConfigFile)(severity);
959
- switch (severityType) {
960
- case 'debug':
961
- return commonModel_1.SeverityNumber.DEBUG;
962
- case 'debug2':
963
- return commonModel_1.SeverityNumber.DEBUG2;
964
- case 'debug3':
965
- return commonModel_1.SeverityNumber.DEBUG3;
966
- case 'debug4':
967
- return commonModel_1.SeverityNumber.DEBUG4;
968
- case 'info':
969
- return commonModel_1.SeverityNumber.INFO;
970
- case 'info2':
971
- return commonModel_1.SeverityNumber.INFO2;
972
- case 'info3':
973
- return commonModel_1.SeverityNumber.INFO3;
974
- case 'info4':
975
- return commonModel_1.SeverityNumber.INFO4;
976
- case 'warn':
977
- return commonModel_1.SeverityNumber.WARN;
978
- case 'warn2':
979
- return commonModel_1.SeverityNumber.WARN2;
980
- case 'warn3':
981
- return commonModel_1.SeverityNumber.WARN3;
982
- case 'warn4':
983
- return commonModel_1.SeverityNumber.WARN4;
984
- case 'error':
985
- return commonModel_1.SeverityNumber.ERROR;
986
- case 'error2':
987
- return commonModel_1.SeverityNumber.ERROR2;
988
- case 'error3':
989
- return commonModel_1.SeverityNumber.ERROR3;
990
- case 'error4':
991
- return commonModel_1.SeverityNumber.ERROR4;
992
- case 'fatal':
993
- return commonModel_1.SeverityNumber.FATAL;
994
- case 'fatal2':
995
- return commonModel_1.SeverityNumber.FATAL2;
996
- case 'fatal3':
997
- return commonModel_1.SeverityNumber.FATAL3;
998
- case 'fatal4':
999
- return commonModel_1.SeverityNumber.FATAL4;
1000
- case 'trace':
1001
- return commonModel_1.SeverityNumber.TRACE;
1002
- case 'trace2':
1003
- return commonModel_1.SeverityNumber.TRACE2;
1004
- case 'trace3':
1005
- return commonModel_1.SeverityNumber.TRACE3;
1006
- case 'trace4':
1007
- return commonModel_1.SeverityNumber.TRACE4;
1008
- default:
1009
- return undefined;
302
+ const ENV_VAR_PATTERN = /\$\{[^}]+\}/;
303
+ function substituteEnvVars(obj) {
304
+ if (typeof obj === 'string') {
305
+ // Only coerce if the string contained env var substitution syntax,
306
+ // so that plain YAML strings (e.g. quoted "1234") are not type-coerced.
307
+ const hasSubstitution = ENV_VAR_PATTERN.test(obj);
308
+ const substituted = (0, utils_1.envVariableSubstitution)(obj);
309
+ return hasSubstitution ? yamlCoerce(substituted) : substituted;
1010
310
  }
1011
- }
1012
- exports.getSeverity = getSeverity;
1013
- function setLoggerProvider(config, loggerProvider) {
1014
- if (loggerProvider && loggerProvider.processors?.length > 0) {
1015
- config.logger_provider = (0, loggerProviderModel_1.initializeDefaultLoggerProviderConfiguration)();
1016
- // Limits
1017
- if (loggerProvider['limits']) {
1018
- const attributeValueLengthLimit = (0, utils_1.getNumberFromConfigFile)(loggerProvider['limits']['attribute_value_length_limit']);
1019
- const attributeCountLimit = (0, utils_1.getNumberFromConfigFile)(loggerProvider['limits']['attribute_count_limit']);
1020
- if (attributeValueLengthLimit || attributeCountLimit) {
1021
- if (attributeValueLengthLimit) {
1022
- config.logger_provider.limits.attribute_value_length_limit =
1023
- attributeValueLengthLimit;
1024
- }
1025
- if (attributeCountLimit) {
1026
- config.logger_provider.limits.attribute_count_limit =
1027
- attributeCountLimit;
1028
- }
1029
- }
1030
- }
1031
- // Processors
1032
- for (let i = 0; i < loggerProvider['processors'].length; i++) {
1033
- const processorType = Object.keys(loggerProvider['processors'][i])[0];
1034
- if (processorType === 'batch') {
1035
- const element = loggerProvider['processors'][i]['batch'];
1036
- if (element) {
1037
- const parsedExporter = parseConfigSpanOrLogRecordExporter(element['exporter'], ProviderType.LOGGER);
1038
- const batchConfig = {
1039
- batch: {
1040
- schedule_delay: (0, utils_1.getNumberFromConfigFile)(element['schedule_delay']) ?? 1000,
1041
- export_timeout: (0, utils_1.getNumberFromConfigFile)(element['export_timeout']) ?? 30000,
1042
- max_queue_size: (0, utils_1.getNumberFromConfigFile)(element['max_queue_size']) ?? 2048,
1043
- max_export_batch_size: (0, utils_1.getNumberFromConfigFile)(element['max_export_batch_size']) ??
1044
- 512,
1045
- exporter: parsedExporter,
1046
- },
1047
- };
1048
- config.logger_provider.processors.push(batchConfig);
1049
- }
1050
- }
1051
- else if (processorType === 'simple') {
1052
- const element = loggerProvider['processors'][i]['simple'];
1053
- if (element) {
1054
- const parsedExporter = parseConfigSpanOrLogRecordExporter(element['exporter'], ProviderType.LOGGER);
1055
- const simpleConfig = {
1056
- simple: {
1057
- exporter: parsedExporter,
1058
- },
1059
- };
1060
- config.logger_provider.processors.push(simpleConfig);
1061
- }
1062
- }
1063
- }
1064
- // logger_configurator/development
1065
- if (loggerProvider['logger_configurator/development']) {
1066
- const defaultConfigDisabled = (0, utils_1.getBooleanFromConfigFile)(loggerProvider['logger_configurator/development']['default_config']?.['enabled']);
1067
- if (defaultConfigDisabled || defaultConfigDisabled === false) {
1068
- config.logger_provider['logger_configurator/development'] = {
1069
- default_config: {
1070
- enabled: defaultConfigDisabled,
1071
- },
1072
- };
1073
- }
1074
- if (loggerProvider['logger_configurator/development'].loggers &&
1075
- loggerProvider['logger_configurator/development'].loggers.length > 0) {
1076
- const loggers = [];
1077
- for (let i = 0; i < loggerProvider['logger_configurator/development'].loggers.length; i++) {
1078
- const logger = loggerProvider['logger_configurator/development'].loggers[i];
1079
- let enabled = false;
1080
- let traceBased;
1081
- let minSeverity;
1082
- if (logger['config']) {
1083
- enabled =
1084
- (0, utils_1.getBooleanFromConfigFile)(logger['config']['enabled']) ?? false;
1085
- traceBased = (0, utils_1.getBooleanFromConfigFile)(logger['config']['trace_based']);
1086
- if (logger['config']['minimum_severity']) {
1087
- minSeverity = getSeverity(logger['config']['minimum_severity']);
1088
- }
1089
- }
1090
- const name = (0, utils_1.getStringFromConfigFile)(logger['name']);
1091
- if (name) {
1092
- const loggerNew = {
1093
- name: name,
1094
- config: {
1095
- enabled: enabled,
1096
- },
1097
- };
1098
- if (traceBased !== undefined) {
1099
- loggerNew.config.trace_based = traceBased;
1100
- }
1101
- if (minSeverity !== undefined) {
1102
- loggerNew.config.minimum_severity = minSeverity;
1103
- }
1104
- loggers.push(loggerNew);
1105
- }
1106
- }
1107
- if (config.logger_provider['logger_configurator/development'] == null) {
1108
- config.logger_provider['logger_configurator/development'] = {};
1109
- }
1110
- config.logger_provider['logger_configurator/development'].loggers =
1111
- loggers;
1112
- }
1113
- }
311
+ if (Array.isArray(obj)) {
312
+ return obj.map(substituteEnvVars);
1114
313
  }
1115
- else if (loggerProvider &&
1116
- (loggerProvider.processors == null ||
1117
- loggerProvider.processors.length === 0)) {
1118
- api_1.diag.warn('LoggerProvider must have at least one processor configured');
314
+ if (typeof obj === 'object' && obj !== null) {
315
+ return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, substituteEnvVars(v)]));
1119
316
  }
317
+ return obj;
318
+ }
319
+ /**
320
+ * Re-coerce a string value to its YAML-equivalent primitive type.
321
+ * Env var substitution always returns strings; this converts them back
322
+ * to booleans/numbers/null where the schema expects those types.
323
+ */
324
+ function yamlCoerce(value) {
325
+ if (value === 'true')
326
+ return true;
327
+ if (value === 'false')
328
+ return false;
329
+ if (value === 'null' || value === '')
330
+ return null;
331
+ if (/^-?\d+$/.test(value))
332
+ return parseInt(value, 10);
333
+ if (/^-?\d+\.\d+$/.test(value))
334
+ return parseFloat(value);
335
+ return value;
1120
336
  }
1121
- exports.setLoggerProvider = setLoggerProvider;
1122
337
  //# sourceMappingURL=FileConfigFactory.js.map