"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__tsfTypeAliases = void 0;
exports.init = init;
exports.shutdownTelemetry = shutdownTelemetry;
exports.resetTelemetryForTests = resetTelemetryForTests;
const tslib_1 = require("tslib");
const _a = tslib_1.__importStar(require("@zyno-io/ts-server-foundation/type-metadata-runtime"));
require("../../timezone");
const node_os_1 = require("node:os");
const api_1 = require("@opentelemetry/api");
const exporter_metrics_otlp_http_1 = require("@opentelemetry/exporter-metrics-otlp-http");
const exporter_prometheus_1 = require("@opentelemetry/exporter-prometheus");
const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
const instrumentation_1 = require("@opentelemetry/instrumentation");
const instrumentation_dns_1 = require("@opentelemetry/instrumentation-dns");
const instrumentation_http_1 = require("@opentelemetry/instrumentation-http");
const instrumentation_ioredis_1 = require("@opentelemetry/instrumentation-ioredis");
const instrumentation_mysql2_1 = require("@opentelemetry/instrumentation-mysql2");
const instrumentation_pg_1 = require("@opentelemetry/instrumentation-pg");
const instrumentation_undici_1 = require("@opentelemetry/instrumentation-undici");
const resources_1 = require("@opentelemetry/resources");
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 const_1 = require("../../app/const");
const current_1 = require("../../app/current");
const env_1 = require("../../env");
const package_1 = require("../../helpers/io/package");
const context_1 = require("../../http/context");
const helpers_1 = require("./helpers");
const metrics_1 = require("./metrics");
tslib_1.__exportStar(require("./helpers"), exports);
function init(options = {}) {
    if (options.disabled || readTelemetrySetting('OTEL_SDK_DISABLED') === 'true')
        return;
    const shouldInstallTraces = shouldInstallTraceProvider(options);
    const shouldInstallMetrics = shouldInstallMeterProvider(options);
    const hasInstalledProviders = !!(helpers_1.OtelState.tracerProvider || helpers_1.OtelState.meterProvider);
    if (helpers_1.OtelState.initialized && (hasInstalledProviders || (!shouldInstallTraces && !shouldInstallMetrics)))
        return;
    helpers_1.OtelState.initialized = true;
    if (options.enableMetricsEndpoint !== undefined)
        helpers_1.OtelState.metricsEndpointPreference = options.enableMetricsEndpoint;
    helpers_1.OtelState.metricsEndpointEnabled ||= shouldEnableMetricsEndpoint(options);
    if (readTelemetrySetting('OTEL_DEBUG')) {
        api_1.diag.setLogger(new api_1.DiagConsoleLogger(), api_1.DiagLogLevel.DEBUG);
    }
    if (!shouldInstallTraces && !shouldInstallMetrics)
        return;
    const resource = buildResource(options);
    if (shouldInstallMetrics) {
        const { meterProvider, prometheusExporter } = createMeterProvider(resource, options);
        api_1.metrics.setGlobalMeterProvider(meterProvider);
        helpers_1.OtelState.meterProvider = meterProvider;
        helpers_1.OtelState.prometheusExporter = prometheusExporter;
    }
    if (shouldInstallTraces) {
        const tracerProvider = new sdk_trace_node_1.NodeTracerProvider({
            resource,
            spanProcessors: options.spanProcessors?.length ? [...options.spanProcessors] : [createDefaultSpanProcessor()]
        });
        tracerProvider.register();
        helpers_1.OtelState.tracerProvider = tracerProvider;
        helpers_1.OtelState.tracer = tracerProvider.getTracer(options.serviceName ?? 'default');
        installHttpTraceContextResolver();
    }
    helpers_1.OtelState.unregisterInstrumentations = (0, instrumentation_1.registerInstrumentations)({
        instrumentations: createDefaultInstrumentations(options)
    });
}
async function shutdownTelemetry() {
    const tracerProvider = helpers_1.OtelState.tracerProvider;
    const meterProvider = helpers_1.OtelState.meterProvider;
    const results = await Promise.allSettled([
        Promise.resolve().then(() => tracerProvider?.shutdown()),
        Promise.resolve().then(() => meterProvider?.shutdown())
    ]);
    const failures = results.filter((result) => result.status === 'rejected').map(result => result.reason);
    try {
        resetTelemetryForTests();
    }
    catch (error) {
        failures.push(error);
    }
    if (failures.length === 1)
        throw failures[0];
    if (failures.length > 1)
        throw new AggregateError(failures, 'Telemetry shutdown failed');
}
function resetTelemetryForTests() {
    const unregisterInstrumentations = helpers_1.OtelState.unregisterInstrumentations;
    helpers_1.OtelState.initialized = false;
    helpers_1.OtelState.tracer = undefined;
    helpers_1.OtelState.tracerProvider = undefined;
    helpers_1.OtelState.meterProvider = undefined;
    helpers_1.OtelState.prometheusExporter = undefined;
    helpers_1.OtelState.unregisterInstrumentations = undefined;
    helpers_1.OtelState.metricsEndpointEnabled = false;
    helpers_1.OtelState.metricsEndpointPreference = undefined;
    const failures = [];
    const runCleanup = (cleanup) => {
        try {
            cleanup();
        }
        catch (error) {
            failures.push(error);
        }
    };
    if (unregisterInstrumentations)
        runCleanup(unregisterInstrumentations);
    runCleanup(restoreHttpTraceContextResolver);
    runCleanup(() => api_1.trace.disable());
    runCleanup(() => api_1.metrics.disable());
    runCleanup(() => api_1.context.disable());
    runCleanup(() => api_1.propagation.disable());
    if (failures.length === 1)
        throw failures[0];
    if (failures.length > 1)
        throw new AggregateError(failures, 'Telemetry reset failed');
}
function shouldInstallTraceProvider(options) {
    return !!(readTelemetrySetting('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') ||
        readTelemetrySetting('OTEL_EXPORTER_OTLP_ENDPOINT') ||
        options.spanProcessors?.length);
}
function shouldInstallMeterProvider(options) {
    return shouldPushMetrics() || shouldEnableMetricsEndpoint(options) || !!options.metricReaders?.length;
}
function buildResource(options) {
    const packageJson = (0, package_1.getPackageJson)();
    return (0, resources_1.resourceFromAttributes)({
        'service.name': options.serviceName ?? packageJson?.name ?? 'unknown',
        'service.version': options.serviceVersion ?? packageJson?.version ?? 'unknown',
        'deployment.environment': readTelemetrySetting('APP_ENV'),
        'host.name': (0, node_os_1.hostname)(),
        'process.pid': process.pid
    });
}
function createDefaultSpanProcessor() {
    const exporter = new exporter_trace_otlp_http_1.OTLPTraceExporter();
    return (0, const_1.isDevelopmentEnvironment)() ? new sdk_trace_base_1.SimpleSpanProcessor(exporter) : new sdk_trace_base_1.BatchSpanProcessor(exporter);
}
function createMeterProvider(resource, options) {
    const readers = [...(options.metricReaders ?? [])];
    let prometheusExporter;
    if (shouldPushMetrics()) {
        readers.push(new sdk_metrics_1.PeriodicExportingMetricReader({
            exporter: new exporter_metrics_otlp_http_1.OTLPMetricExporter(),
            exportIntervalMillis: 10000
        }));
    }
    if (shouldEnableMetricsEndpoint(options)) {
        prometheusExporter = new exporter_prometheus_1.PrometheusExporter({ preventServerStart: true });
        readers.push(prometheusExporter);
    }
    return {
        meterProvider: new sdk_metrics_1.MeterProvider({ resource, readers }),
        prometheusExporter
    };
}
function createDefaultInstrumentations(options) {
    return [
        new instrumentation_http_1.HttpInstrumentation({
            startIncomingSpanHook: options.httpIncomingRequestAttributeHook,
            ignoreIncomingRequestHook: (request) => request.url === '/healthz' || request.url === '/metrics',
            ignoreOutgoingRequestHook: (request) => !!String(request.host ?? request.hostname ?? '').match(/sentry\./)
        }),
        new instrumentation_undici_1.UndiciInstrumentation({
            ignoreRequestHook: (request) => /(\/healthz|\/metrics|sentry\.)/.test(`${request.origin}${request.path}`)
        }),
        new instrumentation_dns_1.DnsInstrumentation(),
        new instrumentation_ioredis_1.IORedisInstrumentation(),
        new instrumentation_mysql2_1.MySQL2Instrumentation(),
        new instrumentation_pg_1.PgInstrumentation(),
        ...(options.instrumentations ?? [])
    ];
}
function shouldPushMetrics() {
    return !!(readTelemetrySetting('OTEL_EXPORTER_OTLP_METRICS_ENDPOINT') || readTelemetrySetting('OTEL_EXPORTER_OTLP_ENDPOINT'));
}
function shouldEnableMetricsEndpoint(options) {
    if (options.enableMetricsEndpoint === false)
        return false;
    return options.enableMetricsEndpoint === true || (0, metrics_1.isOtelMetricsEndpointEnabled)();
}
function readTelemetrySetting(key) {
    const envValue = env_1.Env[key];
    if (envValue !== undefined)
        return envValue;
    try {
        const configValue = (0, current_1.getCurrentApp)().config[key];
        if (configValue === undefined || configValue === null)
            return undefined;
        return String(configValue);
    }
    catch {
        return undefined;
    }
}
let httpContextResolverInstalled = false;
let previousHttpContextResolver;
function installHttpTraceContextResolver() {
    if (httpContextResolverInstalled)
        return;
    httpContextResolverInstalled = true;
    previousHttpContextResolver = (0, context_1.getHttpContextResolver)();
    (0, context_1.setHttpContextResolver)(request => {
        const context = previousHttpContextResolver?.(request) ?? {};
        const traceId = (0, helpers_1.getTraceContext)()?.traceId;
        return traceId ? { ...context, traceId } : context;
    });
}
function restoreHttpTraceContextResolver() {
    if (previousHttpContextResolver)
        (0, context_1.setHttpContextResolver)(previousHttpContextResolver);
    previousHttpContextResolver = undefined;
    httpContextResolverInstalled = false;
}
exports.__tsfTypeAliases = _a.decodeCompactMetadataV1("[1,{\"HttpIncomingRequestAttributeHook\":{\"kind\":2,\"typeName\":\"(request: IncomingMessage) =\\u003e Record\\u003cstring, AttributeValue\\u003e\",\"typeName\":\"HttpIncomingRequestAttributeHook\"},\"IOtelOptions\":{\"kind\":18,\"typeName\":\"TelemetryInitOptions\",\"types\":[{\"kind\":20,\"name\":\"serviceName\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"serviceVersion\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"disabled\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"instrumentations\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"$tsfAlias\":[0,\"@opentelemetry/instrumentation\",\"Instrumentation\",\"Instrumentation\"]}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"httpIncomingRequestAttributeHook\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"(request: IncomingMessage) =\\u003e Record\\u003cstring, AttributeValue\\u003e\",\"typeName\":\"HttpIncomingRequestAttributeHook\"},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"enableMetricsEndpoint\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"spanProcessors\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"$tsfAlias\":[0,\"@opentelemetry/sdk-trace-base\",\"SpanProcessor\",\"SpanProcessor\"]}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"metricReaders\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"$tsfAlias\":[0,\"@opentelemetry/sdk-metrics\",\"MetricReader\",\"MetricReader\"]}},{\"kind\":4}]},\"optional\":true}],\"typeName\":\"IOtelOptions\"},\"SpanInfo\":{\"kind\":12,\"types\":[{\"kind\":18,\"types\":[{\"kind\":20,\"name\":\"traceId\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"spanId\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"traceFlags\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true}]},{\"kind\":18,\"types\":[{\"kind\":20,\"name\":\"traceparent\",\"type\":{\"kind\":6},\"optional\":false}]},{\"kind\":4}],\"typeName\":\"SpanInfo\"},\"SpanLinkRef\":{\"kind\":18,\"types\":[{\"kind\":20,\"name\":\"traceId\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"spanId\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"traceFlags\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"attributes\",\"type\":{\"kind\":12,\"types\":[{\"$tsfAlias\":[0,\"@opentelemetry/api\",\"Attributes\",\"Attributes\"]},{\"kind\":4}]},\"optional\":true}],\"typeName\":\"SpanLinkRef\"},\"TelemetryInitOptions\":{\"kind\":18,\"typeName\":\"TelemetryInitOptions\",\"types\":[{\"kind\":20,\"name\":\"serviceName\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"serviceVersion\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"disabled\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"instrumentations\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"$tsfAlias\":[0,\"@opentelemetry/instrumentation\",\"Instrumentation\",\"Instrumentation\"]}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"httpIncomingRequestAttributeHook\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"(request: IncomingMessage) =\\u003e Record\\u003cstring, AttributeValue\\u003e\",\"typeName\":\"HttpIncomingRequestAttributeHook\"},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"enableMetricsEndpoint\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"spanProcessors\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"$tsfAlias\":[0,\"@opentelemetry/sdk-trace-base\",\"SpanProcessor\",\"SpanProcessor\"]}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"metricReaders\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"$tsfAlias\":[0,\"@opentelemetry/sdk-metrics\",\"MetricReader\",\"MetricReader\"]}},{\"kind\":4}]},\"optional\":true}]}}]", [require]);
//# sourceMappingURL=index.js.map