@opentelemetry/configuration 0.216.0 → 0.218.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 +281 -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 +8413 -0
  17. package/build/src/generated/validator.js.map +1 -0
  18. package/build/src/index.d.ts +1 -5
  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 +5 -2
  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,346 @@
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
+ let detail;
41
+ if (!validateConfig.errors) {
42
+ detail = 'unknown error';
43
+ }
44
+ else if (validateConfig.errors.length === 1) {
45
+ const err = validateConfig.errors[0];
46
+ detail = `${err.instancePath} ${err.message}`;
47
+ }
48
+ else {
49
+ const sep = '\n ';
50
+ detail =
51
+ sep +
52
+ validateConfig.errors
53
+ .map(e => `${e.instancePath} ${e.message}`)
54
+ .join(sep);
55
+ }
56
+ throw new Error(`Invalid OpenTelemetry config file: ${configFile}: ${detail}`);
80
57
  }
58
+ const data = processed;
59
+ // Strip file_format from output — it's a meta-field, not a config value
60
+ delete data['file_format'];
61
+ // The generated TypeScript types omit `| null` from unions (see
62
+ // generate-config.js) because consumers expect `T | undefined`, not
63
+ // `T | null`. To match, we delete any properties still set to `null`
64
+ // after YAML parsing so that accessing them returns `undefined`.
65
+ // Runs after normalizeYamlNulls, which has already converted type-tag
66
+ // nulls (e.g. `console:`) to `{}`.
67
+ stripNulls(processed);
68
+ applyConfigDefaults(data);
69
+ mergeAttributesList(data);
70
+ mergeCompositeList(data);
71
+ applyBatchProcessorDefaults(data);
72
+ applyPeriodicReaderDefaults(data);
73
+ applyOtlpHttpEncodingDefaults(data);
74
+ return data;
81
75
  }
82
76
  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
- }
77
+ /**
78
+ * Apply default encoding ('protobuf') to all otlp_http exporters in the config
79
+ * where encoding was not explicitly specified. The spec defines protobuf as the
80
+ * default encoding for OTLP HTTP exporters.
81
+ */
82
+ function applyOtlpHttpEncodingDefaults(data) {
83
+ const applyEncoding = (exporter) => {
84
+ if (exporter && exporter.encoding == null) {
85
+ exporter.encoding = 'protobuf';
142
86
  }
87
+ };
88
+ for (const processor of data.tracer_provider?.processors ?? []) {
89
+ applyEncoding(processor.batch?.exporter?.otlp_http);
90
+ applyEncoding(processor.simple?.exporter?.otlp_http);
143
91
  }
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
- }
92
+ for (const reader of data.meter_provider?.readers ?? []) {
93
+ applyEncoding(reader.periodic?.exporter?.otlp_http);
164
94
  }
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
- }
95
+ for (const processor of data.logger_provider?.processors ?? []) {
96
+ applyEncoding(processor.batch?.exporter?.otlp_http);
97
+ applyEncoding(processor.simple?.exporter?.otlp_http);
188
98
  }
189
- return result;
190
99
  }
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
- }
100
+ /**
101
+ * Merge resource.attributes_list (comma-separated key=value pairs) into
102
+ * resource.attributes, with entries already in attributes taking precedence.
103
+ */
104
+ function mergeAttributesList(data) {
105
+ const resource = data.resource;
106
+ const list = resource?.attributes_list;
107
+ if (typeof list !== 'string' || !list.trim())
108
+ return;
109
+ if (resource.attributes == null) {
110
+ resource.attributes = [];
206
111
  }
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 });
112
+ const existingKeys = new Set(resource.attributes.map((a) => a.name));
113
+ for (const pair of list.split(',')) {
114
+ const eqIdx = pair.indexOf('=');
115
+ if (eqIdx > 0) {
116
+ const key = pair.slice(0, eqIdx).trim();
117
+ const value = pair.slice(eqIdx + 1).trim();
118
+ if (key && !existingKeys.has(key)) {
119
+ resource.attributes.push({ name: key, value, type: 'string' });
218
120
  }
219
121
  }
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
122
  }
