"use strict";
/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildSamplerFromConfig = exports.getSamplerFromConfiguration = exports.getInstanceID = exports.getMeterViewsFromConfiguration = exports.getAggregationType = exports.getInstrumentType = exports.getMeterReadersFromConfiguration = exports.getIdGeneratorFromConfiguration = exports.getSpanProcessorsFromConfiguration = exports.getSpanExporter = exports.getHttpAgentOptionsFromTls = exports.getHeadersFromConfiguration = exports.createLogRecordProcessorFromConfig = exports.createLogRecordExporterFromConfig = exports.createLoggerProviderFromConfig = exports.getBatchLogRecordProcessorFromEnv = exports.getBatchLogRecordProcessorConfigFromEnv = exports.getLoggerProviderConfigFromEnv = exports.getPeriodicMetricReaderFromConfiguration = exports.getMetricExporter = exports.getOtlpMetricExporterFromEnv = exports.getPeriodicExportingMetricReaderFromEnv = exports.getNonNegativeNumberFromEnv = exports.getKeyListFromObjectArray = exports.setupPropagator = exports.setupContextManager = exports.getPropagatorFromConfiguration = exports.getPropagatorFromEnv = exports.getSpanProcessorsFromEnv = exports.getOtlpProtocolFromEnv = exports.getResourceDetectorsFromConfiguration = exports.getResourceDetectorsFromEnv = exports.getResourceFromConfiguration = void 0;
const api_1 = require("@opentelemetry/api");
const core_1 = require("@opentelemetry/core");
const exporter_trace_otlp_proto_1 = require("@opentelemetry/exporter-trace-otlp-proto");
const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
const exporter_trace_otlp_grpc_1 = require("@opentelemetry/exporter-trace-otlp-grpc");
const exporter_zipkin_1 = require("@opentelemetry/exporter-zipkin");
const resources_1 = require("@opentelemetry/resources");
const sdk_trace_1 = require("@opentelemetry/sdk-trace");
const propagator_b3_1 = require("@opentelemetry/propagator-b3");
const propagator_jaeger_1 = require("@opentelemetry/propagator-jaeger");
const context_async_hooks_1 = require("@opentelemetry/context-async-hooks");
const exporter_logs_otlp_http_1 = require("@opentelemetry/exporter-logs-otlp-http");
const exporter_logs_otlp_grpc_1 = require("@opentelemetry/exporter-logs-otlp-grpc");
const exporter_logs_otlp_proto_1 = require("@opentelemetry/exporter-logs-otlp-proto");
const otlp_exporter_base_1 = require("@opentelemetry/otlp-exporter-base");
const otlp_grpc_exporter_base_1 = require("@opentelemetry/otlp-grpc-exporter-base");
const configuration_1 = require("@opentelemetry/configuration");
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
const exporter_metrics_otlp_grpc_1 = require("@opentelemetry/exporter-metrics-otlp-grpc");
const exporter_metrics_otlp_http_1 = require("@opentelemetry/exporter-metrics-otlp-http");
const exporter_metrics_otlp_http_2 = require("@opentelemetry/exporter-metrics-otlp-http");
const exporter_metrics_otlp_proto_1 = require("@opentelemetry/exporter-metrics-otlp-proto");
const sdk_logs_1 = require("@opentelemetry/sdk-logs");
const fs = require("fs");
const util_1 = require("util");
const create_from_env_1 = require("./create-from-env");
const RESOURCE_DETECTOR_ENVIRONMENT = 'env';
const RESOURCE_DETECTOR_HOST = 'host';
const RESOURCE_DETECTOR_OS = 'os';
const RESOURCE_DETECTOR_PROCESS = 'process';
const RESOURCE_DETECTOR_SERVICE_INSTANCE_ID = 'serviceinstance';
function getResourceFromConfiguration(config) {
    if (!config.resource) {
        return undefined;
    }
    const configAttrs = (0, configuration_1.mergeResourceAttributesConfig)(config.resource.attributes, config.resource.attributes_list);
    if (!configAttrs) {
        return undefined;
    }
    const attrs = {};
    for (let i = 0; i < configAttrs.length; i++) {
        const a = configAttrs[i];
        if (a.value !== null) {
            attrs[a.name] = a.value;
        }
    }
    return (0, resources_1.resourceFromAttributes)(attrs, {
        schemaUrl: config.resource.schema_url ?? undefined,
    });
}
exports.getResourceFromConfiguration = getResourceFromConfiguration;
function getResourceDetectorsFromEnv() {
    // When updating this list, make sure to also update the section `resourceDetectors` on README.
    const resourceDetectors = new Map([
        [RESOURCE_DETECTOR_HOST, resources_1.hostDetector],
        [RESOURCE_DETECTOR_OS, resources_1.osDetector],
        [RESOURCE_DETECTOR_SERVICE_INSTANCE_ID, resources_1.serviceInstanceIdDetector],
        [RESOURCE_DETECTOR_PROCESS, resources_1.processDetector],
        [RESOURCE_DETECTOR_ENVIRONMENT, resources_1.envDetector],
    ]);
    const resourceDetectorsFromEnv = (0, core_1.getStringListFromEnv)('OTEL_NODE_RESOURCE_DETECTORS') ?? ['all'];
    if (resourceDetectorsFromEnv.includes('all')) {
        return [...resourceDetectors.values()].flat();
    }
    if (resourceDetectorsFromEnv.includes('none')) {
        return [];
    }
    return resourceDetectorsFromEnv.flatMap(detector => {
        const resourceDetector = resourceDetectors.get(detector);
        if (!resourceDetector) {
            api_1.diag.warn(`Invalid resource detector "${detector}" specified in the environment variable OTEL_NODE_RESOURCE_DETECTORS`);
        }
        return resourceDetector || [];
    });
}
exports.getResourceDetectorsFromEnv = getResourceDetectorsFromEnv;
function getResourceDetectorsFromConfiguration(config) {
    const detectors = config.resource?.['detection/development']?.detectors ?? [];
    return detectors.flatMap(detector => {
        const result = [];
        if (detector.host !== undefined)
            result.push(resources_1.hostDetector);
        if (detector.os !== undefined)
            result.push(resources_1.osDetector);
        if (detector.process !== undefined)
            result.push(resources_1.processDetector);
        if (detector.service !== undefined)
            result.push(resources_1.serviceInstanceIdDetector);
        if (detector.env !== undefined)
            result.push(resources_1.envDetector);
        return result;
    });
}
exports.getResourceDetectorsFromConfiguration = getResourceDetectorsFromConfiguration;
function getOtlpProtocolFromEnv() {
    return ((0, core_1.getStringFromEnv)('OTEL_EXPORTER_OTLP_TRACES_PROTOCOL') ??
        (0, core_1.getStringFromEnv)('OTEL_EXPORTER_OTLP_PROTOCOL') ??
        'http/protobuf');
}
exports.getOtlpProtocolFromEnv = getOtlpProtocolFromEnv;
function getOtlpExporterFromEnv() {
    const protocol = getOtlpProtocolFromEnv();
    switch (protocol) {
        case 'grpc':
            return new exporter_trace_otlp_grpc_1.OTLPTraceExporter();
        case 'http/json':
            return new exporter_trace_otlp_http_1.OTLPTraceExporter();
        case 'http/protobuf':
            return new exporter_trace_otlp_proto_1.OTLPTraceExporter();
        default:
            api_1.diag.warn(`Unsupported OTLP traces protocol: ${protocol}. Using http/protobuf.`);
            return new exporter_trace_otlp_proto_1.OTLPTraceExporter();
    }
}
function getSpanProcessorsFromEnv(selfObsMeterProvider) {
    const exportersMap = new Map([
        ['otlp', () => getOtlpExporterFromEnv()],
        ['zipkin', () => new exporter_zipkin_1.ZipkinExporter()],
        ['console', () => new sdk_trace_1.ConsoleSpanExporter()],
    ]);
    const exporters = [];
    const processors = [];
    let traceExportersList = Array.from(new Set((0, core_1.getStringListFromEnv)('OTEL_TRACES_EXPORTER'))).filter(s => s !== 'null');
    if (traceExportersList[0] === 'none') {
        api_1.diag.warn('OTEL_TRACES_EXPORTER contains "none". SDK will not be initialized.');
        return [];
    }
    if (traceExportersList.length === 0) {
        api_1.diag.debug('OTEL_TRACES_EXPORTER is empty. Using default otlp exporter.');
        traceExportersList = ['otlp'];
    }
    else if (traceExportersList.length > 1 &&
        traceExportersList.includes('none')) {
        api_1.diag.warn('OTEL_TRACES_EXPORTER contains "none" along with other exporters. Using default otlp exporter.');
        traceExportersList = ['otlp'];
    }
    for (const name of traceExportersList) {
        const exporter = exportersMap.get(name)?.();
        if (exporter) {
            exporters.push(exporter);
        }
        else {
            api_1.diag.warn(`Unrecognized OTEL_TRACES_EXPORTER value: ${name}.`);
        }
    }
    for (const exp of exporters) {
        if (exp instanceof sdk_trace_1.ConsoleSpanExporter) {
            processors.push(new sdk_trace_1.SimpleSpanProcessor({ exporter: exp, selfObsMeterProvider }));
        }
        else {
            processors.push((0, create_from_env_1.createBatchSpanProcessorFromEnv)(exp, selfObsMeterProvider));
        }
    }
    if (exporters.length === 0) {
        api_1.diag.warn('Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.');
    }
    return processors;
}
exports.getSpanProcessorsFromEnv = getSpanProcessorsFromEnv;
/**
 * Get a propagator as defined by environment variables
 */
