refactor(core): Modernize logger service (#11031)
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Benchmark Docker Image CI / build (push) Waiting to run

This commit is contained in:
Iván Ovejero 2024-10-01 12:16:09 +02:00 committed by GitHub
parent f92637a9fe
commit 3a9c65e1cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
120 changed files with 554 additions and 297 deletions

View file

@ -0,0 +1,47 @@
import { Config, Env, Nested } from '../decorators';
import { StringArray } from '../utils';
@Config
class FileLoggingConfig {
/**
* Max number of log files to keep, or max number of days to keep logs for.
* Once the limit is reached, the oldest log files will be rotated out.
* If using days, append a `d` suffix. Only for `file` log output.
*
* @example `N8N_LOG_FILE_COUNT_MAX=7` will keep at most 7 files.
* @example `N8N_LOG_FILE_COUNT_MAX=7d` will keep at most 7 days worth of files.
*/
@Env('N8N_LOG_FILE_COUNT_MAX')
fileCountMax: number = 100;
/** Max size (in MiB) for each log file. Only for `file` log output. */
@Env('N8N_LOG_FILE_SIZE_MAX')
fileSizeMax: number = 16;
/** Location of the log files inside `~/.n8n`. Only for `file` log output. */
@Env('N8N_LOG_FILE_LOCATION')
location: string = 'logs/n8n.log';
}
@Config
export class LoggingConfig {
/**
* Minimum level of logs to output. Logs with this or higher level will be output;
* logs with lower levels will not. Exception: `silent` disables all logging.
*
* @example `N8N_LOG_LEVEL=info` will output `error`, `warn` and `info` logs, but not `debug`.
*/
@Env('N8N_LOG_LEVEL')
level: 'error' | 'warn' | 'info' | 'debug' | 'silent' = 'info';
/**
* Where to output logs to. Options are: `console` or `file` or both in a comma separated list.
*
* @example `N8N_LOG_OUTPUT=console,file` will output to both console and file.
*/
@Env('N8N_LOG_OUTPUT')
outputs: StringArray<'console' | 'file'> = ['console'];
@Nested
file: FileLoggingConfig;
}

View file

@ -5,6 +5,7 @@ import { EndpointsConfig } from './configs/endpoints.config';
import { EventBusConfig } from './configs/event-bus.config'; import { EventBusConfig } from './configs/event-bus.config';
import { ExternalSecretsConfig } from './configs/external-secrets.config'; import { ExternalSecretsConfig } from './configs/external-secrets.config';
import { ExternalStorageConfig } from './configs/external-storage.config'; import { ExternalStorageConfig } from './configs/external-storage.config';
import { LoggingConfig } from './configs/logging.config';
import { NodesConfig } from './configs/nodes.config'; import { NodesConfig } from './configs/nodes.config';
import { PublicApiConfig } from './configs/public-api.config'; import { PublicApiConfig } from './configs/public-api.config';
import { ScalingModeConfig } from './configs/scaling-mode.config'; import { ScalingModeConfig } from './configs/scaling-mode.config';
@ -81,4 +82,7 @@ export class GlobalConfig {
@Nested @Nested
queue: ScalingModeConfig; queue: ScalingModeConfig;
@Nested
logging: LoggingConfig;
} }

View file

@ -0,0 +1,7 @@
export class StringArray<T extends string> extends Array<T> {
constructor(str: string) {
super();
const parsed = str.split(',') as StringArray<T>;
return parsed.every((i) => typeof i === 'string') ? parsed : [];
}
}

View file