243
123
  }
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
- }
124
+ /**
125
+ * Merge propagator.composite_list (comma-separated propagator names) into
126
+ * propagator.composite, with entries already in composite taking precedence.
127
+ */
128
+ function mergeCompositeList(data) {
129
+ const propagator = data.propagator;
130
+ const list = propagator?.composite_list;
131
+ if (typeof list !== 'string' || !list.trim())
132
+ return;
133
+ if (propagator.composite == null) {
134
+ propagator.composite = [];
348
135
  }
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'],
136
+ const existingNames = new Set(propagator.composite.map(entry => Object.keys(entry)[0]));
137
+ for (const name of list.split(',')) {
138
+ const trimmed = name.trim();
139
+ if (trimmed && !existingNames.has(trimmed)) {
140
+ propagator.composite.push({
141
+ [trimmed]: {},
359
142
  });
360
143
  }
361
- if (headers.length > 0) {
362
- return headers;
363
- }
364
144
  }
365
- return null;
366
145
  }
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;
146
+ /**
147
+ * Apply spec-defined defaults for batch span/log-record processor fields that
148
+ * are not encoded in the JSON schema.
149
+ */
150
+ function applyBatchProcessorDefaults(data) {
151
+ const applyDefaults = (batch) => {
152
+ if (batch.schedule_delay == null)
153
+ batch.schedule_delay = 5000;
154
+ if (batch.export_timeout == null)
155
+ batch.export_timeout = 30000;
156
+ if (batch.max_queue_size == null)
157
+ batch.max_queue_size = 2048;
158
+ if (batch.max_export_batch_size == null)
159
+ batch.max_export_batch_size = 512;
160
+ };
161
+ for (const processor of data.tracer_provider?.processors ?? []) {
162
+ if (processor.batch)
163
+ applyDefaults(processor.batch);
394
164
  }
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;
165
+ for (const processor of data.logger_provider?.processors ?? []) {
166
+ if (processor.batch)
167
+ applyDefaults(processor.batch);
479
168
  }
480
- return parsedExporter;
481
169
  }
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
- }
170
+ /**
171
+ * Apply spec-defined defaults for periodic metric reader fields.
172
+ */
173
+ function applyPeriodicReaderDefaults(data) {
174
+ for (const reader of data.meter_provider?.readers ?? []) {
175
+ const periodic = reader.periodic;
176
+ if (!periodic)
177
+ continue;
178
+ if (periodic.interval == null)
179
+ periodic.interval = 60000;
180
+ if (periodic.timeout == null)
181
+ periodic.timeout = 30000;
182
+ if (periodic.cardinality_limits == null) {
183
+ periodic.cardinality_limits = { default: 2000 };
552
184
  }
553
185
  }
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
186
  }
560
- exports.setTracerProvider = setTracerProvider;
561
- function getCardinalityLimits(limits) {
562
- if (limits == null) {
563
- limits = {};
187
+ /**
188
+ * Apply spec-defined defaults that are not encoded in the JSON schema.
189
+ * Both FileConfigFactory and EnvironmentConfigFactory apply the same defaults
190
+ * so consumers see consistent behaviour regardless of config source.
191
+ */
192
+ function applyConfigDefaults(data) {
193
+ if (data.disabled == null) {
194
+ data.disabled = false;
564
195
  }
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
- }
196
+ if (data.log_level == null) {
197
+ data.log_level = 'info';
587
198
  }
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;
199
+ if (data.attribute_limits == null) {
200
+ data.attribute_limits = { attribute_count_limit: 128 };
601
201
  }
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;
202
+ else if (data.attribute_limits.attribute_count_limit == null) {
203
+ data.attribute_limits.attribute_count_limit = 128;
613
204
  }
614
205
  }
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
- };
206
+ /**
207
+ * Recursively delete object properties whose value is `null`.
208
+ *
209
+ * YAML `key:` (no value) parses to `{ key: null }`. The generated TypeScript
210
+ * types use `?: T` (not `T | null`) because downstream consumers like sdk-node
211
+ * assign config values to interfaces that only accept `T | undefined`. The
212
+ * codegen script (generate-config.js) strips `| null` from type unions; this
213
+ * function makes the runtime data match by deleting null-valued keys so that
214
+ * property access returns `undefined` instead of `null`.
215
+ *
216
+ * Must run AFTER normalizeYamlNulls, which converts type-tag nulls
217
+ * (e.g. `console: null` → `console: {}`) — those are already `{}` by the
218
+ * time this function runs and won't be affected.
219
+ */
220
+ function stripNulls(value) {
221
+ if (value == null || typeof value !== 'object')
222
+ return;
223
+ if (Array.isArray(value)) {
224
+ for (const item of value) {
225
+ stripNulls(item);
226
+ }
227
+ }
228
+ else {
229
+ for (const [key, val] of Object.entries(value)) {
230
+ if (val === null) {
231
+ delete value[key];
710
232
  }
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
- };
233
+ else {
234
+ stripNulls(val);
721
235
  }
722
- break;
236
+ }
723
237
  }