function getPropagatorFromEnv() {
    // Empty and undefined MUST be treated equal.
    const propagatorsEnvVarValue = (0, core_1.getStringListFromEnv)('OTEL_PROPAGATORS');
    if (propagatorsEnvVarValue == null) {
        // return undefined to fall back to default
        return undefined;
    }
    if (propagatorsEnvVarValue.includes('none')) {
        return null;
    }
    // Implementation note: this only contains specification required propagators that are actually hosted in this repo.
    // Any other propagators (like aws, aws-lambda, should go into `@opentelemetry/auto-configuration-propagators` instead).
    const propagatorsFactory = new Map([
        ['tracecontext', () => new core_1.W3CTraceContextPropagator()],
        ['baggage', () => new core_1.W3CBaggagePropagator()],
        ['b3', () => new propagator_b3_1.B3Propagator()],
        [
            'b3multi',
            () => new propagator_b3_1.B3Propagator({ injectEncoding: propagator_b3_1.B3InjectEncoding.MULTI_HEADER }),
        ],
        ['jaeger', () => new propagator_jaeger_1.JaegerPropagator()],
    ]);
    // Values MUST be deduplicated in order to register a Propagator only once.
    const uniquePropagatorNames = Array.from(new Set(propagatorsEnvVarValue));
    const validPropagators = [];
    uniquePropagatorNames.forEach(name => {
        const propagator = propagatorsFactory.get(name)?.();
        if (!propagator) {
            api_1.diag.warn(`Propagator "${name}" requested through environment variable is unavailable.`);
            return;
        }
        validPropagators.push(propagator);
    });
    if (validPropagators.length === 0) {
        // null to signal that the default should **not** be used in its place.
        return null;
    }
    else if (uniquePropagatorNames.length === 1) {
        return validPropagators[0];
    }
    else {
        return new core_1.CompositePropagator({
            propagators: validPropagators,
        });
    }
}
exports.getPropagatorFromEnv = getPropagatorFromEnv;
/**
 * Get a propagator as defined by configuration model from configuration
 */
