"use strict";
/*
 * Copyright The OpenTelemetry Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeSDK = void 0;
const api_1 = require("@opentelemetry/api");
const api_logs_1 = require("@opentelemetry/api-logs");
const instrumentation_1 = require("@opentelemetry/instrumentation");
const resources_1 = require("@opentelemetry/resources");
const sdk_logs_1 = require("@opentelemetry/sdk-logs");
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 exporter_metrics_otlp_grpc_1 = require("@opentelemetry/exporter-metrics-otlp-grpc");
const exporter_metrics_otlp_proto_1 = require("@opentelemetry/exporter-metrics-otlp-proto");
const exporter_metrics_otlp_http_1 = require("@opentelemetry/exporter-metrics-otlp-http");
const exporter_prometheus_1 = require("@opentelemetry/exporter-prometheus");
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
const core_1 = require("@opentelemetry/core");
const utils_1 = require("./utils");
/**
 * @Returns param value, if set else returns the default value
 */
function getValueInMillis(envName, defaultValue) {
    return parseInt(process.env[envName] || '') || defaultValue;
}
/**
 *
 * @returns MetricReader[] if appropriate environment variables are configured
 */
function configureMetricProviderFromEnv() {
    const metricReaders = [];
    const metricsExporterList = process.env.OTEL_METRICS_EXPORTER?.trim();
    if (!metricsExporterList) {
        return metricReaders;
    }
    const enabledExporters = (0, utils_1.filterBlanksAndNulls)(metricsExporterList.split(','));
    if (enabledExporters.length === 0) {
        api_1.diag.debug('OTEL_METRICS_EXPORTER is empty. Using default otlp exporter.');
    }
    if (enabledExporters.includes('none')) {
        api_1.diag.info(`OTEL_METRICS_EXPORTER contains "none". Metric provider will not be initialized.`);
        return metricReaders;
    }
    enabledExporters.forEach(exporter => {
        if (exporter === 'otlp') {
            const protocol = process.env.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL?.trim() ||
                process.env.OTEL_EXPORTER_OTLP_PROTOCOL?.trim();
            const exportIntervalMillis = getValueInMillis('OTEL_METRIC_EXPORT_INTERVAL', 60000);
            const exportTimeoutMillis = getValueInMillis('OTEL_METRIC_EXPORT_TIMEOUT', 30000);
            switch (protocol) {
                case 'grpc':
                    metricReaders.push(new sdk_metrics_1.PeriodicExportingMetricReader({
                        exporter: new exporter_metrics_otlp_grpc_1.OTLPMetricExporter(),
                        exportIntervalMillis: exportIntervalMillis,
                        exportTimeoutMillis: exportTimeoutMillis,
                    }));
                    break;
                case 'http/json':
                    metricReaders.push(new sdk_metrics_1.PeriodicExportingMetricReader({
                        exporter: new exporter_metrics_otlp_http_1.OTLPMetricExporter(),
                        exportIntervalMillis: exportIntervalMillis,
                        exportTimeoutMillis: exportTimeoutMillis,
                    }));
                    break;
                case 'http/protobuf':
                    metricReaders.push(new sdk_metrics_1.PeriodicExportingMetricReader({
                        exporter: new exporter_metrics_otlp_proto_1.OTLPMetricExporter(),
                        exportIntervalMillis: exportIntervalMillis,
                        exportTimeoutMillis: exportTimeoutMillis,
                    }));
                    break;
                default:
                    api_1.diag.warn(`Unsupported OTLP metrics protocol: "${protocol}". Using http/protobuf.`);
                    metricReaders.push(new sdk_metrics_1.PeriodicExportingMetricReader({
                        exporter: new exporter_metrics_otlp_proto_1.OTLPMetricExporter(),
                        exportIntervalMillis: exportIntervalMillis,
                        exportTimeoutMillis: exportTimeoutMillis,
                    }));
            }
        }
        else if (exporter === 'console') {
            metricReaders.push(new sdk_metrics_1.PeriodicExportingMetricReader({
                exporter: new sdk_metrics_1.ConsoleMetricExporter(),
            }));
        }
        else if (exporter === 'prometheus') {
            metricReaders.push(new exporter_prometheus_1.PrometheusExporter());
        }
        else {
            api_1.diag.warn(`Unsupported OTEL_METRICS_EXPORTER value: "${exporter}". Supported values are: otlp, console, prometheus, none.`);
        }
    });
    return metricReaders;
}
class NodeSDK {
    _tracerProviderConfig;
    _loggerProviderConfig;
    _meterProviderConfig;
    _instrumentations;
    _resource;
    _resourceDetectors;
    _autoDetectResources;
    _tracerProvider;
    _loggerProvider;
    _meterProvider;
    _serviceName;
    _configuration;
    _disabled;
    /**
     * Create a new NodeJS SDK instance
     */
    constructor(configuration = {}) {
        if ((0, core_1.getBooleanFromEnv)('OTEL_SDK_DISABLED')) {
            this._disabled = true;
            // Functions with possible side-effects are set
            // to no-op via the _disabled flag
        }
        const logLevel = (0, core_1.getStringFromEnv)('OTEL_LOG_LEVEL');
        if (logLevel != null) {
            api_1.diag.setLogger(new api_1.DiagConsoleLogger(), {
                logLevel: (0, core_1.diagLogLevelFromString)(logLevel),
            });
        }
        this._configuration = configuration;
        this._resource = configuration.resource ?? (0, resources_1.defaultResource)();
        this._autoDetectResources = configuration.autoDetectResources ?? true;
        if (!this._autoDetectResources) {
            this._resourceDetectors = [];
        }
        else if (configuration.resourceDetectors != null) {
            this._resourceDetectors = configuration.resourceDetectors;
        }
        else if (process.env.OTEL_NODE_RESOURCE_DETECTORS != null) {
            this._resourceDetectors = (0, utils_1.getResourceDetectorsFromEnv)();
        }
        else {
            this._resourceDetectors = [resources_1.envDetector, resources_1.processDetector, resources_1.hostDetector];
        }
        this._serviceName = configuration.serviceName;
        // If a tracer provider can be created from manual configuration, create it
        if (configuration.traceExporter ||
            configuration.spanProcessor ||
            configuration.spanProcessors) {
            const tracerProviderConfig = {};
            if (configuration.sampler) {
                tracerProviderConfig.sampler = configuration.sampler;
            }
            if (configuration.spanLimits) {
                tracerProviderConfig.spanLimits = configuration.spanLimits;
            }
            if (configuration.idGenerator) {
                tracerProviderConfig.idGenerator = configuration.idGenerator;
            }
            if (configuration.spanProcessor) {
                api_1.diag.warn("The 'spanProcessor' option is deprecated. Please use 'spanProcessors' instead.");
            }
            const spanProcessor = configuration.spanProcessor ??
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                new sdk_trace_base_1.BatchSpanProcessor(configuration.traceExporter);
            const spanProcessors = configuration.spanProcessors ?? [spanProcessor];
            this._tracerProviderConfig = {
                tracerConfig: tracerProviderConfig,
                spanProcessors,
                contextManager: configuration.contextManager,
                textMapPropagator: configuration.textMapPropagator,
            };
        }
        if (configuration.logRecordProcessors) {
            this._loggerProviderConfig = {
                logRecordProcessors: configuration.logRecordProcessors,
            };
        }
        else if (configuration.logRecordProcessor) {
            this._loggerProviderConfig = {
                logRecordProcessors: [configuration.logRecordProcessor],
            };
            api_1.diag.warn("The 'logRecordProcessor' option is deprecated. Please use 'logRecordProcessors' instead.");
        }
        else {
            this.configureLoggerProviderFromEnv();
        }
        if (configuration.metricReader || configuration.views) {
            const meterProviderConfig = {};
            if (configuration.metricReader) {
                meterProviderConfig.reader = configuration.metricReader;
            }
            if (configuration.views) {
                meterProviderConfig.views = configuration.views;
            }
            this._meterProviderConfig = meterProviderConfig;
        }
        this._instrumentations = configuration.instrumentations?.flat() ?? [];
    }
    /**
     * Call this method to construct SDK components and register them with the OpenTelemetry API.
     */
    start() {
        if (this._disabled) {
            return;
        }
        (0, instrumentation_1.registerInstrumentations)({
            instrumentations: this._instrumentations,
        });
        if (this._autoDetectResources) {
            const internalConfig = {
                detectors: this._resourceDetectors,
            };
            this._resource = this._resource.merge((0, resources_1.detectResources)(internalConfig));
        }
        this._resource =
            this._serviceName === undefined
                ? this._resource
                : this._resource.merge((0, resources_1.resourceFromAttributes)({
                    [semantic_conventions_1.ATTR_SERVICE_NAME]: this._serviceName,
                }));
        const spanProcessors = this._tracerProviderConfig
            ? this._tracerProviderConfig.spanProcessors
            : (0, utils_1.getSpanProcessorsFromEnv)();
        this._tracerProvider = new sdk_trace_node_1.NodeTracerProvider({
            ...this._configuration,
            resource: this._resource,
            spanProcessors,
        });
        // Only register if there is a span processor
        if (spanProcessors.length > 0) {
            this._tracerProvider.register({
                contextManager: this._tracerProviderConfig?.contextManager ??
                    // _tracerProviderConfig may be undefined if trace-specific settings are not provided - fall back to raw config
                    this._configuration?.contextManager,
                propagator: this._tracerProviderConfig?.textMapPropagator ??
                    (0, utils_1.getPropagatorFromEnv)(),
            });
        }
        if (this._loggerProviderConfig) {
            const loggerProvider = new sdk_logs_1.LoggerProvider({
                resource: this._resource,
            });
            for (const logRecordProcessor of this._loggerProviderConfig
                .logRecordProcessors) {
                loggerProvider.addLogRecordProcessor(logRecordProcessor);
            }
            this._loggerProvider = loggerProvider;
            api_logs_1.logs.setGlobalLoggerProvider(loggerProvider);
        }
        const metricReadersFromEnv = configureMetricProviderFromEnv();
        if (this._meterProviderConfig || metricReadersFromEnv.length > 0) {
            const readers = [];
            if (this._meterProviderConfig?.reader) {
                readers.push(this._meterProviderConfig.reader);
            }
            if (readers.length === 0) {
                metricReadersFromEnv.forEach((r) => readers.push(r));
            }
            const meterProvider = new sdk_metrics_1.MeterProvider({
                resource: this._resource,
                views: this._meterProviderConfig?.views ?? [],
                readers: readers,
            });
            this._meterProvider = meterProvider;
            api_1.metrics.setGlobalMeterProvider(meterProvider);
            // TODO: This is a workaround to fix https://github.com/open-telemetry/opentelemetry-js/issues/3609
            // If the MeterProvider is not yet registered when instrumentations are registered, all metrics are dropped.
            // This code is obsolete once https://github.com/open-telemetry/opentelemetry-js/issues/3622 is implemented.
            for (const instrumentation of this._instrumentations) {
                instrumentation.setMeterProvider(api_1.metrics.getMeterProvider());
            }
        }
    }
    shutdown() {
        const promises = [];
        if (this._tracerProvider) {
            promises.push(this._tracerProvider.shutdown());
        }
        if (this._loggerProvider) {
            promises.push(this._loggerProvider.shutdown());
        }
        if (this._meterProvider) {
            promises.push(this._meterProvider.shutdown());
        }
        return (Promise.all(promises)
            // return void instead of the array from Promise.all
            .then(() => { }));
    }
    configureLoggerProviderFromEnv() {
        const logExportersList = process.env.OTEL_LOGS_EXPORTER ?? '';
        const enabledExporters = (0, utils_1.filterBlanksAndNulls)(logExportersList.split(','));
        if (enabledExporters.length === 0) {
            api_1.diag.debug('OTEL_LOGS_EXPORTER is empty. Using default otlp exporter.');
            enabledExporters.push('otlp');
        }
        if (enabledExporters.includes('none')) {
            api_1.diag.info(`OTEL_LOGS_EXPORTER contains "none". Logger provider will not be initialized.`);
            return;
        }
        const exporters = [];
        enabledExporters.forEach(exporter => {
            if (exporter === 'otlp') {
                const protocol = (process.env.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL ??
                    process.env.OTEL_EXPORTER_OTLP_PROTOCOL)?.trim();
                switch (protocol) {
                    case 'grpc':
                        exporters.push(new exporter_logs_otlp_grpc_1.OTLPLogExporter());
                        break;
                    case 'http/json':
                        exporters.push(new exporter_logs_otlp_http_1.OTLPLogExporter());
                        break;
                    case 'http/protobuf':
                        exporters.push(new exporter_logs_otlp_proto_1.OTLPLogExporter());
                        break;
                    case undefined:
                    case '':
                        exporters.push(new exporter_logs_otlp_proto_1.OTLPLogExporter());
                        break;
                    default:
                        api_1.diag.warn(`Unsupported OTLP logs protocol: "${protocol}". Using http/protobuf.`);
                        exporters.push(new exporter_logs_otlp_proto_1.OTLPLogExporter());
                }
            }
            else if (exporter === 'console') {
                exporters.push(new sdk_logs_1.ConsoleLogRecordExporter());
            }
            else {
                api_1.diag.warn(`Unsupported OTEL_LOGS_EXPORTER value: "${exporter}". Supported values are: otlp, console, none.`);
            }
        });
        if (exporters.length > 0) {
            this._loggerProviderConfig = {
                logRecordProcessors: exporters.map(exporter => {
                    if (exporter instanceof sdk_logs_1.ConsoleLogRecordExporter) {
                        return new sdk_logs_1.SimpleLogRecordProcessor(exporter);
                    }
                    else {
                        return new sdk_logs_1.BatchLogRecordProcessor(exporter);
                    }
                }),
            };
        }
    }
}
exports.NodeSDK = NodeSDK;
//# sourceMappingURL=sdk.js.map