724
- return parsedExporter;
725
238
  }
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;
239
+ /**
240
+ * Object type-tag keys that may appear as nested (non-array-element) properties.
241
+ * YAML `console:` (no value) parses to `{ console: null }`, but the SDK checks
242
+ * `if (exporter.console)` — truthy — so null must become {}.
243
+ *
244
+ * We exclude keys that are also used as nullable primitives in other contexts
245
+ * (e.g. `host` is a string field in prometheus config; detectors use it as a
246
+ * type-tag but only as array elements, which are already handled below).
247
+ */
248
+ const NESTED_OBJECT_TYPE_TAGS = new Set([
249
+ // Exporter type discriminators (nested inside simple/batch processor or reader)
250
+ 'console',
251
+ 'otlp_http',
252
+ 'otlp_grpc',
253
+ // Sampler type discriminators (nested inside tracer_provider.sampler)
254
+ 'always_on',
255
+ 'always_off',
256
+ 'trace_id_ratio_based',
257
+ 'parent_based',
258
+ 'jaeger_remote',
259
+ // Propagator type discriminators (array elements, but covered here for completeness)
260
+ 'tracecontext',
261
+ 'baggage',
262
+ 'b3',
263
+ 'b3multi',
264
+ 'xray',
265
+ // Telemetry producer type discriminators
266
+ 'opencensus',
267
+ ]);
268
+ /**
269
+ * Normalizes YAML null values to empty objects for discriminated-union type-tag fields.
270
+ *
271
+ * YAML `key:` (no value) parses to `{ key: null }`, but the SDK checks `if (obj.key)`
272
+ * to determine which type to instantiate — null fails that check. Two cases:
273
+ *
274
+ * 1. Array element properties: convert ALL null-valued keys to {} (handles detectors,
275
+ * composite propagators, and most exporter/processor discriminators).
276
+ * 2. Nested NESTED_OBJECT_TYPE_TAGS: convert known type-discriminator keys to {} when
277
+ * encountered anywhere in the tree (handles sampler types, console exporter nested
278
+ * inside simple/batch, etc.).
279
+ *
280
+ * Top-level nullable primitive fields (disabled, log_level, endpoint, ca_file, etc.)
281
+ * are NOT in NESTED_OBJECT_TYPE_TAGS and are left as-is; applyConfigDefaults()
282
+ * handles the boolean/string ones that need defaults.
283
+ */
284
+ function normalizeYamlNulls(value) {
285
+ if (value == null || typeof value !== 'object')
286
+ return;
287
+ if (Array.isArray(value)) {
288
+ for (const item of value) {
289
+ if (item != null && typeof item === 'object' && !Array.isArray(item)) {
290
+ // All null-valued properties of array elements are type-tag discriminators.
291
+ for (const key of Object.keys(item)) {
292
+ if (item[key] === null) {
293
+ item[key] = {};
815
294
  }
816
- config.meter_provider.readers.push(periodicReader);
817
295
  }
818
296
  }
297
+ normalizeYamlNulls(item);
819
298
  }
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);
299
+ }
300
+ else {
301
+ for (const [key, val] of Object.entries(value)) {
302
+ if (val === null &&
303
+ // Known type-tag keys, or slash-qualified experimental type-tags
304
+ // (e.g. otlp_file/development, prometheus/development)
305
+ (NESTED_OBJECT_TYPE_TAGS.has(key) || key.includes('/'))) {
306
+ value[key] = {};
307
+ }
308
+ else {
309
+ normalizeYamlNulls(val);
948
310
  }
949
311
  }
950
312
  }
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
313
  }
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;
314
+ const ENV_VAR_PATTERN = /\$\{[^}]+\}/;
315
+ function substituteEnvVars(obj) {
316
+ if (typeof obj === 'string') {
317
+ // Only coerce if the string contained env var substitution syntax,
318
+ // so that plain YAML strings (e.g. quoted "1234") are not type-coerced.
319
+ const hasSubstitution = ENV_VAR_PATTERN.test(obj);
320
+ const substituted = (0, utils_1.envVariableSubstitution)(obj);
321
+ return hasSubstitution ? yamlCoerce(substituted) : substituted;
1010
322
  }
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
- }
323
+ if (Array.isArray(obj)) {
324
+ return obj.map(substituteEnvVars);
1114
325
  }
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');
326
+ if (typeof obj === 'object' && obj !== null) {
327
+ return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, substituteEnvVars(v)]));
1119
328
  }
329
+ return obj;
330
+ }
331
+ /**
332
+ * Re-coerce a string value to its YAML-equivalent primitive type.
333
+ * Env var substitution always returns strings; this converts them back
334
+ * to booleans/numbers/null where the schema expects those types.
335
+ */
336
+ function yamlCoerce(value) {
337
+ if (value === 'true')
338
+ return true;
339
+ if (value === 'false')
340
+ return false;
341
+ if (value === 'null' || value === '')
342
+ return null;
343
+ if (/^-?\d+$/.test(value))
344
+ return parseInt(value, 10);
345
+ if (/^-?\d+\.\d+$/.test(value))
346
+ return parseFloat(value);
347
+ return value;
1120
348
  }
1121
- exports.setLoggerProvider = setLoggerProvider;
1122
349
  //# sourceMappingURL=FileConfigFactory.js.map