function getPropagatorFromConfiguration(config) {
    if (!config.propagator) {
        return undefined;
    }
    const configComposite = (0, configuration_1.mergePropagatorCompositeConfig)(config.propagator.composite, config.propagator.composite_list);
    if (!configComposite) {
        return undefined;
    }
    // TextMapPropagator config items are objects with a single key (the name).
    // Transform this into a more convenient `(name, value)` 2-tuple.
    //
    // As well, guard against two cases where the TypeScript type
    // `TextMapPropagatorConfigModel` does not exactly represent the JSON schema:
    // 1. `"minProperties": 1, "maxProperties": 1,`
    // 2. The type allows keys with an `undefined` value, but the JSON schema
    //    does not.
    const kvFromItem = (item) => {
        const keys = [];
        let value = undefined;
        for (const key of Object.keys(item)) {
            value = item[key];
            if (value === undefined) {
                continue;
            }
            keys.push(key);
        }
        if (keys.length !== 1) {
            throw new Error(`invalid "propagator" entry in configuration, there must be exactly one key (with a non-undefined value): ${(0, util_1.inspect)(item)}`);
        }
        return [keys[0], value];
    };
    // First pass: handle 'none', remove dupes.
    const names = new Set();
    const kvs = [];
    for (const item of configComposite) {
        const kv = kvFromItem(item);
        const k = kv[0];
        if (names.has(k)) {
            continue;
        }
        names.add(k);
        kvs.push(kv);
        if (k === 'none') {
            return undefined;
        }
    }
    // Implementation note: this only contains specification required propagators that are actually hosted in this repo.
    // Any other propagators (like aws, aws-lambda, should go into `@opentelemetry/auto-configuration-propagators` instead).
    const propagatorsFactory = new Map([
        ['tracecontext', () => new core_1.W3CTraceContextPropagator()],
        ['baggage', () => new core_1.W3CBaggagePropagator()],
        ['b3', () => new propagator_b3_1.B3Propagator()],
        [
            'b3multi',
            () => new propagator_b3_1.B3Propagator({ injectEncoding: propagator_b3_1.B3InjectEncoding.MULTI_HEADER }),
        ],
        ['jaeger', () => new propagator_jaeger_1.JaegerPropagator()],
    ]);
    const validPropagators = [];
    for (const [name] of kvs) {
        const propagator = propagatorsFactory.get(name)?.();
        if (!propagator) {
            api_1.diag.warn(`Propagator "${name}" requested through configuration is unavailable.`);
            continue;
        }
        validPropagators.push(propagator);
    }
    if (validPropagators.length === 0) {
        return undefined;
    }
    else if (validPropagators.length === 1) {
        return validPropagators[0];
    }
    else {
        return new core_1.CompositePropagator({
            propagators: validPropagators,
        });
    }
}
exports.getPropagatorFromConfiguration = getPropagatorFromConfiguration;
function setupContextManager(contextManager) {
    // null means 'do not register'
    if (contextManager === null) {
        return;
    }
    // undefined means 'register default'
    if (contextManager === undefined) {
        const defaultContextManager = new context_async_hooks_1.AsyncLocalStorageContextManager();
        defaultContextManager.enable();
        api_1.context.setGlobalContextManager(defaultContextManager);
        return;
    }
    contextManager.enable();
    api_1.context.setGlobalContextManager(contextManager);
}
exports.setupContextManager = setupContextManager;
function setupPropagator(propagator) {
    // null means 'do not register'
    if (propagator === null) {
        return;
    }
    // undefined means 'register default'
    if (propagator === undefined) {
        api_1.propagation.setGlobalPropagator(new core_1.CompositePropagator({
            propagators: [
                new core_1.W3CTraceContextPropagator(),
                new core_1.W3CBaggagePropagator(),
            ],
        }));
        return;
    }
    api_1.propagation.setGlobalPropagator(propagator);
}
exports.setupPropagator = setupPropagator;
function getKeyListFromObjectArray(obj) {
    if (!obj || obj.length === 0) {
        return undefined;
    }
    const keys = [];
    for (const item of obj) {
        for (const key of Object.keys(item)) {
            keys.push(key);
        }
    }
    return keys;
}
exports.getKeyListFromObjectArray = getKeyListFromObjectArray;
function getNonNegativeNumberFromEnv(envVarName) {
    const value = (0, core_1.getNumberFromEnv)(envVarName);
    if (value != null && value <= 0) {
        api_1.diag.warn(`${envVarName} (${value}) is invalid, expected number greater than 0, using default.`);
        return undefined;
    }
    return value;
}
exports.getNonNegativeNumberFromEnv = getNonNegativeNumberFromEnv;
function getPeriodicExportingMetricReaderFromEnv(exporter) {
    const defaultTimeoutMillis = 30000;
    const defaultIntervalMillis = 60000;
    const rawExportIntervalMillis = getNonNegativeNumberFromEnv('OTEL_METRIC_EXPORT_INTERVAL');
    const rawExportTimeoutMillis = getNonNegativeNumberFromEnv('OTEL_METRIC_EXPORT_TIMEOUT');
    // Apply defaults
    const exportIntervalMillis = rawExportIntervalMillis ?? defaultIntervalMillis;
    let exportTimeoutMillis = rawExportTimeoutMillis ?? defaultTimeoutMillis;
    // Ensure timeout doesn't exceed interval
    if (exportTimeoutMillis > exportIntervalMillis) {
        // determine which env vars were set and which ones defaulted for logging purposes
        const timeoutSource = rawExportTimeoutMillis != null
            ? rawExportTimeoutMillis.toString()
            : `${defaultTimeoutMillis}, default`;
        const intervalSource = rawExportIntervalMillis != null
            ? rawExportIntervalMillis.toString()
            : `${defaultIntervalMillis}, default`;
        const bothSetByUser = rawExportTimeoutMillis != null && rawExportIntervalMillis != null;
        const logMessage = `OTEL_METRIC_EXPORT_TIMEOUT (${timeoutSource}) is greater than OTEL_METRIC_EXPORT_INTERVAL (${intervalSource}). Clamping timeout to interval value.`;
        // only bother users if they explicitly set both values.
        if (bothSetByUser) {
            api_1.diag.warn(logMessage);
        }
        else {
            api_1.diag.info(logMessage);
        }
        exportTimeoutMillis = exportIntervalMillis;
    }
    return new sdk_metrics_1.PeriodicExportingMetricReader({
        exportTimeoutMillis,
        exportIntervalMillis,
        exporter,
    });
}
exports.getPeriodicExportingMetricReaderFromEnv = getPeriodicExportingMetricReaderFromEnv;
function getOtlpMetricExporterFromEnv() {
    const protocol = ((0, core_1.getStringFromEnv)('OTEL_EXPORTER_OTLP_METRICS_PROTOCOL') ??
        (0, core_1.getStringFromEnv)('OTEL_EXPORTER_OTLP_PROTOCOL'))?.trim() || 'http/protobuf'; // Using || to also fall back on empty string
    switch (protocol) {
        case 'grpc':
            return new exporter_metrics_otlp_grpc_1.OTLPMetricExporter();
        case 'http/json':
            return new exporter_metrics_otlp_http_1.OTLPMetricExporter();
        case 'http/protobuf':
            return new exporter_metrics_otlp_proto_1.OTLPMetricExporter();
    }
    api_1.diag.warn(`Unsupported OTLP metrics protocol: "${protocol}". Using http/protobuf.`);
    return new exporter_metrics_otlp_proto_1.OTLPMetricExporter();
}
exports.getOtlpMetricExporterFromEnv = getOtlpMetricExporterFromEnv;
function getMetricProducersFromConfiguration(producers) {
    if (!producers || producers.length === 0) {
        return undefined;
    }
    const result = [];
    for (const producer of producers) {
        // Note: The "opencensus" MetricProducer is intentionally not supported.
        // It is deprecated in OpenTelemetry Configuration v1.2.0.
        api_1.diag.warn(`Unsupported metric producer in configuration: "${producer}". Skipping.`);
    }
    return result.length > 0 ? result : undefined;
}
/**
 * Map a declarative-config `temporality_preference` value to the enum the OTLP
 * metric exporters expect. Returns undefined for an unspecified preference so
 * the exporter falls back to its own default (cumulative).
 */
