UNPKG

n8n

Version:

n8n Workflow Automation Tool

250 lines • 10.3 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); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OtelLifecycleHandler = void 0; exports.countOutputItems = countOutputItems; exports.countInputItems = countInputItems; const decorators_1 = require("@n8n/decorators"); const backend_common_1 = require("@n8n/backend-common"); const di_1 = require("@n8n/di"); const execution_level_tracer_1 = require("./execution-level-tracer"); const otel_config_1 = require("./otel.config"); const tracing_context_1 = require("./tracing-context"); const ownership_service_1 = require("../../services/ownership.service"); const isCustomTelemetryTag = (value) => typeof value === 'object' && value !== null && !Array.isArray(value) && 'key' in value && 'value' in value && typeof value.key === 'string' && typeof value.value === 'string'; const getCustomTelemetryTags = (value) => { if (Array.isArray(value)) return value.filter(isCustomTelemetryTag); if (typeof value !== 'object' || value === null || !('tag' in value)) { return undefined; } const { tag } = value; return Array.isArray(tag) ? tag.filter(isCustomTelemetryTag) : undefined; }; let OtelLifecycleHandler = class OtelLifecycleHandler { constructor(tracer, traceContextService, config, ownershipService, logger) { this.tracer = tracer; this.traceContextService = traceContextService; this.config = config; this.ownershipService = ownershipService; this.logger = logger; } isPublishedWorkflow(workflow) { return !!(workflow.activeVersionId ?? workflow.active); } async onWorkflowStart(ctx) { if (this.config.productionExecutionsOnly && !this.isPublishedWorkflow(ctx.workflow)) return; const parentExecutionId = ctx.executionData?.parentExecution?.executionId; const tracingContext = parentExecutionId ? await this.traceContextService.get(parentExecutionId) : await this.traceContextService.get(ctx.executionId); const project = await this.ownershipService .getWorkflowProjectCached(ctx.workflow.id) .catch((error) => { this.logger.warn('Failed to fetch project for OTEL span', { workflowId: ctx.workflow.id, executionId: ctx.executionId, error: error instanceof Error ? error.message : String(error), }); return undefined; }); const spanContext = this.tracer.startWorkflow({ executionId: ctx.executionId, tracingContext, project: project ? { id: project.id, customAttributes: buildProjectCustomAttributes(project.customTelemetryTags), } : undefined, workflow: { id: ctx.workflow.id, name: ctx.workflow.name, versionId: ctx.workflow.versionId, nodeCount: ctx.workflow.nodes.length, customAttributes: this.buildWorkflowCustomAttributes(ctx), }, }); await this.traceContextService.persist(ctx.executionId, spanContext); } async onWorkflowResume(ctx) { if (this.config.productionExecutionsOnly && !this.isPublishedWorkflow(ctx.workflow)) return; const previousWorkflowExecution = await this.traceContextService.get(ctx.executionId); const project = await this.ownershipService .getWorkflowProjectCached(ctx.workflow.id) .catch((error) => { this.logger.warn('Failed to fetch project for OTEL span', { workflowId: ctx.workflow.id, executionId: ctx.executionId, error: error instanceof Error ? error.message : String(error), }); return undefined; }); this.tracer.startWorkflow({ executionId: ctx.executionId, linkTo: previousWorkflowExecution, project: project ? { id: project.id, customAttributes: buildProjectCustomAttributes(project.customTelemetryTags), } : undefined, workflow: { id: ctx.workflow.id, name: ctx.workflow.name, versionId: ctx.workflow.versionId, nodeCount: ctx.workflow.nodes.length, customAttributes: this.buildWorkflowCustomAttributes(ctx), }, }); } onWorkflowEnd(ctx) { if (this.config.productionExecutionsOnly && !this.isPublishedWorkflow(ctx.workflow)) return; this.tracer.endWorkflow({ executionId: ctx.executionId, status: ctx.runData.status, mode: ctx.runData.mode, error: ctx.runData.data.resultData.error, isRetry: ctx.runData.mode === 'retry', retryOf: ctx.retryOf, }); } onNodeStart(ctx) { if (this.config.productionExecutionsOnly && !this.isPublishedWorkflow(ctx.workflow)) return; if (!this.config.includeNodeSpans) return; const node = ctx.workflow.nodes.find((n) => n.name === ctx.nodeName); if (!node) return; this.tracer.startNode({ executionId: ctx.executionId, node, }); } onNodeEnd(ctx) { if (this.config.productionExecutionsOnly && !this.isPublishedWorkflow(ctx.workflow)) return; if (!this.config.includeNodeSpans) return; const node = ctx.workflow.nodes.find((n) => n.name === ctx.nodeName); if (!node) return; const customAttributes = ctx.taskData.metadata?.tracing ? Object.fromEntries(Object.entries(ctx.taskData.metadata.tracing).map(([key, value]) => [key, String(value)])) : undefined; this.tracer.endNode({ executionId: ctx.executionId, node, inputItemCount: countInputItems(ctx), outputItemCount: countOutputItems(ctx.taskData.data), error: ctx.taskData.error ?? undefined, customAttributes, }); } buildWorkflowCustomAttributes(ctx) { const tags = getCustomTelemetryTags(ctx.workflow.settings?.customTelemetryTags); if (!tags?.length) return; const customAttributes = {}; for (const { key, value } of tags) { const trimmedKey = key.trim(); if (!trimmedKey) continue; customAttributes[trimmedKey] = value; } if (Object.keys(customAttributes).length === 0) return; return customAttributes; } }; exports.OtelLifecycleHandler = OtelLifecycleHandler; __decorate([ (0, decorators_1.OnLifecycleEvent)('workflowExecuteBefore'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], OtelLifecycleHandler.prototype, "onWorkflowStart", null); __decorate([ (0, decorators_1.OnLifecycleEvent)('workflowExecuteResume'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], OtelLifecycleHandler.prototype, "onWorkflowResume", null); __decorate([ (0, decorators_1.OnLifecycleEvent)('workflowExecuteAfter'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], OtelLifecycleHandler.prototype, "onWorkflowEnd", null); __decorate([ (0, decorators_1.OnLifecycleEvent)('nodeExecuteBefore'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], OtelLifecycleHandler.prototype, "onNodeStart", null); __decorate([ (0, decorators_1.OnLifecycleEvent)('nodeExecuteAfter'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], OtelLifecycleHandler.prototype, "onNodeEnd", null); exports.OtelLifecycleHandler = OtelLifecycleHandler = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [execution_level_tracer_1.ExecutionLevelTracer, tracing_context_1.TraceContextService, otel_config_1.OtelConfig, ownership_service_1.OwnershipService, backend_common_1.Logger]) ], OtelLifecycleHandler); function buildProjectCustomAttributes(tags) { if (!tags?.length) return undefined; const attrs = {}; for (const { key, value } of tags) { attrs[key] = value; } return attrs; } function countOutputItems(data) { if (!data?.main) return 0; return data.main.reduce((sum, branch) => sum + (branch?.length ?? 0), 0); } function countInputItems(ctx) { const runData = ctx.executionData.resultData.runData; let total = 0; for (const source of ctx.taskData.source) { if (!source) continue; const sourceRuns = runData[source.previousNode]; if (!sourceRuns) continue; const run = sourceRuns[source.previousNodeRun ?? 0]; if (!run?.data?.main) continue; const branch = run.data.main[source.previousNodeOutput ?? 0]; total += branch?.length ?? 0; } return total; } //# sourceMappingURL=otel-lifecycle-handler.js.map