"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var OtelService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.OtelService = void 0;
const backend_common_1 = require("@n8n/backend-common");
const di_1 = require("@n8n/di");
const api_1 = require("@opentelemetry/api");
const exporter_trace_otlp_proto_1 = require("@opentelemetry/exporter-trace-otlp-proto");
const resources_1 = require("@opentelemetry/resources");
const sdk_node_1 = require("@opentelemetry/sdk-node");
const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
const n8n_core_1 = require("n8n-core");
const constants_1 = require("../../constants");
const otel_config_1 = require("./otel.config");
const otel_constants_1 = require("./otel.constants");
let OtelService = OtelService_1 = class OtelService {
    constructor(config, instanceSettings, logger) {
        this.config = config;
        this.instanceSettings = instanceSettings;
        this.logger = logger;
        this.hasLoggedStartupConnectivityFailure = false;
    }
    init() {
        if (!this.config.enabled)
            return;
        this.configureDiagnosticsLogger();
        const otlpTracesUrl = this.buildOtlpTracesUrl(this.config.exporterEndpoint, this.config.exporterTracingPath);
        const otlpHeaders = this.parseOtlpHeaders(this.config.exporterHeaders);
        this.sdk = new sdk_node_1.NodeSDK({
            resource: (0, resources_1.resourceFromAttributes)({
                [otel_constants_1.ATTR.OTEL_SERVICE_NAME]: this.config.exporterServiceName,
                [otel_constants_1.ATTR.OTEL_SERVICE_VERSION]: constants_1.N8N_VERSION,
                [otel_constants_1.ATTR.INSTANCE_ID]: this.instanceSettings.instanceId,
                [otel_constants_1.ATTR.INSTANCE_ROLE]: this.instanceSettings.instanceType,
            }),
            traceExporter: new exporter_trace_otlp_proto_1.OTLPTraceExporter({
                url: otlpTracesUrl,
                headers: otlpHeaders,
            }),
            sampler: new sdk_trace_node_1.TraceIdRatioBasedSampler(this.config.tracesSampleRate),
        });
        this.sdk.start();
        void this.checkEndpointReachability(otlpTracesUrl);
    }
    async shutdown() {
        await this.sdk?.shutdown();
    }
    parseOtlpHeaders(headersToSplit) {
        const headers = {};
        for (const pair of headersToSplit.split(',')) {
            const trimmedPair = pair.trim();
            if (!trimmedPair)
                continue;
            if (!trimmedPair.includes('=')) {
                this.logger.warn(`Skipping invalid OTEL exporter header "${trimmedPair}": missing "=" separator. Expected format: "key=value".`);
                continue;
            }
            const [key, ...rest] = trimmedPair.split('=');
            const trimmedKey = key.trim();
            if (!trimmedKey) {
                this.logger.warn(`Skipping invalid OTEL exporter header "${trimmedPair}": empty key. Expected format: "key=value".`);
                continue;
            }
            headers[trimmedKey] = rest.join('=').trim();
        }
        return headers;
    }
    configureDiagnosticsLogger() {
        if (OtelService_1.isDiagnosticsLoggerConfigured)
            return;
        const diagnosticsLogger = {
            error: (...args) => this.logger.error('OpenTelemetry diagnostics error', { args }),
            warn: (...args) => this.logger.warn('OpenTelemetry diagnostics warning', { args }),
            info: (...args) => this.logger.info('OpenTelemetry diagnostics info', { args }),
            debug: (...args) => this.logger.debug('OpenTelemetry diagnostics debug', { args }),
            verbose: (...args) => this.logger.debug('OpenTelemetry diagnostics verbose', { args }),
        };
        api_1.diag.setLogger(diagnosticsLogger, api_1.DiagLogLevel.WARN);
        OtelService_1.isDiagnosticsLoggerConfigured = true;
    }
    buildOtlpTracesUrl(endpoint, path) {
        const exporterEndpointWithoutTrailingSlash = endpoint.replace(/\/+$/, '');
        return `${exporterEndpointWithoutTrailingSlash}${path}`;
    }
    async checkEndpointReachability(url) {
        try {
            await fetch(url, {
                method: 'HEAD',
                signal: AbortSignal.timeout(this.config.startupConnectivityTimeoutMs),
            });
        }
        catch (error) {
            if (this.hasLoggedStartupConnectivityFailure)
                return;
            this.hasLoggedStartupConnectivityFailure = true;
            this.logger.error('Failed to connect to OpenTelemetry OTLP endpoint during startup', {
                endpoint: url,
                error: error instanceof Error ? error.message : String(error),
            });
        }
    }
};
exports.OtelService = OtelService;
OtelService.isDiagnosticsLoggerConfigured = false;
exports.OtelService = OtelService = OtelService_1 = __decorate([
    (0, di_1.Service)(),
    __metadata("design:paramtypes", [otel_config_1.OtelConfig,
        n8n_core_1.InstanceSettings,
        backend_common_1.Logger])
], OtelService);
//# sourceMappingURL=otel.service.js.map