function getMetricTemporalityPreference(preference) {
    switch (preference) {
        case 'delta':
            return exporter_metrics_otlp_http_2.AggregationTemporalityPreference.DELTA;
        case 'low_memory':
            return exporter_metrics_otlp_http_2.AggregationTemporalityPreference.LOWMEMORY;
        case 'cumulative':
            return exporter_metrics_otlp_http_2.AggregationTemporalityPreference.CUMULATIVE;
        default:
            return undefined;
    }
}
/**
 * Map a declarative-config `default_histogram_aggregation` value to an
 * AggregationSelector that applies the requested aggregation to histogram
 * instruments and leaves all other instrument types at their default. Returns
 * undefined for an unspecified value so the exporter uses its own default.
 */
function getMetricAggregationPreference(aggregation) {
    let histogramAggregation;
    switch (aggregation) {
        case 'base2_exponential_bucket_histogram':
            histogramAggregation = { type: sdk_metrics_1.AggregationType.EXPONENTIAL_HISTOGRAM };
            break;
        case 'explicit_bucket_histogram':
            histogramAggregation = {
                type: sdk_metrics_1.AggregationType.EXPLICIT_BUCKET_HISTOGRAM,
            };
            break;
        default:
            return undefined;
    }
    return (instrumentType) => instrumentType === sdk_metrics_1.InstrumentType.HISTOGRAM
        ? histogramAggregation
        : { type: sdk_metrics_1.AggregationType.DEFAULT };
}
function getOtlpHttpMetricExporter(otlpHttp) {
    const encoding = otlpHttp?.encoding ?? 'protobuf';
    const options = {
        compression: otlpHttp?.compression === 'gzip'
            ? otlp_exporter_base_1.CompressionAlgorithm.GZIP
            : otlp_exporter_base_1.CompressionAlgorithm.NONE,
        url: otlpHttp?.endpoint ?? undefined,
        headers: getHeadersFromConfiguration(otlpHttp?.headers),
        timeoutMillis: validateExporterTimeout(otlpHttp?.timeout),
        httpAgentOptions: getHttpAgentOptionsFromTls(otlpHttp?.tls),
        temporalityPreference: getMetricTemporalityPreference(otlpHttp?.temporality_preference),
        aggregationPreference: getMetricAggregationPreference(otlpHttp?.default_histogram_aggregation),
    };
    if (encoding === 'json') {
        return new exporter_metrics_otlp_http_1.OTLPMetricExporter(options);
    }
    else if (encoding === 'protobuf') {
        return new exporter_metrics_otlp_proto_1.OTLPMetricExporter(options);
    }
    api_1.diag.warn(`Unsupported OTLP metrics encoding: ${encoding}.`);
    return undefined;
}
function getOtlpGrpcMetricExporter(otlpGrpc) {
    return new exporter_metrics_otlp_grpc_1.OTLPMetricExporter({
        compression: otlpGrpc?.compression === 'gzip'
            ? otlp_exporter_base_1.CompressionAlgorithm.GZIP
            : otlp_exporter_base_1.CompressionAlgorithm.NONE,
        url: otlpGrpc?.endpoint ?? undefined,
        timeoutMillis: validateExporterTimeout(otlpGrpc?.timeout),
        credentials: getGrpcCredentialsFromTls(otlpGrpc?.tls),
        metadata: getGrpcMetadataFromHeaders(otlpGrpc?.headers),
        temporalityPreference: getMetricTemporalityPreference(otlpGrpc?.temporality_preference),
        aggregationPreference: getMetricAggregationPreference(otlpGrpc?.default_histogram_aggregation),
    });
}
function getMetricExporter(exporter) {
    if (exporter.otlp_http !== undefined) {
        return getOtlpHttpMetricExporter(exporter.otlp_http);
    }
    if (exporter.otlp_grpc !== undefined) {
        return getOtlpGrpcMetricExporter(exporter.otlp_grpc);
    }
    if (exporter.console !== undefined) {
        return new sdk_metrics_1.ConsoleMetricExporter();
    }
    api_1.diag.warn('Unsupported Metric Exporter.');
    return undefined;
}
exports.getMetricExporter = getMetricExporter;
function getPeriodicMetricReaderFromConfiguration(periodic) {
    if (!periodic.exporter) {
        api_1.diag.warn('Unsupported Metric Exporter.');
        return undefined;
    }
    const exporter = getMetricExporter(periodic.exporter);
    if (!exporter) {
        return undefined;
    }
    const metricProducers = getMetricProducersFromConfiguration(periodic.producers);
    // TODO(6425): add cardinality_limits
    return new sdk_metrics_1.PeriodicExportingMetricReader({
        exportIntervalMillis: periodic.interval ?? 60000,
        exportTimeoutMillis: periodic.timeout ?? 30000,
        exporter,
        metricProducers,
    });
}
exports.getPeriodicMetricReaderFromConfiguration = getPeriodicMetricReaderFromConfiguration;
/**
 * Get LoggerProviderConfig from environment variables.
 */
