refactor(core): Modernize logger service (#11031)

This commit is contained in:
Iván Ovejero
2024-10-01 12:16:09 +02:00
committed by GitHub
parent f92637a9fe
commit 3a9c65e1cb
120 changed files with 554 additions and 297 deletions

View File

@@ -170,7 +170,7 @@
"typedi": "catalog:",
"uuid": "catalog:",
"validator": "13.7.0",
"winston": "3.8.2",
"winston": "3.14.2",
"ws": "8.17.1",
"xml2js": "catalog:",
"xmllint-wasm": "3.0.1",

View File

@@ -5,7 +5,7 @@ import type { InstanceSettings } from 'n8n-core';
import config from '@/config';
import { N8N_VERSION } from '@/constants';
import { License } from '@/license';
import type { Logger } from '@/logger';
import type { Logger } from '@/logging/logger.service';
jest.mock('@n8n_io/license-sdk');

View File

@@ -13,7 +13,7 @@ import { N8N_VERSION, TEMPLATES_DIR, inDevelopment, inTest } from '@/constants';
import * as Db from '@/db';
import { OnShutdown } from '@/decorators/on-shutdown';
import { ExternalHooks } from '@/external-hooks';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
import { send, sendErrorResponse } from '@/response-helper';
import { WaitingForms } from '@/waiting-forms';

View File

@@ -18,7 +18,7 @@ import type {
IExecutionDb,
IExecutionsCurrentSummary,
} from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { isWorkflowIdValid } from '@/utils';
import { ConcurrencyControlService } from './concurrency/concurrency-control.service';

View File

@@ -37,6 +37,7 @@ import { WorkflowRepository } from '@/databases/repositories/workflow.repository
import { OnShutdown } from '@/decorators/on-shutdown';
import { ExternalHooks } from '@/external-hooks';
import type { IWorkflowDb } from '@/interfaces';
import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types';
import { ActiveWorkflowsService } from '@/services/active-workflows.service';
import { OrchestrationService } from '@/services/orchestration.service';
@@ -47,7 +48,6 @@ import { WorkflowExecutionService } from '@/workflows/workflow-execution.service
import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.service';
import { ExecutionService } from './executions/execution.service';
import { Logger } from './logger';
interface QueuedActivation {
activationMode: WorkflowActivateMode;

View File

@@ -12,7 +12,7 @@ import { UserRepository } from '@/databases/repositories/user.repository';
import { AuthError } from '@/errors/response-errors/auth.error';
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { AuthenticatedRequest } from '@/requests';
import { JwtService } from '@/services/jwt.service';
import { UrlService } from '@/services/url.service';

View File

@@ -19,7 +19,7 @@ import { ExternalHooks } from '@/external-hooks';
import { ExternalSecretsManager } from '@/external-secrets/external-secrets-manager.ee';
import { License } from '@/license';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types';
import { PostHogClient } from '@/posthog';
import { ShutdownService } from '@/shutdown/shutdown.service';

View File

@@ -4,7 +4,7 @@ import { mock } from 'jest-mock-extended';
import { main } from '@/commands/db/revert';
import type { IrreversibleMigration, ReversibleMigration } from '@/databases/types';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { mockInstance } from '@test/mocking';
const logger = mockInstance(Logger);

View File

@@ -8,7 +8,7 @@ import { Container } from 'typedi';
import { getConnectionOptions } from '@/databases/config';
import type { Migration } from '@/databases/types';
import { wrapMigration } from '@/databases/utils/migration-helpers';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
// This function is extracted to make it easier to unit test it.
// Mocking turned into a mess due to this command using typeorm and the db

View File

@@ -365,10 +365,9 @@ export class Start extends BaseCommand {
if (executions.length === 0) return;
this.logger.debug(
'[Startup] Found enqueued executions to run',
executions.map((e) => e.id),
);
this.logger.debug('[Startup] Found enqueued executions to run', {
executionIds: executions.map((e) => e.id),
});
const ownershipService = Container.get(OwnershipService);
const workflowRunner = Container.get(WorkflowRunner);

View File

@@ -11,7 +11,7 @@ import type { ExecutionRepository } from '@/databases/repositories/execution.rep
import { InvalidConcurrencyLimitError } from '@/errors/invalid-concurrency-limit.error';
import type { EventService } from '@/events/event.service';
import type { IExecutingWorkflowData } from '@/interfaces';
import type { Logger } from '@/logger';
import type { Logger } from '@/logging/logger.service';
import type { Telemetry } from '@/telemetry';
import { ConcurrencyQueue } from '../concurrency-queue';

View File

@@ -7,7 +7,8 @@ import { InvalidConcurrencyLimitError } from '@/errors/invalid-concurrency-limit
import { UnknownExecutionModeError } from '@/errors/unknown-execution-mode.error';
import { EventService } from '@/events/event.service';
import type { IExecutingWorkflowData } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { LogMetadata } from '@/logging/types';
import { Telemetry } from '@/telemetry';
import { ConcurrencyQueue } from './concurrency-queue';
@@ -170,8 +171,8 @@ export class ConcurrencyControlService {
throw new UnknownExecutionModeError(mode);
}
private log(message: string, meta?: object) {
this.logger.debug(['[Concurrency Control]', message].join(' '), meta);
private log(message: string, metadata?: LogMetadata) {
this.logger.debug(['[Concurrency Control]', message].join(' '), metadata);
}
private shouldReport(capacity: number) {

View File

@@ -1,7 +1,6 @@
import { GlobalConfig } from '@n8n/config';
import convict from 'convict';
import { InstanceSettings } from 'n8n-core';
import { LOG_LEVELS } from 'n8n-workflow';
import path from 'path';
import { Container } from 'typedi';
@@ -296,41 +295,6 @@ export const schema = {
env: 'EXTERNAL_HOOK_FILES',
},
logs: {
level: {
doc: 'Log output level',
format: LOG_LEVELS,
default: 'info',
env: 'N8N_LOG_LEVEL',
},
output: {
doc: 'Where to output logs. Options are: console, file. Multiple can be separated by comma (",")',
format: String,
default: 'console',
env: 'N8N_LOG_OUTPUT',
},
file: {
fileCountMax: {
doc: 'Maximum number of files to keep.',
format: Number,
default: 100,
env: 'N8N_LOG_FILE_COUNT_MAX',
},
fileSizeMax: {
doc: 'Maximum size for each log file in MB.',
format: Number,
default: 16,
env: 'N8N_LOG_FILE_SIZE_MAX',
},
location: {
doc: 'Log file location; only used if log output is set to file.',
format: String,
default: path.join(Container.get(InstanceSettings).n8nFolder, 'logs/n8n.log'),
env: 'N8N_LOG_FILE_LOCATION',
},
},
},
push: {
backend: {
format: ['sse', 'websocket'] as const,

View File

@@ -14,7 +14,7 @@ import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
import { EventService } from '@/events/event.service';
import type { PublicUser } from '@/interfaces';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { MfaService } from '@/mfa/mfa.service';
import { PostHogClient } from '@/posthog';
import { AuthenticatedRequest, LoginRequest, UserRequest } from '@/requests';

View File

@@ -14,7 +14,7 @@ import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus'
import type { BooleanLicenseFeature, NumericLicenseFeature } from '@/interfaces';
import type { FeatureReturnType } from '@/license';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { MfaService } from '@/mfa/mfa.service';
import { Push } from '@/push';
import type { UserSetupPayload } from '@/requests';

View File

@@ -12,7 +12,7 @@ import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
import { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { PostHogClient } from '@/posthog';
import { UserRequest } from '@/requests';
import { PasswordUtility } from '@/services/password.utility';

View File

@@ -16,7 +16,7 @@ import { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks';
import { validateEntity } from '@/generic-helpers';
import type { PublicUser } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { MfaService } from '@/mfa/mfa.service';
import { AuthenticatedRequest, MeRequest } from '@/requests';
import { PasswordUtility } from '@/services/password.utility';

View File

@@ -15,7 +15,7 @@ import { VariablesService } from '@/environments/variables/variables.service.ee'
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { OAuthRequest } from '@/requests';
import { SecretsHelper } from '@/secrets-helpers';
import { mockInstance } from '@test/mocking';

View File

@@ -15,7 +15,7 @@ import { VariablesService } from '@/environments/variables/variables.service.ee'
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { OAuthRequest } from '@/requests';
import { SecretsHelper } from '@/secrets-helpers';
import { mockInstance } from '@test/mocking';

View File

@@ -15,7 +15,7 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks';
import type { ICredentialsDb } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { OAuthRequest } from '@/requests';
import { UrlService } from '@/services/url.service';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';

View File

@@ -9,7 +9,7 @@ import { GlobalScope, Post, RestController } from '@/decorators';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { EventService } from '@/events/event.service';
import { validateEntity } from '@/generic-helpers';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { PostHogClient } from '@/posthog';
import { OwnerRequest } from '@/requests';
import { PasswordUtility } from '@/services/password.utility';

View File

@@ -13,7 +13,7 @@ import { UnprocessableRequestError } from '@/errors/response-errors/unprocessabl
import { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { MfaService } from '@/mfa/mfa.service';
import { PasswordResetRequest } from '@/requests';
import { PasswordUtility } from '@/services/password.utility';

View File

@@ -18,7 +18,7 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks';
import type { PublicUser } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { listQueryMiddleware } from '@/middlewares';
import { AuthenticatedRequest, ListQuery, UserRequest } from '@/requests';
import { ProjectService } from '@/services/project.service';

View File

@@ -7,7 +7,7 @@ import { WorkflowStatisticsRepository } from '@/databases/repositories/workflow-
import { Get, Middleware, RestController } from '@/decorators';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { IWorkflowStatisticsDataLoaded } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { StatisticsRequest } from './workflow-statistics.types';

View File

@@ -6,7 +6,7 @@ import { join, dirname } from 'path';
import { Container } from 'typedi';
import { inProduction } from '@/constants';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
export const touchFile = async (filePath: string): Promise<void> => {
await mkdir(dirname(filePath), { recursive: true });

View File

@@ -5,7 +5,7 @@ import { Service } from 'typedi';
import { CredentialTypes } from '@/credential-types';
import type { ICredentialsOverwrite } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
@Service()
export class CredentialsOverwrites {

View File

@@ -23,7 +23,7 @@ import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { EventService } from '@/events/event.service';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { listQueryMiddleware } from '@/middlewares';
import { CredentialRequest } from '@/requests';
import { NamingService } from '@/services/naming.service';

View File

@@ -33,7 +33,7 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks';
import { validateEntity } from '@/generic-helpers';
import type { ICredentialsDb } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { userHasScopes } from '@/permissions/check-access';
import type { CredentialRequest, ListQuery } from '@/requests';
import { CredentialsTester } from '@/services/credentials-tester.service';

View File

@@ -12,7 +12,7 @@ import { mockInstance, mockEntityManager } from '@test/mocking';
describe('ExecutionRepository', () => {
const entityManager = mockEntityManager(ExecutionEntity);
const globalConfig = mockInstance(GlobalConfig);
const globalConfig = mockInstance(GlobalConfig, { logging: { outputs: ['console'] } });
const binaryDataService = mockInstance(BinaryDataService);
const executionRepository = Container.get(ExecutionRepository);
const mockDate = new Date('2023-12-28 12:34:56.789Z');

View File

@@ -47,7 +47,7 @@ import type {
IExecutionFlattedDb,
IExecutionResponse,
} from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { separate } from '@/utils';
import { ExecutionDataRepository } from './execution-data.repository';

View File

@@ -3,7 +3,7 @@ import { EventSubscriber } from '@n8n/typeorm';
import { ApplicationError, ErrorReporterProxy } from 'n8n-workflow';
import { Container } from 'typedi';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { Project } from '../entities/project';
import { User } from '../entities/user';

View File

@@ -1,7 +1,7 @@
import type { QueryRunner, ObjectLiteral } from '@n8n/typeorm';
import type { INodeTypes } from 'n8n-workflow';
import type { Logger } from '@/logger';
import type { Logger } from '@/logging/logger.service';
import type { createSchemaBuilder } from './dsl';

View File

@@ -9,7 +9,7 @@ import { Container } from 'typedi';
import { inTest } from '@/constants';
import { createSchemaBuilder } from '@/databases/dsl';
import type { BaseMigration, Migration, MigrationContext, MigrationFn } from '@/databases/types';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types';
const PERSONALIZATION_SURVEY_FILENAME = 'personalizationSurvey.json';

View File

@@ -11,7 +11,7 @@ import { SharedWorkflowRepository } from '@/databases/repositories/shared-workfl
import { TagRepository } from '@/databases/repositories/tag.repository';
import { WorkflowTagMappingRepository } from '@/databases/repositories/workflow-tag-mapping.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import {
SOURCE_CONTROL_CREDENTIAL_EXPORT_FOLDER,

View File

@@ -14,7 +14,7 @@ import type {
import { Service } from 'typedi';
import type { User } from '@/databases/entities/user';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { OwnershipService } from '@/services/ownership.service';
import {

View File

@@ -4,7 +4,7 @@ import path from 'path';
import { Container } from 'typedi';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import {
SOURCE_CONTROL_GIT_KEY_COMMENT,

View File

@@ -23,7 +23,7 @@ import { VariablesRepository } from '@/databases/repositories/variables.reposito
import { WorkflowTagMappingRepository } from '@/databases/repositories/workflow-tag-mapping.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import type { IWorkflowToImport } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { isUniqueConstraintError } from '@/response-helper';
import { assertNever } from '@/utils';

View File

@@ -9,7 +9,7 @@ import Container, { Service } from 'typedi';
import config from '@/config';
import { SettingsRepository } from '@/databases/repositories/settings.repository';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import {
SOURCE_CONTROL_SSH_FOLDER,

View File

@@ -10,7 +10,7 @@ import type { Variables } from '@/databases/entities/variables';
import { TagRepository } from '@/databases/repositories/tag.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { EventService } from '@/events/event.service';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import {
SOURCE_CONTROL_DEFAULT_EMAIL,

View File

@@ -2,7 +2,7 @@ import { MessageEventBusDestinationTypeNames } from 'n8n-workflow';
import { Container } from 'typedi';
import type { EventDestinations } from '@/databases/entities/event-destinations';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { MessageEventBusDestinationSentry } from './message-event-bus-destination-sentry.ee';
import { MessageEventBusDestinationSyslog } from './message-event-bus-destination-syslog.ee';

View File

@@ -7,7 +7,7 @@ import { MessageEventBusDestinationTypeNames } from 'n8n-workflow';
import syslog from 'syslog-client';
import Container from 'typedi';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { MessageEventBusDestination } from './message-event-bus-destination.ee';
import { eventMessageGenericDestinationTestEvent } from '../event-message-classes/event-message-generic';

View File

@@ -5,7 +5,7 @@ import { v4 as uuid } from 'uuid';
import { EventDestinationsRepository } from '@/databases/repositories/event-destinations.repository';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { EventMessageTypes } from '../event-message-classes';
import type { AbstractEventMessage } from '../event-message-classes/abstract-event-message';

View File

@@ -12,7 +12,7 @@ import Container from 'typedi';
import { Worker } from 'worker_threads';
import { inTest } from '@/constants';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { EventMessageTypes } from '../event-message-classes';
import { isEventMessageOptions } from '../event-message-classes/abstract-event-message';

View File

@@ -13,7 +13,7 @@ import { EventDestinationsRepository } from '@/databases/repositories/event-dest
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { OrchestrationService } from '@/services/orchestration.service';
import { ExecutionRecoveryService } from '../../executions/execution-recovery.service';

View File

@@ -37,6 +37,10 @@ describe('TelemetryEventRelay', () => {
includeQueueMetrics: false,
},
},
logging: {
level: 'info',
outputs: ['console'],
},
});
const workflowRepository = mock<WorkflowRepository>();
const nodeTypes = mock<NodeTypes>();

View File

@@ -9,7 +9,7 @@ import { ExecutionRepository } from '@/databases/repositories/execution.reposito
import { saveExecutionProgress } from '@/execution-lifecycle-hooks/save-execution-progress';
import * as fnModule from '@/execution-lifecycle-hooks/to-save-settings';
import type { IExecutionResponse } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { mockInstance } from '@test/mocking';
mockInstance(Logger);

View File

@@ -4,7 +4,7 @@ import type { IRun, WorkflowExecuteMode } from 'n8n-workflow';
import Container from 'typedi';
import config from '@/config';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
/**
* Whenever the execution ID is not available to the binary data service at the

View File

@@ -4,7 +4,7 @@ import { Container } from 'typedi';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { toSaveSettings } from '@/execution-lifecycle-hooks/to-save-settings';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
export async function saveExecutionProgress(
workflowData: IWorkflowBase,

View File

@@ -4,7 +4,7 @@ import { Container } from 'typedi';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import type { IExecutionDb, UpdateExecutionPayload } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { ExecutionMetadataService } from '@/services/execution-metadata.service';
import { isWorkflowIdValid } from '@/utils';

View File

@@ -10,7 +10,7 @@ import { NodeCrashedError } from '@/errors/node-crashed.error';
import { WorkflowCrashedError } from '@/errors/workflow-crashed.error';
import { EventService } from '@/events/event.service';
import type { IExecutionResponse } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { Push } from '@/push';
import { getWorkflowHooksMain } from '@/workflow-execute-additional-data'; // @TODO: Dependency cycle

View File

@@ -38,7 +38,7 @@ import type {
IWorkflowDb,
} from '@/interfaces';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types';
import { WaitTracker } from '@/wait-tracker';
import { WorkflowRunner } from '@/workflow-runner';

View File

@@ -10,7 +10,7 @@ import type {
SecretsProviderSettings,
} from '@/interfaces';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { OrchestrationService } from '@/services/orchestration.service';
import { EXTERNAL_SECRETS_INITIAL_BACKOFF, EXTERNAL_SECRETS_MAX_BACKOFF } from './constants';

View File

@@ -5,7 +5,7 @@ import { Container } from 'typedi';
import type { SecretsProviderSettings, SecretsProviderState } from '@/interfaces';
import { SecretsProvider } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { DOCS_HELP_NOTICE, EXTERNAL_SECRETS_NAME_REGEX } from '../constants';
import { preferGet } from '../external-secrets-helper.ee';

View File

@@ -14,7 +14,7 @@ import { SettingsRepository } from '@/databases/repositories/settings.repository
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { EventService } from '@/events/event.service';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import {
getCurrentAuthenticationMethod,
isEmailCurrentAuthenticationMethod,

View File

@@ -6,7 +6,7 @@ import Container, { Service } from 'typedi';
import config from '@/config';
import { SettingsRepository } from '@/databases/repositories/settings.repository';
import { OnShutdown } from '@/decorators/on-shutdown';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { LicenseMetricsService } from '@/metrics/license-metrics.service';
import { OrchestrationService } from '@/services/orchestration.service';

View File

@@ -6,7 +6,7 @@ import { WorkflowRepository } from '@/databases/repositories/workflow.repository
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { EventService } from '@/events/event.service';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { UrlService } from '@/services/url.service';
type LicenseError = Error & { errorId?: keyof typeof LicenseErrors };

View File

@@ -28,7 +28,7 @@ import {
CLI_DIR,
inE2ETests,
} from '@/constants';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
interface LoadedNodesAndCredentials {
nodes: INodeTypeData;

View File

@@ -1,117 +0,0 @@
import callsites from 'callsites';
import { LoggerProxy, type IDataObject, LOG_LEVELS } from 'n8n-workflow';
import { basename } from 'path';
import { Service } from 'typedi';
import { inspect } from 'util';
import winston from 'winston';
import config from '@/config';
const noOp = () => {};
@Service()
export class Logger {
private logger: winston.Logger;
constructor() {
const level = config.getEnv('logs.level');
this.logger = winston.createLogger({
level,
silent: level === 'silent',
});
// Change all methods with higher log-level to no-op
for (const levelName of LOG_LEVELS) {
if (this.logger.levels[levelName] > this.logger.levels[level]) {
Object.defineProperty(this, levelName, { value: noOp });
}
}
const output = config
.getEnv('logs.output')
.split(',')
.map((line) => line.trim());
if (output.includes('console')) {
let format: winston.Logform.Format;
if (level === 'debug') {
format = winston.format.combine(
winston.format.metadata(),
winston.format.timestamp(),
winston.format.colorize({ all: true }),
winston.format.printf(({ level: logLevel, message, timestamp, metadata }) => {
return `${timestamp} | ${logLevel.padEnd(18)} | ${message}${
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
Object.keys(metadata).length ? ` ${JSON.stringify(inspect(metadata))}` : ''
}`;
}),
);
} else {
format = winston.format.printf(({ message }: { message: string }) => message);
}
this.logger.add(
new winston.transports.Console({
format,
}),
);
}
if (output.includes('file')) {
const fileLogFormat = winston.format.combine(
winston.format.timestamp(),
winston.format.metadata(),
winston.format.json(),
);
this.logger.add(
new winston.transports.File({
filename: config.getEnv('logs.file.location'),
format: fileLogFormat,
maxsize: config.getEnv('logs.file.fileSizeMax') * 1048576, // config * 1mb
maxFiles: config.getEnv('logs.file.fileCountMax'),
}),
);
}
LoggerProxy.init(this);
}
private log(level: (typeof LOG_LEVELS)[number], message: string, meta: object = {}): void {
const callsite = callsites();
// We are using the third array element as the structure is as follows:
// [0]: this file
// [1]: Should be Logger
// [2]: Should point to the caller.
// Note: getting line number is useless because at this point
// We are in runtime, so it means we are looking at compiled js files
const logDetails = {} as IDataObject;
if (callsite[2] !== undefined) {
logDetails.file = basename(callsite[2].getFileName() || '');
const functionName = callsite[2].getFunctionName();
if (functionName) {
logDetails.function = functionName;
}
}
this.logger.log(level, message, { ...meta, ...logDetails });
}
// Convenience methods below
error(message: string, meta: object = {}): void {
this.log('error', message, meta);
}
warn(message: string, meta: object = {}): void {
this.log('warn', message, meta);
}
info(message: string, meta: object = {}): void {
this.log('info', message, meta);
}
debug(message: string, meta: object = {}): void {
this.log('debug', message, meta);
}
}

View File

@@ -0,0 +1,145 @@
import type { GlobalConfig } from '@n8n/config';
import { mock } from 'jest-mock-extended';
import type { InstanceSettings } from 'n8n-core';
import { Logger } from '@/logging/logger.service';
describe('Logger', () => {
describe('transports', () => {
test('if `console` selected, should set console transport', () => {
const globalConfig = mock<GlobalConfig>({
logging: {
level: 'info',
outputs: ['console'],
},
});
const logger = new Logger(globalConfig, mock<InstanceSettings>());
const { transports } = logger.getInternalLogger();
expect(transports).toHaveLength(1);
const [transport] = transports;
expect(transport.constructor.name).toBe('Console');
});
test('if `file` selected, should set file transport', () => {
const globalConfig = mock<GlobalConfig>({
logging: {
level: 'info',
outputs: ['file'],
file: {
fileSizeMax: 100,
fileCountMax: 16,
location: 'logs/n8n.log',
},
},
});
const logger = new Logger(globalConfig, mock<InstanceSettings>({ n8nFolder: '/tmp' }));
const { transports } = logger.getInternalLogger();
expect(transports).toHaveLength(1);
const [transport] = transports;
expect(transport.constructor.name).toBe('File');
});
});
describe('levels', () => {
test('if `error` selected, should enable `error` level', () => {
const globalConfig = mock<GlobalConfig>({
logging: {
level: 'error',
outputs: ['console'],
},
});
const logger = new Logger(globalConfig, mock<InstanceSettings>());
const internalLogger = logger.getInternalLogger();
expect(internalLogger.isErrorEnabled()).toBe(true);
expect(internalLogger.isWarnEnabled()).toBe(false);
expect(internalLogger.isInfoEnabled()).toBe(false);
expect(internalLogger.isDebugEnabled()).toBe(false);
});
test('if `warn` selected, should enable `error` and `warn` levels', () => {
const globalConfig = mock<GlobalConfig>({
logging: {
level: 'warn',
outputs: ['console'],
},
});
const logger = new Logger(globalConfig, mock<InstanceSettings>());
const internalLogger = logger.getInternalLogger();
expect(internalLogger.isErrorEnabled()).toBe(true);
expect(internalLogger.isWarnEnabled()).toBe(true);
expect(internalLogger.isInfoEnabled()).toBe(false);
expect(internalLogger.isDebugEnabled()).toBe(false);
});
test('if `info` selected, should enable `error`, `warn`, and `info` levels', () => {
const globalConfig = mock<GlobalConfig>({
logging: {
level: 'info',
outputs: ['console'],
},
});
const logger = new Logger(globalConfig, mock<InstanceSettings>());
const internalLogger = logger.getInternalLogger();
expect(internalLogger.isErrorEnabled()).toBe(true);
expect(internalLogger.isWarnEnabled()).toBe(true);
expect(internalLogger.isInfoEnabled()).toBe(true);
expect(internalLogger.isDebugEnabled()).toBe(false);
});
test('if `debug` selected, should enable all levels', () => {
const globalConfig = mock<GlobalConfig>({
logging: {
level: 'debug',
outputs: ['console'],
},
});
const logger = new Logger(globalConfig, mock<InstanceSettings>());
const internalLogger = logger.getInternalLogger();
expect(internalLogger.isErrorEnabled()).toBe(true);
expect(internalLogger.isWarnEnabled()).toBe(true);
expect(internalLogger.isInfoEnabled()).toBe(true);
expect(internalLogger.isDebugEnabled()).toBe(true);
});
test('if `silent` selected, should disable all levels', () => {
const globalConfig = mock<GlobalConfig>({
logging: {
level: 'silent',
outputs: ['console'],
},
});
const logger = new Logger(globalConfig, mock<InstanceSettings>());
const internalLogger = logger.getInternalLogger();
expect(internalLogger.isErrorEnabled()).toBe(false);
expect(internalLogger.isWarnEnabled()).toBe(false);
expect(internalLogger.isInfoEnabled()).toBe(false);
expect(internalLogger.isDebugEnabled()).toBe(false);
expect(internalLogger.silent).toBe(true);
});
});
});

View File

@@ -0,0 +1,3 @@
export const noOp = () => {};
export const LOG_LEVELS = ['error', 'warn', 'info', 'debug', 'silent'] as const;

View File

@@ -0,0 +1,148 @@
import { GlobalConfig } from '@n8n/config';
import callsites from 'callsites';
import { InstanceSettings } from 'n8n-core';
import { LoggerProxy, LOG_LEVELS } from 'n8n-workflow';
import path, { basename } from 'node:path';
import { Service } from 'typedi';
import winston from 'winston';
import { isObjectLiteral } from '@/utils';
import { noOp } from './constants';
import type { LogLocationMetadata, LogLevel, LogMetadata } from './types';
@Service()
export class Logger {
private readonly internalLogger: winston.Logger;
private readonly level: LogLevel;
constructor(
private readonly globalConfig: GlobalConfig,
private readonly instanceSettings: InstanceSettings,
) {
this.level = this.globalConfig.logging.level;
const isSilent = this.level === 'silent';
this.internalLogger = winston.createLogger({
level: this.level,
silent: isSilent,
});
if (!isSilent) {
this.setLevel();
const { outputs } = this.globalConfig.logging;
if (outputs.includes('console')) this.setConsoleTransport();
if (outputs.includes('file')) this.setFileTransport();
}
LoggerProxy.init(this);
}
private log(level: LogLevel, message: string, metadata: LogMetadata) {
const location: LogLocationMetadata = {};
const caller = callsites().at(2); // zeroth and first are this file, second is caller
if (caller !== undefined) {
location.file = basename(caller.getFileName() ?? '');
const fnName = caller.getFunctionName();
if (fnName) location.function = fnName;
}
this.internalLogger.log(level, message, { ...metadata, ...location });
}
private setLevel() {
const { levels } = this.internalLogger;
for (const logLevel of LOG_LEVELS) {
if (levels[logLevel] > levels[this.level]) {
// winston defines `{ error: 0, warn: 1, info: 2, debug: 5 }`
// so numerically higher (less severe) log levels become no-op
// to prevent overhead from `callsites` calls
Object.defineProperty(this, logLevel, { value: noOp });
}
}
}
private setConsoleTransport() {
const format =
this.level === 'debug'
? winston.format.combine(
winston.format.metadata(),
winston.format.timestamp(),
winston.format.colorize({ all: true }),
winston.format.printf(({ level, message, timestamp, metadata }) => {
const _metadata = this.toPrintable(metadata);
return `${timestamp} | ${level.padEnd(18)} | ${message}${_metadata}`;
}),
)
: winston.format.printf(({ message }: { message: string }) => message);
this.internalLogger.add(new winston.transports.Console({ format }));
}
private toPrintable(metadata: unknown) {
if (isObjectLiteral(metadata) && Object.keys(metadata).length > 0) {
return ' ' + JSON.stringify(metadata);
}
return '';
}
private setFileTransport() {
const format = winston.format.combine(
winston.format.timestamp(),
winston.format.metadata(),
winston.format.json(),
);
const filename = path.join(
this.instanceSettings.n8nFolder,
this.globalConfig.logging.file.location,
);
const { fileSizeMax, fileCountMax } = this.globalConfig.logging.file;
this.internalLogger.add(
new winston.transports.File({
filename,
format,
maxsize: fileSizeMax * 1_048_576, // config * 1 MiB in bytes
maxFiles: fileCountMax,
}),
);
}
// #region Convenience methods
error(message: string, metadata: LogMetadata = {}) {
this.log('error', message, metadata);
}
warn(message: string, metadata: LogMetadata = {}) {
this.log('warn', message, metadata);
}
info(message: string, metadata: LogMetadata = {}) {
this.log('info', message, metadata);
}
debug(message: string, metadata: LogMetadata = {}) {
this.log('debug', message, metadata);
}
// #endregion
// #region For testing only
getInternalLogger() {
return this.internalLogger;
}
// #endregion
}

View File

@@ -0,0 +1,7 @@
import type { LOG_LEVELS } from './constants';
export type LogLevel = (typeof LOG_LEVELS)[number];
export type LogLocationMetadata = Partial<{ file: string; function: string }>;
export type LogMetadata = Record<string, unknown> | Error;

View File

@@ -1,3 +1,5 @@
import type { GlobalConfig } from '@n8n/config';
import { mock } from 'jest-mock-extended';
import { InstanceSettings } from 'n8n-core';
import { PostHog } from 'posthog-node';
@@ -15,6 +17,8 @@ describe('PostHog', () => {
const instanceSettings = mockInstance(InstanceSettings, { instanceId });
const globalConfig = mock<GlobalConfig>({ logging: { level: 'debug' } });
beforeAll(() => {
config.set('diagnostics.config.posthog.apiKey', apiKey);
config.set('diagnostics.config.posthog.apiHost', apiHost);
@@ -26,7 +30,7 @@ describe('PostHog', () => {
});
it('inits PostHog correctly', async () => {
const ph = new PostHogClient(instanceSettings);
const ph = new PostHogClient(instanceSettings, globalConfig);
await ph.init();
expect(PostHog.prototype.constructor).toHaveBeenCalledWith(apiKey, { host: apiHost });
@@ -35,7 +39,7 @@ describe('PostHog', () => {
it('does not initialize or track if diagnostics are not enabled', async () => {
config.set('diagnostics.enabled', false);
const ph = new PostHogClient(instanceSettings);
const ph = new PostHogClient(instanceSettings, globalConfig);
await ph.init();
ph.track({
@@ -55,7 +59,7 @@ describe('PostHog', () => {
test: true,
};
const ph = new PostHogClient(instanceSettings);
const ph = new PostHogClient(instanceSettings, globalConfig);
await ph.init();
ph.track({
@@ -75,7 +79,7 @@ describe('PostHog', () => {
it('gets feature flags', async () => {
const createdAt = new Date();
const ph = new PostHogClient(instanceSettings);
const ph = new PostHogClient(instanceSettings, globalConfig);
await ph.init();
await ph.getFeatureFlags({

View File

@@ -1,3 +1,4 @@
import { GlobalConfig } from '@n8n/config';
import { InstanceSettings } from 'n8n-core';
import type { FeatureFlags, ITelemetryTrackProperties } from 'n8n-workflow';
import type { PostHog } from 'posthog-node';
@@ -10,7 +11,10 @@ import type { PublicUser } from '@/interfaces';
export class PostHogClient {
private postHog?: PostHog;
constructor(private readonly instanceSettings: InstanceSettings) {}
constructor(
private readonly instanceSettings: InstanceSettings,
private readonly globalConfig: GlobalConfig,
) {}
async init() {
const enabled = config.getEnv('diagnostics.enabled');
@@ -23,7 +27,7 @@ export class PostHogClient {
host: config.getEnv('diagnostics.config.posthog.apiHost'),
});
const logLevel = config.getEnv('logs.level');
const logLevel = this.globalConfig.logging.level;
if (logLevel === 'debug') {
this.postHog.debug(true);
}

View File

@@ -4,7 +4,7 @@ import { Container } from 'typedi';
import type WebSocket from 'ws';
import type { User } from '@/databases/entities/user';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { WebSocketPush } from '@/push/websocket.push';
import { mockInstance } from '@test/mocking';

View File

@@ -2,7 +2,7 @@ import type { PushPayload, PushType } from '@n8n/api-types';
import { assert, jsonStringify } from 'n8n-workflow';
import type { User } from '@/databases/entities/user';
import type { Logger } from '@/logger';
import type { Logger } from '@/logging/logger.service';
import type { OnPushMessage } from '@/push/types';
import { TypedEmitter } from '@/typed-emitter';

View File

@@ -1,7 +1,7 @@
import { Service } from 'typedi';
import type { User } from '@/databases/entities/user';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import SSEChannel from 'sse-channel';
import { AbstractPush } from './abstract.push';

View File

@@ -3,7 +3,7 @@ import { Service } from 'typedi';
import type WebSocket from 'ws';
import type { User } from '@/databases/entities/user';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { AbstractPush } from './abstract.push';

View File

@@ -10,9 +10,9 @@ import picocolors from 'picocolors';
import Container from 'typedi';
import { inDevelopment } from '@/constants';
import { Logger } from '@/logging/logger.service';
import { ResponseError } from './errors/response-errors/abstract/response.error';
import { Logger } from './logger';
export function sendSuccessResponse(
res: Response,

View File

@@ -8,7 +8,7 @@ import { Service } from 'typedi';
import config from '@/config';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';

View File

@@ -2,7 +2,7 @@ import type { Redis as SingleNodeClient, Cluster as MultiNodeClient } from 'iore
import { Service } from 'typedi';
import config from '@/config';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { RedisClientService } from '@/services/redis-client.service';
import type { PubSub } from './pubsub.types';

View File

@@ -2,7 +2,7 @@ import type { Redis as SingleNodeClient, Cluster as MultiNodeClient } from 'iore
import { Service } from 'typedi';
import config from '@/config';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { RedisClientService } from '@/services/redis-client.service';
import type { PubSub } from './pubsub.types';

View File

@@ -11,7 +11,7 @@ import { ExecutionRepository } from '@/databases/repositories/execution.reposito
import { OnShutdown } from '@/decorators/on-shutdown';
import { MaxStalledCountError } from '@/errors/max-stalled-count.error';
import { EventService } from '@/events/event.service';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { OrchestrationService } from '@/services/orchestration.service';
import { JOB_TYPE_NAME, QUEUE_NAME } from './constants';

View File

@@ -16,7 +16,7 @@ import { PortTakenError } from '@/errors/port-taken.error';
import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error';
import { ExternalHooks } from '@/external-hooks';
import type { ICredentialsOverwrite } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { PrometheusMetricsService } from '@/metrics/prometheus-metrics.service';
import { rawBodyReader, bodyParser } from '@/middlewares';
import * as ResponseHelper from '@/response-helper';

View File

@@ -6,7 +6,7 @@ import { Service } from 'typedi';
import config from '@/config';
import { getN8nPackageJson, inDevelopment } from '@/constants';
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { isApiEnabled } from '@/public-api';
import {
ENV_VARS_DOCS_URL,

View File

@@ -5,7 +5,7 @@ import type { User } from '@/databases/entities/user';
import { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
@Service()
export class ActiveWorkflowsService {

View File

@@ -1,3 +1,4 @@
import { GlobalConfig } from '@n8n/config';
import type { AiAssistantSDK } from '@n8n_io/ai-assistant-sdk';
import { AiAssistantClient } from '@n8n_io/ai-assistant-sdk';
import { assert, type IUser } from 'n8n-workflow';
@@ -14,7 +15,10 @@ import { License } from '../license';
export class AiAssistantService {
private client: AiAssistantClient | undefined;
constructor(private readonly licenseService: License) {}
constructor(
private readonly licenseService: License,
private readonly globalConfig: GlobalConfig,
) {}
async init() {
const aiAssistantEnabled = this.licenseService.isAiAssistantEnabled();
@@ -25,7 +29,7 @@ export class AiAssistantService {
const licenseCert = await this.licenseService.loadCertStr();
const consumerId = this.licenseService.getConsumerId();
const baseUrl = config.get('aiAssistant.baseUrl');
const logLevel = config.getEnv('logs.level');
const logLevel = this.globalConfig.logging.level;
this.client = new AiAssistantClient({
licenseCert,

View File

@@ -22,7 +22,7 @@ import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error';
import type { CommunityPackages } from '@/interfaces';
import { License } from '@/license';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { toError } from '@/utils';
import { OrchestrationService } from './orchestration.service';

View File

@@ -35,7 +35,7 @@ import { Service } from 'typedi';
import { CredentialTypes } from '@/credential-types';
import type { User } from '@/databases/entities/user';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';

View File

@@ -17,7 +17,7 @@ import { getVariablesLimit } from '@/environments/variables/environment-helpers'
import { getLdapLoginLabel } from '@/ldap/helpers.ee';
import { License } from '@/license';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { isApiEnabled } from '@/public-api';
import type { CommunityPackagesService } from '@/services/community-packages.service';
import { getSamlLoginLabel } from '@/sso/saml/saml-helpers';
@@ -123,7 +123,7 @@ export class FrontendService {
apiKey: config.getEnv('diagnostics.config.posthog.apiKey'),
autocapture: false,
disableSessionRecording: config.getEnv('deployment.type') !== 'cloud',
debug: config.getEnv('logs.level') === 'debug',
debug: this.globalConfig.logging.level === 'debug',
},
personalizationSurveyEnabled:
config.getEnv('personalization.enabled') && config.getEnv('diagnostics.enabled'),
@@ -153,7 +153,7 @@ export class FrontendService {
},
},
workflowTagsDisabled: config.getEnv('workflowTagsDisabled'),
logLevel: config.getEnv('logs.level'),
logLevel: this.globalConfig.logging.level,
hiringBannerEnabled: config.getEnv('hiringBanner.enabled'),
aiAssistant: {
enabled: false,

View File

@@ -11,7 +11,7 @@ import { CredentialsRepository } from '@/databases/repositories/credentials.repo
import { TagRepository } from '@/databases/repositories/tag.repository';
import * as Db from '@/db';
import type { ICredentialsDb } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { replaceInvalidCredentials } from '@/workflow-helpers';
@Service()

View File

@@ -4,7 +4,7 @@ import Container, { Service } from 'typedi';
import config from '@/config';
import type { PubSubCommandMap } from '@/events/maps/pub-sub.event-map';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { Publisher } from '@/scaling/pubsub/publisher.service';
import type { Subscriber } from '@/scaling/pubsub/subscriber.service';

View File

@@ -2,7 +2,7 @@ import { jsonParse } from 'n8n-workflow';
import os from 'node:os';
import { Container } from 'typedi';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { COMMAND_PUBSUB_CHANNEL } from '@/scaling/constants';
import type { PubSub } from '@/scaling/pubsub/pubsub.types';

View File

@@ -7,7 +7,7 @@ import { WorkflowRepository } from '@/databases/repositories/workflow.repository
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import { ExternalSecretsManager } from '@/external-secrets/external-secrets-manager.ee';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { Push } from '@/push';
import { CommunityPackagesService } from '@/services/community-packages.service';
import { OrchestrationService } from '@/services/orchestration.service';

View File

@@ -1,7 +1,7 @@
import { jsonParse } from 'n8n-workflow';
import Container from 'typedi';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { WORKER_RESPONSE_PUBSUB_CHANNEL } from '@/scaling/constants';
import type { PubSub } from '@/scaling/pubsub/pubsub.types';

View File

@@ -4,7 +4,7 @@ import { Service } from 'typedi';
import config from '@/config';
import { TIME } from '@/constants';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { Publisher } from '@/scaling/pubsub/publisher.service';
import { RedisClientService } from '@/services/redis-client.service';
import { TypedEmitter } from '@/typed-emitter';

View File

@@ -6,7 +6,7 @@ import { N8N_VERSION } from '@/constants';
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import { ExternalSecretsManager } from '@/external-secrets/external-secrets-manager.ee';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { COMMAND_PUBSUB_CHANNEL } from '@/scaling/constants';
import type { PubSub } from '@/scaling/pubsub/pubsub.types';
import { CommunityPackagesService } from '@/services/community-packages.service';

View File

@@ -6,7 +6,7 @@ import config from '@/config';
import { inTest, TIME } from '@/constants';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { OnShutdown } from '@/decorators/on-shutdown';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { OrchestrationService } from './orchestration.service';

View File

@@ -4,7 +4,7 @@ import type { Cluster, RedisOptions } from 'ioredis';
import { Service } from 'typedi';
import { Debounce } from '@/decorators/debounce';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { TypedEmitter } from '@/typed-emitter';
import type { RedisClientType } from '../scaling/redis/redis.types';

View File

@@ -7,7 +7,7 @@ import { UserRepository } from '@/databases/repositories/user.repository';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { EventService } from '@/events/event.service';
import type { Invitation, PublicUser } from '@/interfaces';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { PostHogClient } from '@/posthog';
import type { UserRequest } from '@/requests';
import { UrlService } from '@/services/url.service';

View File

@@ -4,7 +4,7 @@ import { Service } from 'typedi';
import { StatisticsNames } from '@/databases/entities/workflow-statistics';
import { WorkflowStatisticsRepository } from '@/databases/repositories/workflow-statistics.repository';
import { EventService } from '@/events/event.service';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { UserService } from '@/services/user.service';
import { TypedEmitter } from '@/typed-emitter';

View File

@@ -3,7 +3,7 @@ import { ApplicationError, ErrorReporterProxy, assert } from 'n8n-workflow';
import { Container, Service } from 'typedi';
import { LOWEST_SHUTDOWN_PRIORITY, HIGHEST_SHUTDOWN_PRIORITY } from '@/constants';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
type HandlerFn = () => Promise<void> | void;
export type ServiceClass = Class<Record<string, HandlerFn>>;

View File

@@ -2,7 +2,7 @@ import type express from 'express';
import { mock } from 'jest-mock-extended';
import type { IdentityProviderInstance, ServiceProviderInstance } from 'samlify';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { UrlService } from '@/services/url.service';
import * as samlHelpers from '@/sso/saml/saml-helpers';
import { SamlService } from '@/sso/saml/saml.service.ee';

View File

@@ -1,7 +1,7 @@
import { Container } from 'typedi';
import type { XMLFileInfo } from 'xmllint-wasm';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
let xml: XMLFileInfo;
let xmldsigCore: XMLFileInfo;

View File

@@ -12,7 +12,7 @@ import { SettingsRepository } from '@/databases/repositories/settings.repository
import { UserRepository } from '@/databases/repositories/user.repository';
import { AuthError } from '@/errors/response-errors/auth.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { UrlService } from '@/services/url.service';
import { SAML_PREFERENCES_DB_KEY } from './constants';

View File

@@ -5,7 +5,7 @@ import { Service } from 'typedi';
import type { Project } from '@/databases/entities/project';
import { SubworkflowPolicyDenialError } from '@/errors/subworkflow-policy-denial.error';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { AccessService } from '@/services/access.service';
import { OwnershipService } from '@/services/ownership.service';
import { UrlService } from '@/services/url.service';

View File

@@ -1,3 +1,4 @@
import type { GlobalConfig } from '@n8n/config';
import type RudderStack from '@rudderstack/rudder-sdk-node';
import { mock } from 'jest-mock-extended';
import { InstanceSettings } from 'n8n-core';
@@ -41,10 +42,17 @@ describe('Telemetry', () => {
beforeEach(async () => {
spyTrack.mockClear();
const postHog = new PostHogClient(instanceSettings);
const postHog = new PostHogClient(instanceSettings, mock());
await postHog.init();
telemetry = new Telemetry(mock(), postHog, mock(), instanceSettings, mock());
telemetry = new Telemetry(
mock(),
postHog,
mock(),
instanceSettings,
mock(),
mock<GlobalConfig>({ logging: { level: 'info', outputs: ['console'] } }),
);
// @ts-expect-error Assigning to private property
telemetry.rudderStack = mockRudderStack;
});

View File

@@ -1,3 +1,4 @@
import { GlobalConfig } from '@n8n/config';
import type RudderStack from '@rudderstack/rudder-sdk-node';
import axios from 'axios';
import { InstanceSettings } from 'n8n-core';
@@ -13,7 +14,7 @@ import { WorkflowRepository } from '@/databases/repositories/workflow.repository
import { OnShutdown } from '@/decorators/on-shutdown';
import type { IExecutionTrackProperties } from '@/interfaces';
import { License } from '@/license';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { PostHogClient } from '@/posthog';
import { SourceControlPreferencesService } from '../environments/source-control/source-control-preferences.service.ee';
@@ -49,6 +50,7 @@ export class Telemetry {
private readonly license: License,
private readonly instanceSettings: InstanceSettings,
private readonly workflowRepository: WorkflowRepository,
private readonly globalConfig: GlobalConfig,
) {}
async init() {
@@ -62,7 +64,7 @@ export class Telemetry {
return;
}
const logLevel = config.getEnv('logs.level');
const logLevel = this.globalConfig.logging.level;
const { default: RudderStack } = await import('@rudderstack/rudder-sdk-node');
const axiosInstance = axios.create();

View File

@@ -7,7 +7,7 @@ import { createTransport } from 'nodemailer';
import type SMTPConnection from 'nodemailer/lib/smtp-connection';
import { Service } from 'typedi';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import type { MailData, SendEmailResult } from './interfaces';

View File

@@ -11,7 +11,7 @@ import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
import { UserRepository } from '@/databases/repositories/user.repository';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { EventService } from '@/events/event.service';
import { Logger } from '@/logger';
import { Logger } from '@/logging/logger.service';
import { UrlService } from '@/services/url.service';
import { toError } from '@/utils';

Some files were not shown because too many files have changed in this diff Show More