@ -225,6 +225,15 @@ describe('GlobalConfig', () => {
backendDsn: '', backendDsn: '',
frontendDsn: '', frontendDsn: '',
}, },
logging: {
level: 'info',
outputs: ['console'],
file: {
fileCountMax: 100,
fileSizeMax: 16,
location: 'logs/n8n.log',
},
},
}; };
it('should use all default values when no env variables are defined', () => { it('should use all default values when no env variables are defined', () => {

View file

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

View file

@ -5,7 +5,7 @@ import type { InstanceSettings } from 'n8n-core';
import config from '@/config'; import config from '@/config';
import { N8N_VERSION } from '@/constants'; import { N8N_VERSION } from '@/constants';
import { License } from '@/license'; import { License } from '@/license';
import type { Logger } from '@/logger'; import type { Logger } from '@/logging/logger.service';
jest.mock('@n8n_io/license-sdk'); 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 * as Db from '@/db';
import { OnShutdown } from '@/decorators/on-shutdown'; import { OnShutdown } from '@/decorators/on-shutdown';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares'; import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
import { send, sendErrorResponse } from '@/response-helper'; import { send, sendErrorResponse } from '@/response-helper';
import { WaitingForms } from '@/waiting-forms'; import { WaitingForms } from '@/waiting-forms';

View file

@ -18,7 +18,7 @@ import type {
IExecutionDb, IExecutionDb,
IExecutionsCurrentSummary, IExecutionsCurrentSummary,
} from '@/interfaces'; } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { isWorkflowIdValid } from '@/utils'; import { isWorkflowIdValid } from '@/utils';
import { ConcurrencyControlService } from './concurrency/concurrency-control.service'; 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 { OnShutdown } from '@/decorators/on-shutdown';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import type { IWorkflowDb } from '@/interfaces'; import type { IWorkflowDb } from '@/interfaces';
import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types'; import { NodeTypes } from '@/node-types';
import { ActiveWorkflowsService } from '@/services/active-workflows.service'; import { ActiveWorkflowsService } from '@/services/active-workflows.service';
import { OrchestrationService } from '@/services/orchestration.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 { WorkflowStaticDataService } from '@/workflows/workflow-static-data.service';
import { ExecutionService } from './executions/execution.service'; import { ExecutionService } from './executions/execution.service';
import { Logger } from './logger';
interface QueuedActivation { interface QueuedActivation {
activationMode: WorkflowActivateMode; activationMode: WorkflowActivateMode;

View file

@ -12,7 +12,7 @@ import { UserRepository } from '@/databases/repositories/user.repository';
import { AuthError } from '@/errors/response-errors/auth.error'; import { AuthError } from '@/errors/response-errors/auth.error';
import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import type { AuthenticatedRequest } from '@/requests'; import type { AuthenticatedRequest } from '@/requests';
import { JwtService } from '@/services/jwt.service'; import { JwtService } from '@/services/jwt.service';
import { UrlService } from '@/services/url.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 { ExternalSecretsManager } from '@/external-secrets/external-secrets-manager.ee';
import { License } from '@/license'; import { License } from '@/license';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types'; import { NodeTypes } from '@/node-types';
import { PostHogClient } from '@/posthog'; import { PostHogClient } from '@/posthog';
import { ShutdownService } from '@/shutdown/shutdown.service'; 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 { main } from '@/commands/db/revert';
import type { IrreversibleMigration, ReversibleMigration } from '@/databases/types'; import type { IrreversibleMigration, ReversibleMigration } from '@/databases/types';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { mockInstance } from '@test/mocking'; import { mockInstance } from '@test/mocking';
const logger = mockInstance(Logger); const logger = mockInstance(Logger);

View file

@ -8,7 +8,7 @@ import { Container } from 'typedi';
import { getConnectionOptions } from '@/databases/config'; import { getConnectionOptions } from '@/databases/config';
import type { Migration } from '@/databases/types'; import type { Migration } from '@/databases/types';
import { wrapMigration } from '@/databases/utils/migration-helpers'; 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. // 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 // 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; if (executions.length === 0) return;
this.logger.debug( this.logger.debug('[Startup] Found enqueued executions to run', {
'[Startup] Found enqueued executions to run', executionIds: executions.map((e) => e.id),
executions.map((e) => e.id), });
);
const ownershipService = Container.get(OwnershipService); const ownershipService = Container.get(OwnershipService);
const workflowRunner = Container.get(WorkflowRunner); 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 { InvalidConcurrencyLimitError } from '@/errors/invalid-concurrency-limit.error';
import type { EventService } from '@/events/event.service'; import type { EventService } from '@/events/event.service';
import type { IExecutingWorkflowData } from '@/interfaces'; import type { IExecutingWorkflowData } from '@/interfaces';
import type { Logger } from '@/logger'; import type { Logger } from '@/logging/logger.service';
import type { Telemetry } from '@/telemetry'; import type { Telemetry } from '@/telemetry';
import { ConcurrencyQueue } from '../concurrency-queue'; 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 { UnknownExecutionModeError } from '@/errors/unknown-execution-mode.error';
import { EventService } from '@/events/event.service'; import { EventService } from '@/events/event.service';
import type { IExecutingWorkflowData } from '@/interfaces'; 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 { Telemetry } from '@/telemetry';
import { ConcurrencyQueue } from './concurrency-queue'; import { ConcurrencyQueue } from './concurrency-queue';
@ -170,8 +171,8 @@ export class ConcurrencyControlService {
throw new UnknownExecutionModeError(mode); throw new UnknownExecutionModeError(mode);
} }
private log(message: string, meta?: object) { private log(message: string, metadata?: LogMetadata) {
this.logger.debug(['[Concurrency Control]', message].join(' '), meta); this.logger.debug(['[Concurrency Control]', message].join(' '), metadata);
} }
private shouldReport(capacity: number) { private shouldReport(capacity: number) {

View file

@ -1,7 +1,6 @@
import { GlobalConfig } from '@n8n/config'; import { GlobalConfig } from '@n8n/config';
import convict from 'convict'; import convict from 'convict';
import { InstanceSettings } from 'n8n-core'; import { InstanceSettings } from 'n8n-core';
import { LOG_LEVELS } from 'n8n-workflow';
import path from 'path'; import path from 'path';
import { Container } from 'typedi'; import { Container } from 'typedi';
@ -296,41 +295,6 @@ export const schema = {
env: 'EXTERNAL_HOOK_FILES', 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: { push: {
backend: { backend: {
format: ['sse', 'websocket'] as const, 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 { EventService } from '@/events/event.service';
import type { PublicUser } from '@/interfaces'; import type { PublicUser } from '@/interfaces';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { MfaService } from '@/mfa/mfa.service'; import { MfaService } from '@/mfa/mfa.service';
import { PostHogClient } from '@/posthog'; import { PostHogClient } from '@/posthog';
import { AuthenticatedRequest, LoginRequest, UserRequest } from '@/requests'; 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 { BooleanLicenseFeature, NumericLicenseFeature } from '@/interfaces';
import type { FeatureReturnType } from '@/license'; import type { FeatureReturnType } from '@/license';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { MfaService } from '@/mfa/mfa.service'; import { MfaService } from '@/mfa/mfa.service';
import { Push } from '@/push'; import { Push } from '@/push';
import type { UserSetupPayload } from '@/requests'; 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 { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { PostHogClient } from '@/posthog'; import { PostHogClient } from '@/posthog';
import { UserRequest } from '@/requests'; import { UserRequest } from '@/requests';
import { PasswordUtility } from '@/services/password.utility'; import { PasswordUtility } from '@/services/password.utility';

View file

@ -16,7 +16,7 @@ import { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import { validateEntity } from '@/generic-helpers'; import { validateEntity } from '@/generic-helpers';
import type { PublicUser } from '@/interfaces'; import type { PublicUser } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { MfaService } from '@/mfa/mfa.service'; import { MfaService } from '@/mfa/mfa.service';
import { AuthenticatedRequest, MeRequest } from '@/requests'; import { AuthenticatedRequest, MeRequest } from '@/requests';
import { PasswordUtility } from '@/services/password.utility'; 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 { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import type { OAuthRequest } from '@/requests'; import type { OAuthRequest } from '@/requests';
import { SecretsHelper } from '@/secrets-helpers'; import { SecretsHelper } from '@/secrets-helpers';
import { mockInstance } from '@test/mocking'; 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 { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import type { OAuthRequest } from '@/requests'; import type { OAuthRequest } from '@/requests';
import { SecretsHelper } from '@/secrets-helpers'; import { SecretsHelper } from '@/secrets-helpers';
import { mockInstance } from '@test/mocking'; 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 { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import type { ICredentialsDb } from '@/interfaces'; import type { ICredentialsDb } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import type { OAuthRequest } from '@/requests'; import type { OAuthRequest } from '@/requests';
import { UrlService } from '@/services/url.service'; import { UrlService } from '@/services/url.service';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data'; 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 { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { EventService } from '@/events/event.service'; import { EventService } from '@/events/event.service';
import { validateEntity } from '@/generic-helpers'; import { validateEntity } from '@/generic-helpers';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { PostHogClient } from '@/posthog'; import { PostHogClient } from '@/posthog';
import { OwnerRequest } from '@/requests'; import { OwnerRequest } from '@/requests';
import { PasswordUtility } from '@/services/password.utility'; 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 { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { MfaService } from '@/mfa/mfa.service'; import { MfaService } from '@/mfa/mfa.service';
import { PasswordResetRequest } from '@/requests'; import { PasswordResetRequest } from '@/requests';
import { PasswordUtility } from '@/services/password.utility'; 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 { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import type { PublicUser } from '@/interfaces'; import type { PublicUser } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { listQueryMiddleware } from '@/middlewares'; import { listQueryMiddleware } from '@/middlewares';
import { AuthenticatedRequest, ListQuery, UserRequest } from '@/requests'; import { AuthenticatedRequest, ListQuery, UserRequest } from '@/requests';
import { ProjectService } from '@/services/project.service'; 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 { Get, Middleware, RestController } from '@/decorators';
import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { IWorkflowStatisticsDataLoaded } from '@/interfaces'; import type { IWorkflowStatisticsDataLoaded } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { StatisticsRequest } from './workflow-statistics.types'; import { StatisticsRequest } from './workflow-statistics.types';

View file

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

View file

@ -5,7 +5,7 @@ import { Service } from 'typedi';
import { CredentialTypes } from '@/credential-types'; import { CredentialTypes } from '@/credential-types';
import type { ICredentialsOverwrite } from '@/interfaces'; import type { ICredentialsOverwrite } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
@Service() @Service()
export class CredentialsOverwrites { 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 { NotFoundError } from '@/errors/response-errors/not-found.error';
import { EventService } from '@/events/event.service'; import { EventService } from '@/events/event.service';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { listQueryMiddleware } from '@/middlewares'; import { listQueryMiddleware } from '@/middlewares';
import { CredentialRequest } from '@/requests'; import { CredentialRequest } from '@/requests';
import { NamingService } from '@/services/naming.service'; 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 { ExternalHooks } from '@/external-hooks';
import { validateEntity } from '@/generic-helpers'; import { validateEntity } from '@/generic-helpers';
import type { ICredentialsDb } from '@/interfaces'; import type { ICredentialsDb } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { userHasScopes } from '@/permissions/check-access'; import { userHasScopes } from '@/permissions/check-access';
import type { CredentialRequest, ListQuery } from '@/requests'; import type { CredentialRequest, ListQuery } from '@/requests';
import { CredentialsTester } from '@/services/credentials-tester.service'; import { CredentialsTester } from '@/services/credentials-tester.service';

View file

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

View file

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

View file

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

View file

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

View file

@ -9,7 +9,7 @@ import { Container } from 'typedi';
import { inTest } from '@/constants'; import { inTest } from '@/constants';
import { createSchemaBuilder } from '@/databases/dsl'; import { createSchemaBuilder } from '@/databases/dsl';
import type { BaseMigration, Migration, MigrationContext, MigrationFn } from '@/databases/types'; import type { BaseMigration, Migration, MigrationContext, MigrationFn } from '@/databases/types';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types'; import { NodeTypes } from '@/node-types';
const PERSONALIZATION_SURVEY_FILENAME = 'personalizationSurvey.json'; 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 { TagRepository } from '@/databases/repositories/tag.repository';
import { WorkflowTagMappingRepository } from '@/databases/repositories/workflow-tag-mapping.repository'; import { WorkflowTagMappingRepository } from '@/databases/repositories/workflow-tag-mapping.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { import {
SOURCE_CONTROL_CREDENTIAL_EXPORT_FOLDER, SOURCE_CONTROL_CREDENTIAL_EXPORT_FOLDER,

View file

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

View file

@ -4,7 +4,7 @@ import path from 'path';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { import {
SOURCE_CONTROL_GIT_KEY_COMMENT, 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 { WorkflowTagMappingRepository } from '@/databases/repositories/workflow-tag-mapping.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import type { IWorkflowToImport } from '@/interfaces'; import type { IWorkflowToImport } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { isUniqueConstraintError } from '@/response-helper'; import { isUniqueConstraintError } from '@/response-helper';
import { assertNever } from '@/utils'; import { assertNever } from '@/utils';

View file

@ -9,7 +9,7 @@ import Container, { Service } from 'typedi';
import config from '@/config'; import config from '@/config';
import { SettingsRepository } from '@/databases/repositories/settings.repository'; import { SettingsRepository } from '@/databases/repositories/settings.repository';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { import {
SOURCE_CONTROL_SSH_FOLDER, 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 { TagRepository } from '@/databases/repositories/tag.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { EventService } from '@/events/event.service'; import { EventService } from '@/events/event.service';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { import {
SOURCE_CONTROL_DEFAULT_EMAIL, SOURCE_CONTROL_DEFAULT_EMAIL,

View file

@ -2,7 +2,7 @@ import { MessageEventBusDestinationTypeNames } from 'n8n-workflow';
import { Container } from 'typedi'; import { Container } from 'typedi';
import type { EventDestinations } from '@/databases/entities/event-destinations'; 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 { MessageEventBusDestinationSentry } from './message-event-bus-destination-sentry.ee';
import { MessageEventBusDestinationSyslog } from './message-event-bus-destination-syslog.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 syslog from 'syslog-client';
import Container from 'typedi'; import Container from 'typedi';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { MessageEventBusDestination } from './message-event-bus-destination.ee'; import { MessageEventBusDestination } from './message-event-bus-destination.ee';
import { eventMessageGenericDestinationTestEvent } from '../event-message-classes/event-message-generic'; 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 { EventDestinationsRepository } from '@/databases/repositories/event-destinations.repository';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import type { EventMessageTypes } from '../event-message-classes'; import type { EventMessageTypes } from '../event-message-classes';
import type { AbstractEventMessage } from '../event-message-classes/abstract-event-message'; 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 { Worker } from 'worker_threads';
import { inTest } from '@/constants'; import { inTest } from '@/constants';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import type { EventMessageTypes } from '../event-message-classes'; import type { EventMessageTypes } from '../event-message-classes';
import { isEventMessageOptions } from '../event-message-classes/abstract-event-message'; 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 { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { OrchestrationService } from '@/services/orchestration.service'; import { OrchestrationService } from '@/services/orchestration.service';
import { ExecutionRecoveryService } from '../../executions/execution-recovery.service'; import { ExecutionRecoveryService } from '../../executions/execution-recovery.service';

View file

@ -37,6 +37,10 @@ describe('TelemetryEventRelay', () => {
includeQueueMetrics: false, includeQueueMetrics: false,
}, },
}, },
logging: {
level: 'info',
outputs: ['console'],
},
}); });
const workflowRepository = mock<WorkflowRepository>(); const workflowRepository = mock<WorkflowRepository>();
const nodeTypes = mock<NodeTypes>(); 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 { saveExecutionProgress } from '@/execution-lifecycle-hooks/save-execution-progress';
import * as fnModule from '@/execution-lifecycle-hooks/to-save-settings'; import * as fnModule from '@/execution-lifecycle-hooks/to-save-settings';
import type { IExecutionResponse } from '@/interfaces'; import type { IExecutionResponse } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { mockInstance } from '@test/mocking'; import { mockInstance } from '@test/mocking';
mockInstance(Logger); mockInstance(Logger);

View file

@ -4,7 +4,7 @@ import type { IRun, WorkflowExecuteMode } from 'n8n-workflow';
import Container from 'typedi'; import Container from 'typedi';
import config from '@/config'; 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 * 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 { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { toSaveSettings } from '@/execution-lifecycle-hooks/to-save-settings'; import { toSaveSettings } from '@/execution-lifecycle-hooks/to-save-settings';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
export async function saveExecutionProgress( export async function saveExecutionProgress(
workflowData: IWorkflowBase, workflowData: IWorkflowBase,

View file

@ -4,7 +4,7 @@ import { Container } from 'typedi';
import { ExecutionRepository } from '@/databases/repositories/execution.repository'; import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import type { IExecutionDb, UpdateExecutionPayload } from '@/interfaces'; import type { IExecutionDb, UpdateExecutionPayload } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { ExecutionMetadataService } from '@/services/execution-metadata.service'; import { ExecutionMetadataService } from '@/services/execution-metadata.service';
import { isWorkflowIdValid } from '@/utils'; 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 { WorkflowCrashedError } from '@/errors/workflow-crashed.error';
import { EventService } from '@/events/event.service'; import { EventService } from '@/events/event.service';
import type { IExecutionResponse } from '@/interfaces'; import type { IExecutionResponse } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { Push } from '@/push'; import { Push } from '@/push';
import { getWorkflowHooksMain } from '@/workflow-execute-additional-data'; // @TODO: Dependency cycle import { getWorkflowHooksMain } from '@/workflow-execute-additional-data'; // @TODO: Dependency cycle

View file

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

View file

@ -10,7 +10,7 @@ import type {
SecretsProviderSettings, SecretsProviderSettings,
} from '@/interfaces'; } from '@/interfaces';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { OrchestrationService } from '@/services/orchestration.service'; import { OrchestrationService } from '@/services/orchestration.service';
import { EXTERNAL_SECRETS_INITIAL_BACKOFF, EXTERNAL_SECRETS_MAX_BACKOFF } from './constants'; 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 type { SecretsProviderSettings, SecretsProviderState } from '@/interfaces';
import { SecretsProvider } 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 { DOCS_HELP_NOTICE, EXTERNAL_SECRETS_NAME_REGEX } from '../constants';
import { preferGet } from '../external-secrets-helper.ee'; 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 { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error'; import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { EventService } from '@/events/event.service'; import { EventService } from '@/events/event.service';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { import {
getCurrentAuthenticationMethod, getCurrentAuthenticationMethod,
isEmailCurrentAuthenticationMethod, isEmailCurrentAuthenticationMethod,

View file

@ -6,7 +6,7 @@ import Container, { Service } from 'typedi';
import config from '@/config'; import config from '@/config';
import { SettingsRepository } from '@/databases/repositories/settings.repository'; import { SettingsRepository } from '@/databases/repositories/settings.repository';
import { OnShutdown } from '@/decorators/on-shutdown'; import { OnShutdown } from '@/decorators/on-shutdown';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { LicenseMetricsService } from '@/metrics/license-metrics.service'; import { LicenseMetricsService } from '@/metrics/license-metrics.service';
import { OrchestrationService } from '@/services/orchestration.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 { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { EventService } from '@/events/event.service'; import { EventService } from '@/events/event.service';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { UrlService } from '@/services/url.service'; import { UrlService } from '@/services/url.service';
type LicenseError = Error & { errorId?: keyof typeof LicenseErrors }; type LicenseError = Error & { errorId?: keyof typeof LicenseErrors };

View file

@ -28,7 +28,7 @@ import {
CLI_DIR, CLI_DIR,
inE2ETests, inE2ETests,
} from '@/constants'; } from '@/constants';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
interface LoadedNodesAndCredentials { interface LoadedNodesAndCredentials {
nodes: INodeTypeData; 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 { InstanceSettings } from 'n8n-core';
import { PostHog } from 'posthog-node'; import { PostHog } from 'posthog-node';
@ -15,6 +17,8 @@ describe('PostHog', () => {
const instanceSettings = mockInstance(InstanceSettings, { instanceId }); const instanceSettings = mockInstance(InstanceSettings, { instanceId });
const globalConfig = mock<GlobalConfig>({ logging: { level: 'debug' } });
beforeAll(() => { beforeAll(() => {
config.set('diagnostics.config.posthog.apiKey', apiKey); config.set('diagnostics.config.posthog.apiKey', apiKey);
config.set('diagnostics.config.posthog.apiHost', apiHost); config.set('diagnostics.config.posthog.apiHost', apiHost);
@ -26,7 +30,7 @@ describe('PostHog', () => {
}); });
it('inits PostHog correctly', async () => { it('inits PostHog correctly', async () => {
const ph = new PostHogClient(instanceSettings); const ph = new PostHogClient(instanceSettings, globalConfig);
await ph.init(); await ph.init();
expect(PostHog.prototype.constructor).toHaveBeenCalledWith(apiKey, { host: apiHost }); 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 () => { it('does not initialize or track if diagnostics are not enabled', async () => {
config.set('diagnostics.enabled', false); config.set('diagnostics.enabled', false);
const ph = new PostHogClient(instanceSettings); const ph = new PostHogClient(instanceSettings, globalConfig);
await ph.init(); await ph.init();
ph.track({ ph.track({
@ -55,7 +59,7 @@ describe('PostHog', () => {
test: true, test: true,
}; };
const ph = new PostHogClient(instanceSettings); const ph = new PostHogClient(instanceSettings, globalConfig);
await ph.init(); await ph.init();
ph.track({ ph.track({
@ -75,7 +79,7 @@ describe('PostHog', () => {
it('gets feature flags', async () => { it('gets feature flags', async () => {
const createdAt = new Date(); const createdAt = new Date();
const ph = new PostHogClient(instanceSettings); const ph = new PostHogClient(instanceSettings, globalConfig);
await ph.init(); await ph.init();
await ph.getFeatureFlags({ await ph.getFeatureFlags({

View file

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

View file

@ -4,7 +4,7 @@ import { Container } from 'typedi';
import type WebSocket from 'ws'; import type WebSocket from 'ws';
import type { User } from '@/databases/entities/user'; import type { User } from '@/databases/entities/user';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { WebSocketPush } from '@/push/websocket.push'; import { WebSocketPush } from '@/push/websocket.push';
import { mockInstance } from '@test/mocking'; 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 { assert, jsonStringify } from 'n8n-workflow';
import type { User } from '@/databases/entities/user'; 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 type { OnPushMessage } from '@/push/types';
import { TypedEmitter } from '@/typed-emitter'; import { TypedEmitter } from '@/typed-emitter';

View file

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

View file

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

View file

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

View file

@ -8,7 +8,7 @@ import { Service } from 'typedi';
import config from '@/config'; import config from '@/config';
import { ExecutionRepository } from '@/databases/repositories/execution.repository'; import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types'; import { NodeTypes } from '@/node-types';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data'; 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 { Service } from 'typedi';
import config from '@/config'; import config from '@/config';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { RedisClientService } from '@/services/redis-client.service'; import { RedisClientService } from '@/services/redis-client.service';
import type { PubSub } from './pubsub.types'; 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 { Service } from 'typedi';
import config from '@/config'; import config from '@/config';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { RedisClientService } from '@/services/redis-client.service'; import { RedisClientService } from '@/services/redis-client.service';
import type { PubSub } from './pubsub.types'; 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 { OnShutdown } from '@/decorators/on-shutdown';
import { MaxStalledCountError } from '@/errors/max-stalled-count.error'; import { MaxStalledCountError } from '@/errors/max-stalled-count.error';
import { EventService } from '@/events/event.service'; import { EventService } from '@/events/event.service';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { OrchestrationService } from '@/services/orchestration.service'; import { OrchestrationService } from '@/services/orchestration.service';
import { JOB_TYPE_NAME, QUEUE_NAME } from './constants'; 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 { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error';
import { ExternalHooks } from '@/external-hooks'; import { ExternalHooks } from '@/external-hooks';
import type { ICredentialsOverwrite } from '@/interfaces'; import type { ICredentialsOverwrite } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { PrometheusMetricsService } from '@/metrics/prometheus-metrics.service'; import { PrometheusMetricsService } from '@/metrics/prometheus-metrics.service';
import { rawBodyReader, bodyParser } from '@/middlewares'; import { rawBodyReader, bodyParser } from '@/middlewares';
import * as ResponseHelper from '@/response-helper'; import * as ResponseHelper from '@/response-helper';

View file

@ -6,7 +6,7 @@ import { Service } from 'typedi';
import config from '@/config'; import config from '@/config';
import { getN8nPackageJson, inDevelopment } from '@/constants'; import { getN8nPackageJson, inDevelopment } from '@/constants';
import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { isApiEnabled } from '@/public-api'; import { isApiEnabled } from '@/public-api';
import { import {
ENV_VARS_DOCS_URL, 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 { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
@Service() @Service()
export class ActiveWorkflowsService { export class ActiveWorkflowsService {

View file

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

View file

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

View file

@ -35,7 +35,7 @@ import { Service } from 'typedi';
import { CredentialTypes } from '@/credential-types'; import { CredentialTypes } from '@/credential-types';
import type { User } from '@/databases/entities/user'; import type { User } from '@/databases/entities/user';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { NodeTypes } from '@/node-types'; import { NodeTypes } from '@/node-types';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data'; 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 { getLdapLoginLabel } from '@/ldap/helpers.ee';
import { License } from '@/license'; import { License } from '@/license';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { isApiEnabled } from '@/public-api'; import { isApiEnabled } from '@/public-api';
import type { CommunityPackagesService } from '@/services/community-packages.service'; import type { CommunityPackagesService } from '@/services/community-packages.service';
import { getSamlLoginLabel } from '@/sso/saml/saml-helpers'; import { getSamlLoginLabel } from '@/sso/saml/saml-helpers';
@ -123,7 +123,7 @@ export class FrontendService {
apiKey: config.getEnv('diagnostics.config.posthog.apiKey'), apiKey: config.getEnv('diagnostics.config.posthog.apiKey'),
autocapture: false, autocapture: false,
disableSessionRecording: config.getEnv('deployment.type') !== 'cloud', disableSessionRecording: config.getEnv('deployment.type') !== 'cloud',
debug: config.getEnv('logs.level') === 'debug', debug: this.globalConfig.logging.level === 'debug',
}, },
personalizationSurveyEnabled: personalizationSurveyEnabled:
config.getEnv('personalization.enabled') && config.getEnv('diagnostics.enabled'), config.getEnv('personalization.enabled') && config.getEnv('diagnostics.enabled'),
@ -153,7 +153,7 @@ export class FrontendService {
}, },
}, },
workflowTagsDisabled: config.getEnv('workflowTagsDisabled'), workflowTagsDisabled: config.getEnv('workflowTagsDisabled'),
logLevel: config.getEnv('logs.level'), logLevel: this.globalConfig.logging.level,
hiringBannerEnabled: config.getEnv('hiringBanner.enabled'), hiringBannerEnabled: config.getEnv('hiringBanner.enabled'),
aiAssistant: { aiAssistant: {
enabled: false, enabled: false,

View file

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

View file

@ -4,7 +4,7 @@ import Container, { Service } from 'typedi';
import config from '@/config'; import config from '@/config';
import type { PubSubCommandMap } from '@/events/maps/pub-sub.event-map'; 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 { Publisher } from '@/scaling/pubsub/publisher.service';
import type { Subscriber } from '@/scaling/pubsub/subscriber.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 os from 'node:os';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { COMMAND_PUBSUB_CHANNEL } from '@/scaling/constants'; import { COMMAND_PUBSUB_CHANNEL } from '@/scaling/constants';
import type { PubSub } from '@/scaling/pubsub/pubsub.types'; 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 { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import { ExternalSecretsManager } from '@/external-secrets/external-secrets-manager.ee'; import { ExternalSecretsManager } from '@/external-secrets/external-secrets-manager.ee';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { Push } from '@/push'; import { Push } from '@/push';
import { CommunityPackagesService } from '@/services/community-packages.service'; import { CommunityPackagesService } from '@/services/community-packages.service';
import { OrchestrationService } from '@/services/orchestration.service'; import { OrchestrationService } from '@/services/orchestration.service';

View file

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

View file

@ -4,7 +4,7 @@ import { Service } from 'typedi';
import config from '@/config'; import config from '@/config';
import { TIME } from '@/constants'; import { TIME } from '@/constants';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { Publisher } from '@/scaling/pubsub/publisher.service'; import { Publisher } from '@/scaling/pubsub/publisher.service';
import { RedisClientService } from '@/services/redis-client.service'; import { RedisClientService } from '@/services/redis-client.service';
import { TypedEmitter } from '@/typed-emitter'; 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 { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import { ExternalSecretsManager } from '@/external-secrets/external-secrets-manager.ee'; import { ExternalSecretsManager } from '@/external-secrets/external-secrets-manager.ee';
import { License } from '@/license'; import { License } from '@/license';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { COMMAND_PUBSUB_CHANNEL } from '@/scaling/constants'; import { COMMAND_PUBSUB_CHANNEL } from '@/scaling/constants';
import type { PubSub } from '@/scaling/pubsub/pubsub.types'; import type { PubSub } from '@/scaling/pubsub/pubsub.types';
import { CommunityPackagesService } from '@/services/community-packages.service'; import { CommunityPackagesService } from '@/services/community-packages.service';

View file

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

View file

@ -4,7 +4,7 @@ import type { Cluster, RedisOptions } from 'ioredis';
import { Service } from 'typedi'; import { Service } from 'typedi';
import { Debounce } from '@/decorators/debounce'; import { Debounce } from '@/decorators/debounce';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { TypedEmitter } from '@/typed-emitter'; import { TypedEmitter } from '@/typed-emitter';
import type { RedisClientType } from '../scaling/redis/redis.types'; 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 { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { EventService } from '@/events/event.service'; import { EventService } from '@/events/event.service';
import type { Invitation, PublicUser } from '@/interfaces'; import type { Invitation, PublicUser } from '@/interfaces';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import type { PostHogClient } from '@/posthog'; import type { PostHogClient } from '@/posthog';
import type { UserRequest } from '@/requests'; import type { UserRequest } from '@/requests';
import { UrlService } from '@/services/url.service'; import { UrlService } from '@/services/url.service';

View file

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

View file

@ -3,7 +3,7 @@ import { ApplicationError, ErrorReporterProxy, assert } from 'n8n-workflow';
import { Container, Service } from 'typedi'; import { Container, Service } from 'typedi';
import { LOWEST_SHUTDOWN_PRIORITY, HIGHEST_SHUTDOWN_PRIORITY } from '@/constants'; import { LOWEST_SHUTDOWN_PRIORITY, HIGHEST_SHUTDOWN_PRIORITY } from '@/constants';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
type HandlerFn = () => Promise<void> | void; type HandlerFn = () => Promise<void> | void;
export type ServiceClass = Class<Record<string, HandlerFn>>; 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 { mock } from 'jest-mock-extended';
import type { IdentityProviderInstance, ServiceProviderInstance } from 'samlify'; import type { IdentityProviderInstance, ServiceProviderInstance } from 'samlify';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
import { UrlService } from '@/services/url.service'; import { UrlService } from '@/services/url.service';
import * as samlHelpers from '@/sso/saml/saml-helpers'; import * as samlHelpers from '@/sso/saml/saml-helpers';
import { SamlService } from '@/sso/saml/saml.service.ee'; import { SamlService } from '@/sso/saml/saml.service.ee';

View file

@ -1,7 +1,7 @@
import { Container } from 'typedi'; import { Container } from 'typedi';
import type { XMLFileInfo } from 'xmllint-wasm'; import type { XMLFileInfo } from 'xmllint-wasm';
import { Logger } from '@/logger'; import { Logger } from '@/logging/logger.service';
let xml: XMLFileInfo; let xml: XMLFileInfo;
let xmldsigCore: 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 { UserRepository } from '@/databases/repositories/user.repository';
import { AuthError } from '@/errors/response-errors/auth.error'; import { AuthError } from '@/errors/response-errors/auth.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.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 { UrlService } from '@/services/url.service';
import { SAML_PREFERENCES_DB_KEY } from './constants'; 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 type { Project } from '@/databases/entities/project';
import { SubworkflowPolicyDenialError } from '@/errors/subworkflow-policy-denial.error'; 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 { AccessService } from '@/services/access.service';
import { OwnershipService } from '@/services/ownership.service'; import { OwnershipService } from '@/services/ownership.service';
import { UrlService } from '@/services/url.service'; import { UrlService } from '@/services/url.service';

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