function getLoggerProviderConfigFromEnv() {
    return {
        logRecordLimits: {
            attributeCountLimit: getNonNegativeNumberFromEnv('OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT') ??
                getNonNegativeNumberFromEnv('OTEL_ATTRIBUTE_COUNT_LIMIT'),
            attributeValueLengthLimit: getNonNegativeNumberFromEnv('OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT') ?? getNonNegativeNumberFromEnv('OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT'),
        },
    };
}
exports.getLoggerProviderConfigFromEnv = getLoggerProviderConfigFromEnv;
/**
 * Get configuration for BatchLogRecordProcessor from environment variables.
 */
function getBatchLogRecordProcessorConfigFromEnv() {
    return {
        maxQueueSize: getNonNegativeNumberFromEnv('OTEL_BLRP_MAX_QUEUE_SIZE'),
        scheduledDelayMillis: getNonNegativeNumberFromEnv('OTEL_BLRP_SCHEDULE_DELAY'),
        exportTimeoutMillis: getNonNegativeNumberFromEnv('OTEL_BLRP_EXPORT_TIMEOUT'),
        maxExportBatchSize: getNonNegativeNumberFromEnv('OTEL_BLRP_MAX_EXPORT_BATCH_SIZE'),
    };
}
exports.getBatchLogRecordProcessorConfigFromEnv = getBatchLogRecordProcessorConfigFromEnv;
function getBatchLogRecordProcessorFromEnv(exporter) {
    return new sdk_logs_1.BatchLogRecordProcessor({
        exporter,
        ...getBatchLogRecordProcessorConfigFromEnv(),
    });
}
exports.getBatchLogRecordProcessorFromEnv = getBatchLogRecordProcessorFromEnv;
function createLogRecordLimitsFromConfig(limits, attribute_limits) {
    return {
        attributeValueLengthLimit: limits?.attribute_value_length_limit ??
            attribute_limits?.attribute_value_length_limit ??
            undefined,
        attributeCountLimit: limits?.attribute_count_limit ??
            attribute_limits?.attribute_count_limit ??
            undefined,
    };
}
function createLoggerProviderFromConfig(resource, logger_provider, attribute_limits) {
    const processors = logger_provider.processors.map(p => createLogRecordProcessorFromConfig(p));
    const logRecordLimits = createLogRecordLimitsFromConfig(logger_provider.limits, attribute_limits);
    checkConfigUse('LoggerProvider', logger_provider, ['processors', 'limits']);
    return new sdk_logs_1.LoggerProvider({
        resource,
        processors,
        logRecordLimits,
        // TODO: loggerConfigurator
        // TODO: meterProvider
        // Note: forceFlushTimeoutMillis not configurable via decl conf.
    });
}
exports.createLoggerProviderFromConfig = createLoggerProviderFromConfig;
function createLogRecordExporterFromConfig(exporter) {
    const [name, properties] = mustSingleEntry(exporter, 'LogRecordExporter');
    switch (name) {
        case 'otlp_http': {
            const props = properties;
            const commonOpts = {
                compression: props?.compression === 'gzip'
                    ? otlp_exporter_base_1.CompressionAlgorithm.GZIP
                    : otlp_exporter_base_1.CompressionAlgorithm.NONE,
                url: props?.endpoint ?? undefined,
                headers: getHeadersFromConfiguration(props?.headers),
                timeoutMillis: validateExporterTimeout(props?.timeout),
                httpAgentOptions: getHttpAgentOptionsFromTls(props?.tls),
            };
            const encoding = props?.encoding ?? 'protobuf';
            switch (encoding) {
                case 'json':
                    return new exporter_logs_otlp_http_1.OTLPLogExporter(commonOpts);
                case 'protobuf':
                    return new exporter_logs_otlp_proto_1.OTLPLogExporter(commonOpts);
                default:
                    throw new Error(`unknown OtlpHttpExporter encoding in configuration: "${encoding}"`);
            }
        }
        case 'otlp_grpc': {
            const props = properties;
            return new exporter_logs_otlp_grpc_1.OTLPLogExporter({
                compression: props?.compression === 'gzip'
                    ? otlp_exporter_base_1.CompressionAlgorithm.GZIP
                    : otlp_exporter_base_1.CompressionAlgorithm.NONE,
                url: props?.endpoint ?? undefined,
                timeoutMillis: validateExporterTimeout(props?.timeout),
                credentials: getGrpcCredentialsFromTls(props?.tls),
                metadata: getGrpcMetadataFromHeaders(props?.headers),
            });
        }
        case 'console':
            return new sdk_logs_1.ConsoleLogRecordExporter();
        default:
            throw new Error(`unknown LogRecordExporter name in configuration: "${name}"`);
    }
}
exports.createLogRecordExporterFromConfig = createLogRecordExporterFromConfig;
function createLogRecordProcessorFromConfig(processor) {
    const [name, properties] = mustSingleEntry(processor, 'LogRecordProcessor');
    switch (name) {
        case 'batch': {
            const props = properties;
            const exporter = createLogRecordExporterFromConfig(props.exporter);
            return new sdk_logs_1.BatchLogRecordProcessor({
                exporter,
                maxQueueSize: props.max_queue_size ?? undefined,
                maxExportBatchSize: props.max_export_batch_size ?? undefined,
                scheduledDelayMillis: props.schedule_delay ?? undefined,
                exportTimeoutMillis: props.export_timeout ?? undefined,
            });
        }
        case 'simple': {
            const props = properties;
            const exporter = createLogRecordExporterFromConfig(props.exporter);
            return new sdk_logs_1.SimpleLogRecordProcessor({ exporter });
        }
        default:
            throw new Error(`unknown LogRecordProcessor name: "${name}"`);
    }
}
exports.createLogRecordProcessorFromConfig = createLogRecordProcessorFromConfig;
function getHeadersFromConfiguration(headers) {
    if (!headers) {
        return undefined;
    }
    const result = {};
    headers.forEach(header => {
        if (header.value !== null) {
            result[header.name] = header.value;
        }
    });
    return result;
}
exports.getHeadersFromConfiguration = getHeadersFromConfiguration;
/**
 * Validate an exporter timeout value. The spec says 0 means "no limit
 * (infinity)" but the JS exporters don't support that yet (see #6617).
 * Warn and return undefined so the exporter falls back to its default.
 */
