UNPKG

n8n

Version:

n8n Workflow Automation Tool

176 lines • 8.71 kB
"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 backend_network_1 = require("@n8n/backend-network"); 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_base_1 = require("@opentelemetry/sdk-trace-base"); const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node"); const n8n_core_1 = require("n8n-core"); const otel_settings_service_1 = require("./otel-settings.service"); const otel_constants_1 = require("./otel.constants"); const constants_1 = require("../../constants"); let OtelService = OtelService_1 = class OtelService { constructor(otelSettingsService, instanceSettings, logger, outboundHttp) { this.otelSettingsService = otelSettingsService; this.instanceSettings = instanceSettings; this.logger = logger; this.outboundHttp = outboundHttp; this.hasLoggedStartupConnectivityFailure = false; } async init() { const settings = await this.otelSettingsService.loadSettings(); this.start(settings); } async restart() { await this.shutdown(); const settings = await this.otelSettingsService.loadSettings(); this.start(settings); } async sendTestTrace(connection) { const url = this.buildOtlpTracesUrl(connection.exporterEndpoint, connection.exporterTracingPath); const exporter = new exporter_trace_otlp_proto_1.OTLPTraceExporter({ url, headers: this.parseOtlpHeaders(connection.exporterHeaders), timeoutMillis: connection.startupConnectivityTimeoutMs, }); let provider; try { return await new Promise((resolve) => { const processor = { onStart: () => { }, onEnd: (span) => exporter.export([span], (result) => resolve(result.error ? { success: false, error: result.error.message } : { success: true })), forceFlush: async () => { }, shutdown: async () => { }, }; provider = new sdk_trace_base_1.BasicTracerProvider({ resource: (0, resources_1.resourceFromAttributes)({ [otel_constants_1.ATTR.OTEL_SERVICE_NAME]: connection.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, }), sampler: new sdk_trace_node_1.TraceIdRatioBasedSampler(1), spanProcessors: [processor], }); const span = provider .getTracer('n8n-otel-test') .startSpan(otel_constants_1.OTEL_TEST_SPAN_NAME, { attributes: { [otel_constants_1.ATTR.IS_TEST_TRACE]: true } }); span.end(); }); } finally { await provider?.shutdown().catch(() => { }); await exporter.shutdown().catch(() => { }); } } start(settings) { this.hasLoggedStartupConnectivityFailure = false; if (!settings.enabled) return; this.configureDiagnosticsLogger(); void this.checkEndpointReachability(this.startSdk(settings), settings.startupConnectivityTimeoutMs); } async shutdown() { await this.sdk?.shutdown(); this.sdk = undefined; api_1.trace?.disable(); api_1.context?.disable(); api_1.propagation?.disable(); api_1.metrics?.disable(); } startSdk(settings) { const otlpTracesUrl = this.buildOtlpTracesUrl(settings.exporterEndpoint, settings.exporterTracingPath); const otlpHeaders = this.parseOtlpHeaders(settings.exporterHeaders); this.sdk = new sdk_node_1.NodeSDK({ resource: (0, resources_1.resourceFromAttributes)({ [otel_constants_1.ATTR.OTEL_SERVICE_NAME]: settings.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(settings.tracesSampleRate), }); this.sdk.start(); return otlpTracesUrl; } 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, timeoutMs) { try { await this.outboundHttp.transport({ ssrf: 'disabled' }).asCustomFetch()(url, { method: 'HEAD', signal: AbortSignal.timeout(timeoutMs), }); } 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_settings_service_1.OtelSettingsService, n8n_core_1.InstanceSettings, backend_common_1.Logger, backend_network_1.OutboundHttp]) ], OtelService); //# sourceMappingURL=otel.service.js.map