@opentelemetry/configuration 0.218.0 → 0.220.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.
- package/README.md +76 -9
- package/build/src/EnvironmentConfigFactory.js +25 -28
- package/build/src/EnvironmentConfigFactory.js.map +1 -1
- package/build/src/FileConfigFactory.d.ts +19 -1
- package/build/src/FileConfigFactory.js +90 -278
- package/build/src/FileConfigFactory.js.map +1 -1
- package/build/src/generated/types.d.ts +611 -604
- package/build/src/generated/types.js +1 -1
- package/build/src/generated/types.js.map +1 -1
- package/build/src/generated/validator.js +688 -568
- package/build/src/generated/validator.js.map +1 -1
- package/build/src/index.d.ts +2 -1
- package/build/src/index.js +4 -1
- package/build/src/index.js.map +1 -1
- package/build/src/utils.d.ts +12 -1
- package/build/src/utils.js +94 -19
- package/build/src/utils.js.map +1 -1
- package/build/src/version.d.ts +1 -1
- package/build/src/version.js +1 -1
- package/build/src/version.js.map +1 -1
- package/package.json +8 -8
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.parseConfigFile = exports.FileConfigFactory = void 0;
|
|
7
|
+
exports.mergePropagatorCompositeConfig = exports.mergeResourceAttributesConfig = exports.parseConfigFile = exports.FileConfigFactory = void 0;
|
|
8
|
+
const api_1 = require("@opentelemetry/api");
|
|
8
9
|
const core_1 = require("@opentelemetry/core");
|
|
9
10
|
const fs = require("fs");
|
|
10
11
|
const yaml = require("yaml");
|
|
@@ -22,19 +23,39 @@ class FileConfigFactory {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
exports.FileConfigFactory = FileConfigFactory;
|
|
26
|
+
// The schema version this SDK targets. Per the configuration versioning spec,
|
|
27
|
+
// an implementation MUST reject a config file whose MAJOR version differs (a
|
|
28
|
+
// breaking-change boundary), accepts any MINOR version within that major, and
|
|
29
|
+
// SHOULD warn when the MINOR version is newer than it targets since unknown
|
|
30
|
+
// settings may be ignored. See
|
|
31
|
+
// https://github.com/open-telemetry/opentelemetry-configuration/blob/main/VERSIONING.md
|
|
32
|
+
const SUPPORTED_FILE_FORMAT_MAJOR = 1;
|
|
33
|
+
const SUPPORTED_FILE_FORMAT_MINOR = 1;
|
|
34
|
+
function validateFileFormat(configFile, fileFormat) {
|
|
35
|
+
if (typeof fileFormat !== 'string' || fileFormat.length === 0) {
|
|
36
|
+
throw new Error(`${configFile}: Unsupported file_format: "${fileFormat}". Expected a "MAJOR.MINOR" version string.`);
|
|
37
|
+
}
|
|
38
|
+
// Drop any pre-release / meta tag, e.g. "1.0-rc.2" -> "1.0".
|
|
39
|
+
const parts = fileFormat.split('-', 1)[0].split('.');
|
|
40
|
+
const major = Number(parts[0]);
|
|
41
|
+
const minor = parts.length > 1 ? Number(parts[1]) : 0;
|
|
42
|
+
if (!Number.isInteger(major) || !Number.isInteger(minor)) {
|
|
43
|
+
throw new Error(`${configFile}: Unsupported file_format: "${fileFormat}". Expected "MAJOR.MINOR" version numbers.`);
|
|
44
|
+
}
|
|
45
|
+
if (major !== SUPPORTED_FILE_FORMAT_MAJOR) {
|
|
46
|
+
throw new Error(`${configFile}: Unsupported file_format: "${fileFormat}". This SDK supports schema version ${SUPPORTED_FILE_FORMAT_MAJOR}.x.`);
|
|
47
|
+
}
|
|
48
|
+
if (minor > SUPPORTED_FILE_FORMAT_MINOR) {
|
|
49
|
+
api_1.diag.warn(`${configFile}: Configuration file_format "${fileFormat}" has a newer minor version than this SDK supports (${SUPPORTED_FILE_FORMAT_MAJOR}.${SUPPORTED_FILE_FORMAT_MINOR}); some settings may be ignored.`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
25
52
|
function parseConfigFile() {
|
|
26
|
-
const supportedFileVersionPattern = /^1\.0$/;
|
|
27
53
|
const configFile = (0, core_1.getStringFromEnv)('OTEL_CONFIG_FILE') || '';
|
|
28
54
|
const file = fs.readFileSync(configFile, 'utf8');
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const processed =
|
|
32
|
-
|
|
33
|
-
if (!fileFormat || !supportedFileVersionPattern.test(String(fileFormat))) {
|
|
34
|
-
throw new Error(`${configFile}: Unsupported file_format: "${fileFormat}". Must match ${supportedFileVersionPattern}.`);
|
|
35
|
-
}
|
|
36
|
-
// Normalize YAML null type-tag values to {} before validation
|
|
37
|
-
normalizeYamlNulls(processed);
|
|
55
|
+
const doc = yaml.parseDocument(file, { version: '1.2' });
|
|
56
|
+
(0, utils_1.substituteEnvVars)(doc);
|
|
57
|
+
const processed = doc.toJS();
|
|
58
|
+
validateFileFormat(configFile, processed?.file_format);
|
|
38
59
|
const valid = validateConfig(processed);
|
|
39
60
|
if (!valid) {
|
|
40
61
|
let detail;
|
|
@@ -58,292 +79,83 @@ function parseConfigFile() {
|
|
|
58
79
|
const data = processed;
|
|
59
80
|
// Strip file_format from output — it's a meta-field, not a config value
|
|
60
81
|
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
82
|
return data;
|
|
75
83
|
}
|
|
76
84
|
exports.parseConfigFile = parseConfigFile;
|
|
77
85
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
86
|
+
* Merge attributes from `resource.attributes` and `resource.attributes_list`
|
|
87
|
+
* (comma-separated key=value pairs) into an `AttributeNameValue[]` array.
|
|
88
|
+
* Entries already in `resource.attributes` taking precedence.
|
|
89
|
+
*
|
|
90
|
+
* Note: The returned array might not be a copy, so it should not be mutated.
|
|
81
91
|
*/
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
function mergeResourceAttributesConfig(attributes, attributes_list) {
|
|
93
|
+
if (!attributes_list) {
|
|
94
|
+
return attributes;
|
|
95
|
+
}
|
|
96
|
+
const mergedAttrs = attributes ? attributes.slice() : [];
|
|
97
|
+
const existingNames = new Set(mergedAttrs.map(a => a.name));
|
|
98
|
+
// TODO(6842): handle attribute limits, if any
|
|
99
|
+
let discard = false;
|
|
100
|
+
const listAttrs = {};
|
|
101
|
+
for (const pair of attributes_list.split(',')) {
|
|
102
|
+
if (pair.trim() === '')
|
|
103
|
+
continue;
|
|
104
|
+
const parts = pair.split('=');
|
|
105
|
+
if (parts.length !== 2) {
|
|
106
|
+
api_1.diag.warn(`Invalid format for resource.attributes_list entry "${pair}": expected key=value with '=' percent-encoded in keys/values. Discarding all entries.`);
|
|
107
|
+
discard = true;
|
|
108
|
+
break;
|
|
86
109
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
applyEncoding(reader.periodic?.exporter?.otlp_http);
|
|
94
|
-
}
|
|
95
|
-
for (const processor of data.logger_provider?.processors ?? []) {
|
|
96
|
-
applyEncoding(processor.batch?.exporter?.otlp_http);
|
|
97
|
-
applyEncoding(processor.simple?.exporter?.otlp_http);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
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 = [];
|
|
111
|
-
}
|
|
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' });
|
|
120
|
-
}
|
|
110
|
+
const rawName = parts[0].trim();
|
|
111
|
+
const rawValue = parts[1].trim();
|
|
112
|
+
if (rawName === '') {
|
|
113
|
+
api_1.diag.warn(`Empty attribute name in resource.attributes_list entry "${pair}". Discarding all entries.`);
|
|
114
|
+
discard = true;
|
|
115
|
+
break;
|
|
121
116
|
}
|
|
122
|
-
|
|
123
|
-
|
|
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 = [];
|
|
135
|
-
}
|
|
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]: {},
|
|
142
|
-
});
|
|
117
|
+
try {
|
|
118
|
+
listAttrs[decodeURIComponent(rawName)] = decodeURIComponent(rawValue);
|
|
143
119
|
}
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
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);
|
|
164
|
-
}
|
|
165
|
-
for (const processor of data.logger_provider?.processors ?? []) {
|
|
166
|
-
if (processor.batch)
|
|
167
|
-
applyDefaults(processor.batch);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
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 };
|
|
120
|
+
catch (e) {
|
|
121
|
+
api_1.diag.warn(`Failed to percent-decode resource.attributes_list entry "${pair}", discarding all entries: ${e}`);
|
|
122
|
+
discard = true;
|
|
123
|
+
break;
|
|
184
124
|
}
|
|
185
125
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
* so consumers see consistent behaviour regardless of config source.
|
|
191
|
-
*/
|
|
192
|
-
function applyConfigDefaults(data) {
|
|
193
|
-
if (data.disabled == null) {
|
|
194
|
-
data.disabled = false;
|
|
195
|
-
}
|
|
196
|
-
if (data.log_level == null) {
|
|
197
|
-
data.log_level = 'info';
|
|
198
|
-
}
|
|
199
|
-
if (data.attribute_limits == null) {
|
|
200
|
-
data.attribute_limits = { attribute_count_limit: 128 };
|
|
201
|
-
}
|
|
202
|
-
else if (data.attribute_limits.attribute_count_limit == null) {
|
|
203
|
-
data.attribute_limits.attribute_count_limit = 128;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
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];
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
stripNulls(val);
|
|
126
|
+
if (!discard) {
|
|
127
|
+
for (const [name, value] of Object.entries(listAttrs)) {
|
|
128
|
+
if (!existingNames.has(name)) {
|
|
129
|
+
mergedAttrs.push({ name, value, type: 'string' });
|
|
235
130
|
}
|
|
236
131
|
}
|
|
237
132
|
}
|
|
133
|
+
return mergedAttrs;
|
|
238
134
|
}
|
|
135
|
+
exports.mergeResourceAttributesConfig = mergeResourceAttributesConfig;
|
|
239
136
|
/**
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
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.).
|
|
137
|
+
* Merge propagator.composite_list (comma-separated propagator names) into
|
|
138
|
+
* propagator.composite, with entries already in composite taking precedence.
|
|
139
|
+
* Merge TextMapPropagator configs from `propagator.composite` and `propagator.composite_list`
|
|
140
|
+
* (comma-separated names) into a `TextMapPropagator[]` array.
|
|
141
|
+
* Entries already in `propagator.composite` taking precedence.
|
|
279
142
|
*
|
|
280
|
-
*
|
|
281
|
-
* are NOT in NESTED_OBJECT_TYPE_TAGS and are left as-is; applyConfigDefaults()
|
|
282
|
-
* handles the boolean/string ones that need defaults.
|
|
143
|
+
* Note: The returned array might not be a copy, so it should not be mutated.
|
|
283
144
|
*/
|
|
284
|
-
function
|
|
285
|
-
if (
|
|
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] = {};
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
normalizeYamlNulls(item);
|
|
298
|
-
}
|
|
145
|
+
function mergePropagatorCompositeConfig(composite, composite_list) {
|
|
146
|
+
if (!composite_list) {
|
|
147
|
+
return composite;
|
|
299
148
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
normalizeYamlNulls(val);
|
|
310
|
-
}
|
|
149
|
+
const mergedComposite = composite ? composite.slice() : [];
|
|
150
|
+
const existingNames = new Set(mergedComposite.map(c => Object.keys(c)[0]));
|
|
151
|
+
for (const name of composite_list.split(',')) {
|
|
152
|
+
const trimmed = name.trim();
|
|
153
|
+
if (trimmed && !existingNames.has(trimmed)) {
|
|
154
|
+
mergedComposite.push({ [trimmed]: null });
|
|
155
|
+
existingNames.add(trimmed);
|
|
311
156
|
}
|
|
312
157
|
}
|
|
158
|
+
return mergedComposite;
|
|
313
159
|
}
|
|
314
|
-
|
|
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;
|
|
322
|
-
}
|
|
323
|
-
if (Array.isArray(obj)) {
|
|
324
|
-
return obj.map(substituteEnvVars);
|
|
325
|
-
}
|
|
326
|
-
if (typeof obj === 'object' && obj !== null) {
|
|
327
|
-
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, substituteEnvVars(v)]));
|
|
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;
|
|
348
|
-
}
|
|
160
|
+
exports.mergePropagatorCompositeConfig = mergePropagatorCompositeConfig;
|
|
349
161
|
//# sourceMappingURL=FileConfigFactory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileConfigFactory.js","sourceRoot":"","sources":["../../src/FileConfigFactory.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { getStringFromEnv } from '@opentelemetry/core';\nimport type { ConfigFactory } from './IConfigFactory';\nimport * as fs from 'fs';\nimport * as yaml from 'yaml';\nimport { envVariableSubstitution } from './utils';\nimport type {\n BatchLogRecordProcessor,\n BatchSpanProcessor,\n CardinalityLimits,\n ConfigurationModel,\n PeriodicMetricReader,\n} from './generated/types';\n// Pre-compiled ajv validator — eliminates runtime schema compilation on cold start.\n// Generated by `npm run generate:config`; do not import ajv directly here.\nimport * as validateConfig from './generated/validator';\n\nexport class FileConfigFactory implements ConfigFactory {\n private _config: ConfigurationModel;\n\n constructor() {\n this._config = parseConfigFile();\n }\n\n getConfigModel(): ConfigurationModel {\n return this._config;\n }\n}\n\nexport function parseConfigFile(): ConfigurationModel {\n const supportedFileVersionPattern = /^1\\.0$/;\n const configFile = getStringFromEnv('OTEL_CONFIG_FILE') || '';\n const file = fs.readFileSync(configFile, 'utf8');\n\n // Apply env var substitution to all string values before schema parsing\n const rawParsed = yaml.parse(file) as Record<string, unknown>;\n const processed = substituteEnvVars(rawParsed) as Record<string, unknown>;\n\n const fileFormat = processed?.file_format;\n if (!fileFormat || !supportedFileVersionPattern.test(String(fileFormat))) {\n throw new Error(\n `${configFile}: Unsupported file_format: \"${fileFormat}\". Must match ${supportedFileVersionPattern}.`\n );\n }\n\n // Normalize YAML null type-tag values to {} before validation\n normalizeYamlNulls(processed);\n\n const valid = validateConfig(processed);\n if (!valid) {\n let detail: string;\n if (!validateConfig.errors) {\n detail = 'unknown error';\n } else if (validateConfig.errors.length === 1) {\n const err = validateConfig.errors[0];\n detail = `${err.instancePath} ${err.message}`;\n } else {\n const sep = '\\n ';\n detail =\n sep +\n validateConfig.errors\n .map(e => `${e.instancePath} ${e.message}`)\n .join(sep);\n }\n throw new Error(\n `Invalid OpenTelemetry config file: ${configFile}: ${detail}`\n );\n }\n\n const data = processed as unknown as ConfigurationModel;\n\n // Strip file_format from output — it's a meta-field, not a config value\n delete (data as Record<string, unknown>)['file_format'];\n\n // The generated TypeScript types omit `| null` from unions (see\n // generate-config.js) because consumers expect `T | undefined`, not\n // `T | null`. To match, we delete any properties still set to `null`\n // after YAML parsing so that accessing them returns `undefined`.\n // Runs after normalizeYamlNulls, which has already converted type-tag\n // nulls (e.g. `console:`) to `{}`.\n stripNulls(processed);\n\n applyConfigDefaults(data);\n mergeAttributesList(data);\n mergeCompositeList(data);\n applyBatchProcessorDefaults(data);\n applyPeriodicReaderDefaults(data);\n applyOtlpHttpEncodingDefaults(data);\n\n return data;\n}\n\n/**\n * Apply default encoding ('protobuf') to all otlp_http exporters in the config\n * where encoding was not explicitly specified. The spec defines protobuf as the\n * default encoding for OTLP HTTP exporters.\n */\nfunction applyOtlpHttpEncodingDefaults(data: ConfigurationModel): void {\n const applyEncoding = (\n exporter: { encoding?: string | null } | null | undefined\n ) => {\n if (exporter && exporter.encoding == null) {\n exporter.encoding = 'protobuf';\n }\n };\n\n for (const processor of data.tracer_provider?.processors ?? []) {\n applyEncoding(processor.batch?.exporter?.otlp_http);\n applyEncoding(processor.simple?.exporter?.otlp_http);\n }\n\n for (const reader of data.meter_provider?.readers ?? []) {\n applyEncoding(reader.periodic?.exporter?.otlp_http);\n }\n\n for (const processor of data.logger_provider?.processors ?? []) {\n applyEncoding(processor.batch?.exporter?.otlp_http);\n applyEncoding(processor.simple?.exporter?.otlp_http);\n }\n}\n\n/**\n * Merge resource.attributes_list (comma-separated key=value pairs) into\n * resource.attributes, with entries already in attributes taking precedence.\n */\nfunction mergeAttributesList(data: ConfigurationModel): void {\n const resource = data.resource;\n const list = resource?.attributes_list;\n if (typeof list !== 'string' || !list.trim()) return;\n\n if (resource!.attributes == null) {\n resource!.attributes = [];\n }\n\n const existingKeys = new Set(\n resource!.attributes.map((a: { name: string }) => a.name)\n );\n\n for (const pair of list.split(',')) {\n const eqIdx = pair.indexOf('=');\n if (eqIdx > 0) {\n const key = pair.slice(0, eqIdx).trim();\n const value = pair.slice(eqIdx + 1).trim();\n if (key && !existingKeys.has(key)) {\n resource!.attributes.push({ name: key, value, type: 'string' });\n }\n }\n }\n}\n\n/**\n * Merge propagator.composite_list (comma-separated propagator names) into\n * propagator.composite, with entries already in composite taking precedence.\n */\nfunction mergeCompositeList(data: ConfigurationModel): void {\n const propagator = data.propagator;\n const list = propagator?.composite_list;\n if (typeof list !== 'string' || !list.trim()) return;\n\n if (propagator!.composite == null) {\n propagator!.composite = [];\n }\n\n const existingNames = new Set(\n (propagator!.composite as Array<Record<string, unknown>>).map(\n entry => Object.keys(entry)[0]\n )\n );\n\n for (const name of list.split(',')) {\n const trimmed = name.trim();\n if (trimmed && !existingNames.has(trimmed)) {\n (propagator!.composite as Array<Record<string, unknown>>).push({\n [trimmed]: {},\n });\n }\n }\n}\n\n/**\n * Apply spec-defined defaults for batch span/log-record processor fields that\n * are not encoded in the JSON schema.\n */\nfunction applyBatchProcessorDefaults(data: ConfigurationModel): void {\n const applyDefaults = (\n batch: BatchSpanProcessor | BatchLogRecordProcessor\n ) => {\n if (batch.schedule_delay == null) batch.schedule_delay = 5000;\n if (batch.export_timeout == null) batch.export_timeout = 30000;\n if (batch.max_queue_size == null) batch.max_queue_size = 2048;\n if (batch.max_export_batch_size == null) batch.max_export_batch_size = 512;\n };\n\n for (const processor of data.tracer_provider?.processors ?? []) {\n if (processor.batch) applyDefaults(processor.batch);\n }\n for (const processor of data.logger_provider?.processors ?? []) {\n if (processor.batch) applyDefaults(processor.batch);\n }\n}\n\n/**\n * Apply spec-defined defaults for periodic metric reader fields.\n */\nfunction applyPeriodicReaderDefaults(data: ConfigurationModel): void {\n for (const reader of data.meter_provider?.readers ?? []) {\n const periodic = reader.periodic as PeriodicMetricReader | undefined;\n if (!periodic) continue;\n if (periodic.interval == null) periodic.interval = 60000;\n if (periodic.timeout == null) periodic.timeout = 30000;\n if (periodic.cardinality_limits == null) {\n periodic.cardinality_limits = { default: 2000 } as CardinalityLimits;\n }\n }\n}\n\n/**\n * Apply spec-defined defaults that are not encoded in the JSON schema.\n * Both FileConfigFactory and EnvironmentConfigFactory apply the same defaults\n * so consumers see consistent behaviour regardless of config source.\n */\nfunction applyConfigDefaults(data: ConfigurationModel): void {\n if (data.disabled == null) {\n data.disabled = false;\n }\n if (data.log_level == null) {\n data.log_level = 'info';\n }\n if (data.attribute_limits == null) {\n data.attribute_limits = { attribute_count_limit: 128 };\n } else if (data.attribute_limits.attribute_count_limit == null) {\n data.attribute_limits.attribute_count_limit = 128;\n }\n}\n\n/**\n * Recursively delete object properties whose value is `null`.\n *\n * YAML `key:` (no value) parses to `{ key: null }`. The generated TypeScript\n * types use `?: T` (not `T | null`) because downstream consumers like sdk-node\n * assign config values to interfaces that only accept `T | undefined`. The\n * codegen script (generate-config.js) strips `| null` from type unions; this\n * function makes the runtime data match by deleting null-valued keys so that\n * property access returns `undefined` instead of `null`.\n *\n * Must run AFTER normalizeYamlNulls, which converts type-tag nulls\n * (e.g. `console: null` → `console: {}`) — those are already `{}` by the\n * time this function runs and won't be affected.\n */\nfunction stripNulls(value: unknown): void {\n if (value == null || typeof value !== 'object') return;\n if (Array.isArray(value)) {\n for (const item of value) {\n stripNulls(item);\n }\n } else {\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n if (val === null) {\n delete (value as Record<string, unknown>)[key];\n } else {\n stripNulls(val);\n }\n }\n }\n}\n\n/**\n * Object type-tag keys that may appear as nested (non-array-element) properties.\n * YAML `console:` (no value) parses to `{ console: null }`, but the SDK checks\n * `if (exporter.console)` — truthy — so null must become {}.\n *\n * We exclude keys that are also used as nullable primitives in other contexts\n * (e.g. `host` is a string field in prometheus config; detectors use it as a\n * type-tag but only as array elements, which are already handled below).\n */\nconst NESTED_OBJECT_TYPE_TAGS = new Set([\n // Exporter type discriminators (nested inside simple/batch processor or reader)\n 'console',\n 'otlp_http',\n 'otlp_grpc',\n // Sampler type discriminators (nested inside tracer_provider.sampler)\n 'always_on',\n 'always_off',\n 'trace_id_ratio_based',\n 'parent_based',\n 'jaeger_remote',\n // Propagator type discriminators (array elements, but covered here for completeness)\n 'tracecontext',\n 'baggage',\n 'b3',\n 'b3multi',\n 'xray',\n // Telemetry producer type discriminators\n 'opencensus',\n]);\n\n/**\n * Normalizes YAML null values to empty objects for discriminated-union type-tag fields.\n *\n * YAML `key:` (no value) parses to `{ key: null }`, but the SDK checks `if (obj.key)`\n * to determine which type to instantiate — null fails that check. Two cases:\n *\n * 1. Array element properties: convert ALL null-valued keys to {} (handles detectors,\n * composite propagators, and most exporter/processor discriminators).\n * 2. Nested NESTED_OBJECT_TYPE_TAGS: convert known type-discriminator keys to {} when\n * encountered anywhere in the tree (handles sampler types, console exporter nested\n * inside simple/batch, etc.).\n *\n * Top-level nullable primitive fields (disabled, log_level, endpoint, ca_file, etc.)\n * are NOT in NESTED_OBJECT_TYPE_TAGS and are left as-is; applyConfigDefaults()\n * handles the boolean/string ones that need defaults.\n */\nfunction normalizeYamlNulls(value: unknown): void {\n if (value == null || typeof value !== 'object') return;\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item != null && typeof item === 'object' && !Array.isArray(item)) {\n // All null-valued properties of array elements are type-tag discriminators.\n for (const key of Object.keys(item as object)) {\n if ((item as Record<string, unknown>)[key] === null) {\n (item as Record<string, unknown>)[key] = {};\n }\n }\n }\n normalizeYamlNulls(item);\n }\n } else {\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n if (\n val === null &&\n // Known type-tag keys, or slash-qualified experimental type-tags\n // (e.g. otlp_file/development, prometheus/development)\n (NESTED_OBJECT_TYPE_TAGS.has(key) || key.includes('/'))\n ) {\n (value as Record<string, unknown>)[key] = {};\n } else {\n normalizeYamlNulls(val);\n }\n }\n }\n}\n\nconst ENV_VAR_PATTERN = /\\$\\{[^}]+\\}/;\n\nfunction substituteEnvVars(obj: unknown): unknown {\n if (typeof obj === 'string') {\n // Only coerce if the string contained env var substitution syntax,\n // so that plain YAML strings (e.g. quoted \"1234\") are not type-coerced.\n const hasSubstitution = ENV_VAR_PATTERN.test(obj);\n const substituted = envVariableSubstitution(obj);\n return hasSubstitution ? yamlCoerce(substituted) : substituted;\n }\n if (Array.isArray(obj)) {\n return obj.map(substituteEnvVars);\n }\n if (typeof obj === 'object' && obj !== null) {\n return Object.fromEntries(\n Object.entries(obj).map(([k, v]) => [k, substituteEnvVars(v)])\n );\n }\n return obj;\n}\n\n/**\n * Re-coerce a string value to its YAML-equivalent primitive type.\n * Env var substitution always returns strings; this converts them back\n * to booleans/numbers/null where the schema expects those types.\n */\nfunction yamlCoerce(value: string): unknown {\n if (value === 'true') return true;\n if (value === 'false') return false;\n if (value === 'null' || value === '') return null;\n if (/^-?\\d+$/.test(value)) return parseInt(value, 10);\n if (/^-?\\d+\\.\\d+$/.test(value)) return parseFloat(value);\n return value;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"FileConfigFactory.js","sourceRoot":"","sources":["../../src/FileConfigFactory.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { diag } from '@opentelemetry/api';\nimport { getStringFromEnv } from '@opentelemetry/core';\nimport * as fs from 'fs';\nimport * as yaml from 'yaml';\nimport type { ConfigFactory } from './IConfigFactory';\nimport { substituteEnvVars } from './utils';\nimport type {\n AttributeNameValue,\n ConfigurationModel,\n TextMapPropagator,\n} from './generated/types';\n// Pre-compiled ajv validator — eliminates runtime schema compilation on cold start.\n// Generated by `npm run generate:config`; do not import ajv directly here.\nimport * as validateConfig from './generated/validator';\n\nexport class FileConfigFactory implements ConfigFactory {\n private _config: ConfigurationModel;\n\n constructor() {\n this._config = parseConfigFile();\n }\n\n getConfigModel(): ConfigurationModel {\n return this._config;\n }\n}\n\n// The schema version this SDK targets. Per the configuration versioning spec,\n// an implementation MUST reject a config file whose MAJOR version differs (a\n// breaking-change boundary), accepts any MINOR version within that major, and\n// SHOULD warn when the MINOR version is newer than it targets since unknown\n// settings may be ignored. See\n// https://github.com/open-telemetry/opentelemetry-configuration/blob/main/VERSIONING.md\nconst SUPPORTED_FILE_FORMAT_MAJOR = 1;\nconst SUPPORTED_FILE_FORMAT_MINOR = 1;\n\nfunction validateFileFormat(configFile: string, fileFormat: unknown): void {\n if (typeof fileFormat !== 'string' || fileFormat.length === 0) {\n throw new Error(\n `${configFile}: Unsupported file_format: \"${fileFormat}\". Expected a \"MAJOR.MINOR\" version string.`\n );\n }\n\n // Drop any pre-release / meta tag, e.g. \"1.0-rc.2\" -> \"1.0\".\n const parts = fileFormat.split('-', 1)[0].split('.');\n const major = Number(parts[0]);\n const minor = parts.length > 1 ? Number(parts[1]) : 0;\n if (!Number.isInteger(major) || !Number.isInteger(minor)) {\n throw new Error(\n `${configFile}: Unsupported file_format: \"${fileFormat}\". Expected \"MAJOR.MINOR\" version numbers.`\n );\n }\n\n if (major !== SUPPORTED_FILE_FORMAT_MAJOR) {\n throw new Error(\n `${configFile}: Unsupported file_format: \"${fileFormat}\". This SDK supports schema version ${SUPPORTED_FILE_FORMAT_MAJOR}.x.`\n );\n }\n\n if (minor > SUPPORTED_FILE_FORMAT_MINOR) {\n diag.warn(\n `${configFile}: Configuration file_format \"${fileFormat}\" has a newer minor version than this SDK supports (${SUPPORTED_FILE_FORMAT_MAJOR}.${SUPPORTED_FILE_FORMAT_MINOR}); some settings may be ignored.`\n );\n }\n}\n\nexport function parseConfigFile(): ConfigurationModel {\n const configFile = getStringFromEnv('OTEL_CONFIG_FILE') || '';\n const file = fs.readFileSync(configFile, 'utf8');\n\n const doc = yaml.parseDocument(file, { version: '1.2' });\n substituteEnvVars(doc);\n const processed = doc.toJS() as Record<string, unknown>;\n\n validateFileFormat(configFile, processed?.file_format);\n\n const valid = validateConfig(processed);\n if (!valid) {\n let detail: string;\n if (!validateConfig.errors) {\n detail = 'unknown error';\n } else if (validateConfig.errors.length === 1) {\n const err = validateConfig.errors[0];\n detail = `${err.instancePath} ${err.message}`;\n } else {\n const sep = '\\n ';\n detail =\n sep +\n validateConfig.errors\n .map(e => `${e.instancePath} ${e.message}`)\n .join(sep);\n }\n throw new Error(\n `Invalid OpenTelemetry config file: ${configFile}: ${detail}`\n );\n }\n\n const data = processed as unknown as ConfigurationModel;\n\n // Strip file_format from output — it's a meta-field, not a config value\n delete (data as Record<string, unknown>)['file_format'];\n\n return data;\n}\n\n/**\n * Merge attributes from `resource.attributes` and `resource.attributes_list`\n * (comma-separated key=value pairs) into an `AttributeNameValue[]` array.\n * Entries already in `resource.attributes` taking precedence.\n *\n * Note: The returned array might not be a copy, so it should not be mutated.\n */\nexport function mergeResourceAttributesConfig(\n attributes?: AttributeNameValue[],\n attributes_list?: string | null\n): AttributeNameValue[] | undefined {\n if (!attributes_list) {\n return attributes;\n }\n\n const mergedAttrs = attributes ? attributes.slice() : [];\n const existingNames = new Set(mergedAttrs.map(a => a.name));\n\n // TODO(6842): handle attribute limits, if any\n let discard = false;\n const listAttrs: Record<string, string> = {};\n for (const pair of attributes_list.split(',')) {\n if (pair.trim() === '') continue;\n\n const parts = pair.split('=');\n if (parts.length !== 2) {\n diag.warn(\n `Invalid format for resource.attributes_list entry \"${pair}\": expected key=value with '=' percent-encoded in keys/values. Discarding all entries.`\n );\n discard = true;\n break;\n }\n\n const rawName = parts[0].trim();\n const rawValue = parts[1].trim();\n if (rawName === '') {\n diag.warn(\n `Empty attribute name in resource.attributes_list entry \"${pair}\". Discarding all entries.`\n );\n discard = true;\n break;\n }\n\n try {\n listAttrs[decodeURIComponent(rawName)] = decodeURIComponent(rawValue);\n } catch (e) {\n diag.warn(\n `Failed to percent-decode resource.attributes_list entry \"${pair}\", discarding all entries: ${e}`\n );\n discard = true;\n break;\n }\n }\n\n if (!discard) {\n for (const [name, value] of Object.entries(listAttrs)) {\n if (!existingNames.has(name)) {\n mergedAttrs.push({ name, value, type: 'string' });\n }\n }\n }\n\n return mergedAttrs;\n}\n\n/**\n * Merge propagator.composite_list (comma-separated propagator names) into\n * propagator.composite, with entries already in composite taking precedence.\n * Merge TextMapPropagator configs from `propagator.composite` and `propagator.composite_list`\n * (comma-separated names) into a `TextMapPropagator[]` array.\n * Entries already in `propagator.composite` taking precedence.\n *\n * Note: The returned array might not be a copy, so it should not be mutated.\n */\nexport function mergePropagatorCompositeConfig(\n composite?: TextMapPropagator[],\n composite_list?: string | null\n): TextMapPropagator[] | undefined {\n if (!composite_list) {\n return composite;\n }\n\n const mergedComposite = composite ? composite.slice() : [];\n const existingNames = new Set(mergedComposite.map(c => Object.keys(c)[0]));\n\n for (const name of composite_list.split(',')) {\n const trimmed = name.trim();\n if (trimmed && !existingNames.has(trimmed)) {\n mergedComposite.push({ [trimmed]: null });\n existingNames.add(trimmed);\n }\n }\n\n return mergedComposite;\n}\n"]}
|