function validateExporterTimeout(timeout) {
    if (timeout === null) {
        return undefined;
    }
    else if (timeout === 0) {
        api_1.diag.warn('Exporter timeout of 0 (infinite) is not supported. Using default timeout.');
        return undefined;
    }
    return timeout;
}
function getHttpAgentOptionsFromTls(tls) {
    if (tls && (tls.ca_file || tls.cert_file || tls.key_file)) {
        return {
            ca: readFileOrWarn(tls.ca_file, 'TLS CA'),
            cert: readFileOrWarn(tls.cert_file, 'TLS cert'),
            key: readFileOrWarn(tls.key_file, 'TLS key'),
        };
    }
    return undefined;
}
exports.getHttpAgentOptionsFromTls = getHttpAgentOptionsFromTls;
function getGrpcCredentialsFromTls(tls) {
    if (tls?.insecure) {
        return (0, otlp_grpc_exporter_base_1.createInsecureCredentials)();
    }
    const rootCert = readFileOrWarn(tls?.ca_file, 'TLS CA');
    const privateKey = readFileOrWarn(tls?.key_file, 'TLS key');
    const certChain = readFileOrWarn(tls?.cert_file, 'TLS cert');
    if (rootCert || privateKey || certChain) {
        try {
            return (0, otlp_grpc_exporter_base_1.createSslCredentials)(rootCert, privateKey, certChain);
        }
        catch (e) {
            api_1.diag.warn(`Failed to create gRPC SSL credentials: ${e}`);
            return undefined;
        }
    }
    return undefined;
}
function getGrpcMetadataFromHeaders(headers) {
    if (!headers || headers.length === 0) {
        return undefined;
    }
    const metadata = (0, otlp_grpc_exporter_base_1.createEmptyMetadata)();
    for (const header of headers) {
        if (header.value !== null) {
            metadata.set(header.name, header.value);
        }
    }
    return metadata;
}
function readFileOrWarn(filePath, label) {
    if (!filePath)
        return undefined;
    try {
        return fs.readFileSync(filePath);
    }
    catch (e) {
        api_1.diag.warn(`Failed to read ${label} file at ${filePath}: ${e}`);
        return undefined;
    }
}
function getSpanExporter(exporter) {
    if (exporter.otlp_http !== undefined) {
        const encoding = exporter.otlp_http?.encoding ?? 'protobuf';
        if (encoding === 'json') {
            return new exporter_trace_otlp_http_1.OTLPTraceExporter({
                compression: exporter.otlp_http?.compression === 'gzip'
                    ? otlp_exporter_base_1.CompressionAlgorithm.GZIP
                    : otlp_exporter_base_1.CompressionAlgorithm.NONE,
                url: exporter.otlp_http?.endpoint ?? undefined,
                headers: getHeadersFromConfiguration(exporter.otlp_http?.headers),
                timeoutMillis: validateExporterTimeout(exporter.otlp_http?.timeout),
                httpAgentOptions: getHttpAgentOptionsFromTls(exporter.otlp_http?.tls),
            });
        }
        else {
            return new exporter_trace_otlp_proto_1.OTLPTraceExporter({
                compression: exporter.otlp_http?.compression === 'gzip'
                    ? otlp_exporter_base_1.CompressionAlgorithm.GZIP
                    : otlp_exporter_base_1.CompressionAlgorithm.NONE,
                url: exporter.otlp_http?.endpoint ?? undefined,
                headers: getHeadersFromConfiguration(exporter.otlp_http?.headers),
                timeoutMillis: validateExporterTimeout(exporter.otlp_http?.timeout),
                httpAgentOptions: getHttpAgentOptionsFromTls(exporter.otlp_http?.tls),
            });
        }
    }
    else if (exporter.otlp_grpc !== undefined) {
        return new exporter_trace_otlp_grpc_1.OTLPTraceExporter({
            compression: exporter.otlp_grpc?.compression === 'gzip'
                ? otlp_exporter_base_1.CompressionAlgorithm.GZIP
                : otlp_exporter_base_1.CompressionAlgorithm.NONE,
            url: exporter.otlp_grpc?.endpoint ?? undefined,
            timeoutMillis: validateExporterTimeout(exporter.otlp_grpc?.timeout),
            credentials: getGrpcCredentialsFromTls(exporter.otlp_grpc?.tls),
            metadata: getGrpcMetadataFromHeaders(exporter.otlp_grpc?.headers),
        });
    }
    else if (exporter.console !== undefined) {
        return new sdk_trace_1.ConsoleSpanExporter();
    }
    api_1.diag.warn('Unsupported Exporter value. No Span Exporter registered');
    return undefined;
}
exports.getSpanExporter = getSpanExporter;
function getSpanProcessorsFromConfiguration(config) {
    const spanProcessors = [];
    config.tracer_provider?.processors?.forEach(processor => {
        if (processor.batch) {
            const exporter = getSpanExporter(processor.batch.exporter);
            if (exporter) {
                spanProcessors.push(new sdk_trace_1.BatchSpanProcessor({
                    exporter,
                    maxQueueSize: processor.batch.max_queue_size ?? undefined,
                    maxExportBatchSize: processor.batch.max_export_batch_size ?? undefined,
                    scheduledDelayMillis: processor.batch.schedule_delay ?? undefined,
                    exportTimeoutMillis: processor.batch.export_timeout ?? undefined,
                }));
            }
        }
        if (processor.simple) {
            const exporter = getSpanExporter(processor.simple.exporter);
            if (exporter) {
                spanProcessors.push(new sdk_trace_1.SimpleSpanProcessor({ exporter }));
            }
        }
    });
    if (spanProcessors.length > 0) {
        return spanProcessors;
    }
    return undefined;
}
exports.getSpanProcessorsFromConfiguration = getSpanProcessorsFromConfiguration;
function getIdGeneratorFromConfiguration(config) {
    const idGenerator = config.tracer_provider?.id_generator;
    if (!idGenerator) {
        return undefined;
    }
    if (idGenerator.random !== undefined) {
        return new sdk_trace_1.RandomIdGenerator();
    }
    // Any other key is a third-party / custom id_generator type which we
    // don't currently support. Warn and fall back to SDK default.
    const unknownKeys = Object.keys(idGenerator).filter(k => k !== 'random');
    if (unknownKeys.length > 0) {
        api_1.diag.warn(`Unsupported id_generator type(s): ${unknownKeys.join(', ')}. Using default.`);
    }
    return undefined;
}
exports.getIdGeneratorFromConfiguration = getIdGeneratorFromConfiguration;
function getMeterReadersFromConfiguration(config) {
    const metricReaders = [];
    config.meter_provider?.readers?.forEach(reader => {
        if (reader.periodic) {
            const periodicReader = getPeriodicMetricReaderFromConfiguration(reader.periodic);
            if (periodicReader) {
                metricReaders.push(periodicReader);
            }
        }
    });
    if (metricReaders.length > 0) {
        return metricReaders;
    }
    return undefined;
}
exports.getMeterReadersFromConfiguration = getMeterReadersFromConfiguration;
function getInstrumentType(instrument) {
    switch (instrument) {
        case 'counter':
            return sdk_metrics_1.InstrumentType.COUNTER;
        case 'gauge':
            return sdk_metrics_1.InstrumentType.GAUGE;
        case 'histogram':
            return sdk_metrics_1.InstrumentType.HISTOGRAM;
        case 'observable_counter':
            return sdk_metrics_1.InstrumentType.OBSERVABLE_COUNTER;
        case 'observable_gauge':
            return sdk_metrics_1.InstrumentType.OBSERVABLE_GAUGE;
        case 'observable_up_down_counter':
            return sdk_metrics_1.InstrumentType.OBSERVABLE_UP_DOWN_COUNTER;
        case 'up_down_counter':
            return sdk_metrics_1.InstrumentType.UP_DOWN_COUNTER;
        default:
            api_1.diag.warn(`Unsupported instrument type: ${instrument}`);
            return undefined;
    }
}
exports.getInstrumentType = getInstrumentType;
function getAggregationType(aggregation) {
    if (aggregation.default) {
        return {
            type: sdk_metrics_1.AggregationType.DEFAULT,
        };
    }
    if (aggregation.drop) {
        return {
            type: sdk_metrics_1.AggregationType.DROP,
        };
    }
    if (aggregation.explicit_bucket_histogram) {
        return {
            type: sdk_metrics_1.AggregationType.EXPLICIT_BUCKET_HISTOGRAM,
            options: {
                recordMinMax: aggregation.explicit_bucket_histogram.record_min_max ?? true,
                boundaries: aggregation.explicit_bucket_histogram.boundaries ?? [
                    0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500,
                    10000,
                ],
            },
        };
    }
    if (aggregation.base2_exponential_bucket_histogram) {
        return {
            type: sdk_metrics_1.AggregationType.EXPONENTIAL_HISTOGRAM,
            options: {
                recordMinMax: aggregation.base2_exponential_bucket_histogram.record_min_max ??
                    undefined,
                maxSize: aggregation.base2_exponential_bucket_histogram.max_size ?? undefined,
            },
        };
    }
    if (aggregation.last_value) {
        return {
            type: sdk_metrics_1.AggregationType.LAST_VALUE,
        };
    }
    if (aggregation.sum) {
        return {
            type: sdk_metrics_1.AggregationType.SUM,
        };
    }
    api_1.diag.warn('Unsupported aggregation type');
    return undefined;
}
exports.getAggregationType = getAggregationType;
function getMeterViewsFromConfiguration(config) {
    const metricViews = [];
    config.meter_provider?.views?.forEach(view => {
        const viewOption = {};
        if (view.selector) {
            if (view.selector.instrument_name) {
                viewOption.instrumentName = view.selector.instrument_name;
            }
            if (view.selector.instrument_type) {
                const instrumentType = getInstrumentType(view.selector.instrument_type);
                if (instrumentType) {
                    viewOption.instrumentType = instrumentType;
                }
            }
            if (view.selector.unit) {
                viewOption.instrumentUnit = view.selector.unit;
            }
            if (view.selector.meter_name) {
                viewOption.meterName = view.selector.meter_name;
            }
            if (view.selector.meter_version) {
                viewOption.meterVersion = view.selector.meter_version;
            }
            if (view.selector.meter_schema_url) {
                viewOption.meterSchemaUrl = view.selector.meter_schema_url;
            }
        }
        if (view.stream) {
            if (view.stream.name) {
                viewOption.name = view.stream.name;
            }
            viewOption.aggregationCardinalityLimit =
                view.stream.aggregation_cardinality_limit ?? 2000;
            if (view.stream.description) {
                viewOption.description = view.stream.description;
            }
            if (view.stream.aggregation) {
                const aggregationType = getAggregationType(view.stream.aggregation);
                if (aggregationType) {
                    viewOption.aggregation = aggregationType;
                }
            }
            if (view.stream.attribute_keys) {
                const processors = [];
                if (view.stream.attribute_keys.included &&
                    view.stream.attribute_keys.included.length > 0) {
                    processors.push((0, sdk_metrics_1.createAllowListAttributesProcessor)(view.stream.attribute_keys.included));
                }
                if (view.stream.attribute_keys.excluded &&
                    view.stream.attribute_keys.excluded.length > 0) {
                    processors.push((0, sdk_metrics_1.createDenyListAttributesProcessor)(view.stream.attribute_keys.excluded));
                }
                if (processors.length > 0) {
                    viewOption.attributesProcessors = processors;
                }
            }
        }
        if (Object.keys(viewOption).length > 0) {
            metricViews.push(viewOption);
        }
    });
    if (metricViews.length > 0) {
        return metricViews;
    }
    return undefined;
}
exports.getMeterViewsFromConfiguration = getMeterViewsFromConfiguration;
function getInstanceID(config) {
    if (config.resource?.attributes) {
        for (let i = 0; i < config.resource.attributes.length; i++) {
            const element = config.resource.attributes[i];
            if (element.name === 'service.instance.id') {
                return element.value?.toString();
            }
        }
    }
    return undefined;
}
exports.getInstanceID = getInstanceID;
const DEFAULT_RATIO = 1;
/**
 * Returns the {@link Sampler} configured under `tracer_provider.sampler` in
 * the declarative configuration, or `undefined` if none is set (in which case
 * the SDK applies its default sampler).
 */
