refactor(core): Improve workflow activation logs (#15180)

This commit is contained in:
Iván Ovejero
2025-05-09 14:55:20 +02:00
committed by GitHub
parent ae27b48ee7
commit 591aee5e62
4 changed files with 52 additions and 48 deletions

View File

@@ -17,6 +17,7 @@ export const LOG_SCOPES = [
'waiting-executions',
'task-runner',
'insights',
'workflow-activation',
] as const;
export type LogScope = (typeof LOG_SCOPES)[number];

View File

@@ -13,6 +13,7 @@ import { Workflow } from 'n8n-workflow';
import { ActiveWorkflowManager } from '@/active-workflow-manager';
import type { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import type { NodeTypes } from '@/node-types';
import { mockLogger } from '@test/mocking';
describe('ActiveWorkflowManager', () => {
let activeWorkflowManager: ActiveWorkflowManager;
@@ -23,7 +24,7 @@ describe('ActiveWorkflowManager', () => {
beforeEach(() => {
jest.clearAllMocks();
activeWorkflowManager = new ActiveWorkflowManager(
mock(),
mockLogger(),
mock(),
mock(),
mock(),
@@ -137,12 +138,12 @@ describe('ActiveWorkflowManager', () => {
);
workflowRepository.findById.mockResolvedValue(mock<WorkflowEntity>({ active: false }));
const result = await activeWorkflowManager.add('some-id', mode);
const added = await activeWorkflowManager.add('some-id', mode);
expect(checkSpy).not.toHaveBeenCalled();
expect(addWebhooksSpy).not.toHaveBeenCalled();
expect(addTriggersAndPollersSpy).not.toHaveBeenCalled();
expect(result).toBe(false);
expect(added).toEqual({ triggersAndPollers: false, webhooks: false });
},
);
});

View File

@@ -85,7 +85,9 @@ export class ActiveWorkflowManager {
private readonly instanceSettings: InstanceSettings,
private readonly publisher: Publisher,
private readonly workflowsConfig: WorkflowsConfig,
) {}
) {
this.logger = this.logger.scoped(['workflow-activation']);
}
async init() {
strict(
@@ -158,9 +160,7 @@ export class ActiveWorkflowManager {
const webhooks = WebhookHelpers.getWorkflowWebhooks(workflow, additionalData, undefined, true);
let path = '';
if (webhooks.length === 0) return;
this.logger.debug(`Adding webhooks for workflow "${workflow.name}" (ID ${workflow.id})`);
if (webhooks.length === 0) return false;
for (const webhookData of webhooks) {
const node = workflow.getNode(webhookData.node) as INode;
@@ -227,6 +227,12 @@ export class ActiveWorkflowManager {
await this.webhookService.populateCache();
await this.workflowStaticDataService.saveStaticData(workflow);
this.logger.debug(`Added webhooks for workflow "${workflow.name}" (ID ${workflow.id})`, {
workflowId: workflow.id,
});
return true;
}
/**
@@ -434,7 +440,7 @@ export class ActiveWorkflowManager {
await Promise.all(activationPromises);
}
this.logger.debug('Activated all trigger- and poller-based workflows');
this.logger.debug('Finished activating all workflows');
}
private async activateWorkflow(
@@ -445,13 +451,14 @@ export class ActiveWorkflowManager {
if (!dbWorkflow) return;
try {
const wasActivated = await this.add(dbWorkflow.id, activationMode, dbWorkflow, {
const added = await this.add(dbWorkflow.id, activationMode, dbWorkflow, {
shouldPublish: false,
});
if (wasActivated) {
if (added.webhooks || added.triggersAndPollers) {
this.logger.info(` - ${formatWorkflow(dbWorkflow)})`);
this.logger.info(' => Started');
this.logger.debug(`Successfully started workflow ${formatWorkflow(dbWorkflow)}`, {
this.logger.debug(`Activated workflow ${formatWorkflow(dbWorkflow)}`, {
workflowName: dbWorkflow.name,
workflowId: dbWorkflow.id,
});
@@ -517,6 +524,8 @@ export class ActiveWorkflowManager {
* Triggers and pollers are registered as active in memory at `ActiveWorkflows`,
* but webhooks are registered by being entered in the `webhook_entity` table,
* since webhooks do not require continuous execution.
*
* Returns whether this operation added webhooks and/or triggers and pollers.
*/
async add(
workflowId: WorkflowId,
@@ -524,13 +533,15 @@ export class ActiveWorkflowManager {
existingWorkflow?: WorkflowEntity,
{ shouldPublish } = { shouldPublish: true },
) {
const added = { webhooks: false, triggersAndPollers: false };
if (this.instanceSettings.isMultiMain && shouldPublish) {
void this.publisher.publishCommand({
command: 'add-webhooks-triggers-and-pollers',
payload: { workflowId },
});
return;
return added;
}
let workflow: Workflow;
@@ -538,10 +549,6 @@ export class ActiveWorkflowManager {
const shouldAddWebhooks = this.shouldAddWebhooks(activationMode);
const shouldAddTriggersAndPollers = this.shouldAddTriggersAndPollers();
const shouldDisplayActivationMessage =
(shouldAddWebhooks || shouldAddTriggersAndPollers) &&
['init', 'leadershipChange'].includes(activationMode);
try {
const dbWorkflow = existingWorkflow ?? (await this.workflowRepository.findById(workflowId));
@@ -554,18 +561,10 @@ export class ActiveWorkflowManager {
if (['init', 'leadershipChange'].includes(activationMode) && !dbWorkflow.active) {
this.logger.debug(
`Skipping workflow ${formatWorkflow(dbWorkflow)} as it is no longer active`,
{
workflowId: dbWorkflow.id,
},
{ workflowId: dbWorkflow.id },
);
return false;
}
if (shouldDisplayActivationMessage) {
this.logger.debug(`Initializing active workflow ${formatWorkflow(dbWorkflow)} (startup)`, {
workflowName: dbWorkflow.name,
workflowId: dbWorkflow.id,
});
return added;
}
workflow = new Workflow({
@@ -591,11 +590,16 @@ export class ActiveWorkflowManager {
const additionalData = await WorkflowExecuteAdditionalData.getBase();
if (shouldAddWebhooks) {
await this.addWebhooks(workflow, additionalData, 'trigger', activationMode);
added.webhooks = await this.addWebhooks(
workflow,
additionalData,
'trigger',
activationMode,
);
}
if (shouldAddTriggersAndPollers) {
await this.addTriggersAndPollers(dbWorkflow, workflow, {
added.triggersAndPollers = await this.addTriggersAndPollers(dbWorkflow, workflow, {
activationMode,
executionMode: 'trigger',
additionalData,
@@ -620,7 +624,7 @@ export class ActiveWorkflowManager {
// id of them in the static data. So make sure that data gets persisted.
await this.workflowStaticDataService.saveStaticData(workflow);
return shouldDisplayActivationMessage;
return added;
}
/**
@@ -863,24 +867,23 @@ export class ActiveWorkflowManager {
activationMode,
);
if (workflow.getTriggerNodes().length !== 0 || workflow.getPollNodes().length !== 0) {
this.logger.debug(`Adding triggers and pollers for workflow ${formatWorkflow(dbWorkflow)}`);
await this.activeWorkflows.add(
workflow.id,
workflow,
additionalData,
executionMode,
activationMode,
getTriggerFunctions,
getPollFunctions,
);
this.logger.debug(`Workflow ${formatWorkflow(dbWorkflow)} activated`, {
workflowId: dbWorkflow.id,
workflowName: dbWorkflow.name,
});
if (workflow.getTriggerNodes().length === 0 && workflow.getPollNodes().length === 0) {
return false;
}
await this.activeWorkflows.add(
workflow.id,
workflow,
additionalData,
executionMode,
activationMode,
getTriggerFunctions,
getPollFunctions,
);
this.logger.debug(`Added triggers and pollers for workflow ${formatWorkflow(dbWorkflow)}`);
return true;
}
async removeActivationError(workflowId: WorkflowId) {

View File

@@ -1,7 +1,7 @@
import type { WebhookEntity } from '@n8n/db';
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { InstanceSettings, Logger } from 'n8n-core';
import { InstanceSettings } from 'n8n-core';
import { FormTrigger } from 'n8n-nodes-base/nodes/Form/FormTrigger.node';
import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node';
import { NodeApiError, Workflow } from 'n8n-workflow';
@@ -32,7 +32,6 @@ import * as utils from './shared/utils/';
import { mockInstance } from '../shared/mocking';
mockInstance(ActiveExecutions);
mockInstance(Logger);
mockInstance(Push);
mockInstance(SecretsHelper);
mockInstance(ExecutionService);