function getSamplerFromConfiguration(config) {
    const samplerConfig = config.tracer_provider?.sampler;
    if (!samplerConfig) {
        return undefined;
    }
    return buildSamplerFromConfig(samplerConfig);
}
exports.getSamplerFromConfiguration = getSamplerFromConfiguration;
/**
 * Builds a {@link Sampler} from a {@link SamplerConfigModel} data model.
 * This allows sampler construction from declarative configuration.
 */
function buildSamplerFromConfig(samplerConfig) {
    if (samplerConfig.always_on !== undefined) {
        return new sdk_trace_1.AlwaysOnSampler();
    }
    if (samplerConfig.always_off !== undefined) {
        return new sdk_trace_1.AlwaysOffSampler();
    }
    if (samplerConfig.trace_id_ratio_based !== undefined) {
        return new sdk_trace_1.TraceIdRatioBasedSampler(samplerConfig.trace_id_ratio_based?.ratio ?? DEFAULT_RATIO);
    }
    if (samplerConfig.parent_based !== undefined) {
        const pb = samplerConfig.parent_based ?? {};
        return new sdk_trace_1.ParentBasedSampler({
            root: pb.root ? buildSamplerFromConfig(pb.root) : new sdk_trace_1.AlwaysOnSampler(),
            remoteParentSampled: pb.remote_parent_sampled
                ? buildSamplerFromConfig(pb.remote_parent_sampled)
                : undefined,
            remoteParentNotSampled: pb.remote_parent_not_sampled
                ? buildSamplerFromConfig(pb.remote_parent_not_sampled)
                : undefined,
            localParentSampled: pb.local_parent_sampled
                ? buildSamplerFromConfig(pb.local_parent_sampled)
                : undefined,
            localParentNotSampled: pb.local_parent_not_sampled
                ? buildSamplerFromConfig(pb.local_parent_not_sampled)
                : undefined,
        });
    }
    api_1.diag.warn('Unknown sampler config, defaulting to ParentBased(AlwaysOn).');
    return new sdk_trace_1.ParentBasedSampler({ root: new sdk_trace_1.AlwaysOnSampler() });
}
exports.buildSamplerFromConfig = buildSamplerFromConfig;
/**
 * Warn if some props from a declarative config object have not been handled.
 *
 * This is intended to be used by `create*FromConfig()` functions. It is a low
 * tech mechanism to add awareness when a given valid config is not being
 * completely handled. This could help when properties are added to the
 * configuration schema. (A higher tech mechanism that wraps the parsed
 * configuration during `create()` and watches for untouched properties
 * might be nice.)
 */
function checkConfigUse(name, props, handledProps) {
    if (!props)
        return;
    // Dev note: I'd use Set#difference, but that requires Node.js v22.
    const unhandledProps = Object.keys(props).filter(k => !handledProps.includes(k));
    if (unhandledProps.length > 0) {
        api_1.diag.warn(`Config warning: some specified ${name} configuration properties were not handled by SDK setup: ${JSON.stringify(unhandledProps)}`);
    }
}
/**
 * Return the single non-undefined entry in the given config object, or throw.
 *
 * It is common for Declarative Configuration to have config objects with
 * a single entry, e.g.
 *
 *    "LogRecordProcessor": {
 *      "type": "object",
 *      "additionalProperties": {
 *        "type": [
 *          "object",
 *          "null"
 *        ]
 *      },
 *      "minProperties": 1,
 *      "maxProperties": 1,
 *
 * The TypeScript types cannot express the minProperties/maxProperties from the
 * JSON schema. We guard against that here.
 */
function mustSingleEntry(configObj, configTypeName) {
    const entries = Object.entries(configObj).filter(([_name, properties]) => properties !== undefined);
    if (entries.length !== 1) {
        const entryNames = entries.map(e => e[0]);
        throw Error(`invalid ${configTypeName} in configuration: must have exactly one entry: entries=${JSON.stringify(entryNames)}`);
    }
    return entries[0];
}
//# sourceMappingURL=utils.js.map