mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 04:47:29 -08:00
refactor(core): Support multiple log scopes (#11318)
This commit is contained in:
parent
c863abd083
commit
43f31b86aa
|
@ -1,14 +1,16 @@
|
|||
import { Config, Env, Nested } from '../decorators';
|
||||
import { StringArray } from '../utils';
|
||||
|
||||
/**
|
||||
* Scopes (areas of functionality) to filter logs by.
|
||||
*
|
||||
* `executions` -> execution lifecycle
|
||||
* `license` -> license SDK
|
||||
* `scaling` -> scaling mode
|
||||
*/
|
||||
export const LOG_SCOPES = ['executions', 'license', 'scaling'] as const;
|
||||
/** Scopes (areas of functionality) to filter logs by. */
|
||||
export const LOG_SCOPES = [
|
||||
'concurrency',
|
||||
'license',
|
||||
'multi-main-setup',
|
||||
'pubsub',
|
||||
'redis',
|
||||
'scaling',
|
||||
'waiting-executions',
|
||||
] as const;
|
||||
|
||||
export type LogScope = (typeof LOG_SCOPES)[number];
|
||||
|
||||
|
@ -59,14 +61,19 @@ export class LoggingConfig {
|
|||
/**
|
||||
* Scopes to filter logs by. Nothing is filtered by default.
|
||||
*
|
||||
* Currently supported log scopes:
|
||||
* - `executions`
|
||||
* Supported log scopes:
|
||||
*
|
||||
* - `concurrency`
|
||||
* - `license`
|
||||
* - `multi-main-setup`
|
||||
* - `pubsub`
|
||||
* - `redis`
|
||||
* - `scaling`
|
||||
* - `waiting-executions`
|
||||
*
|
||||
* @example
|
||||
* `N8N_LOG_SCOPES=license`
|
||||
* `N8N_LOG_SCOPES=license,executions`
|
||||
* `N8N_LOG_SCOPES=license,waiting-executions`
|
||||
*/
|
||||
@Env('N8N_LOG_SCOPES')
|
||||
scopes: StringArray<LogScope> = [];
|
||||
|
|
|
@ -170,7 +170,7 @@ export class Start extends BaseCommand {
|
|||
|
||||
this.logger.info('Initializing n8n process');
|
||||
if (config.getEnv('executions.mode') === 'queue') {
|
||||
const scopedLogger = this.logger.withScope('scaling');
|
||||
const scopedLogger = this.logger.scoped('scaling');
|
||||
scopedLogger.debug('Starting main instance in scaling mode');
|
||||
scopedLogger.debug(`Host ID: ${this.instanceSettings.hostId}`);
|
||||
}
|
||||
|
@ -263,7 +263,7 @@ export class Start extends BaseCommand {
|
|||
await subscriber.subscribe('n8n.commands');
|
||||
await subscriber.subscribe('n8n.worker-response');
|
||||
|
||||
this.logger.withScope('scaling').debug('Pubsub setup completed');
|
||||
this.logger.scoped(['scaling', 'pubsub']).debug('Pubsub setup completed');
|
||||
|
||||
if (!orchestrationService.isMultiMainSetupEnabled) return;
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ export class Worker extends BaseCommand {
|
|||
|
||||
super(argv, cmdConfig);
|
||||
|
||||
this.logger = Container.get(Logger).withScope('scaling');
|
||||
this.logger = Container.get(Logger).scoped('scaling');
|
||||
}
|
||||
|
||||
async init() {
|
||||
|
@ -151,7 +151,7 @@ export class Worker extends BaseCommand {
|
|||
Container.get(PubSubHandler).init();
|
||||
await Container.get(Subscriber).subscribe('n8n.commands');
|
||||
|
||||
this.logger.withScope('scaling').debug('Pubsub setup ready');
|
||||
this.logger.scoped(['scaling', 'pubsub']).debug('Pubsub setup completed');
|
||||
}
|
||||
|
||||
async setConcurrency() {
|
||||
|
|
|
@ -33,7 +33,7 @@ export class ConcurrencyControlService {
|
|||
private readonly telemetry: Telemetry,
|
||||
private readonly eventService: EventService,
|
||||
) {
|
||||
this.logger = this.logger.withScope('executions');
|
||||
this.logger = this.logger.scoped('concurrency');
|
||||
|
||||
this.productionLimit = config.getEnv('executions.concurrency.productionLimit');
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ export class License {
|
|||
private readonly licenseMetricsService: LicenseMetricsService,
|
||||
private readonly globalConfig: GlobalConfig,
|
||||
) {
|
||||
this.logger = this.logger.withScope('license');
|
||||
this.logger = this.logger.scoped('license');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -58,9 +58,11 @@ export class Logger {
|
|||
this.internalLogger = internalLogger;
|
||||
}
|
||||
|
||||
withScope(scope: LogScope) {
|
||||
/** Create a logger that injects the given scopes into its log metadata. */
|
||||
scoped(scopes: LogScope | LogScope[]) {
|
||||
scopes = Array.isArray(scopes) ? scopes : [scopes];
|
||||
const scopedLogger = new Logger(this.globalConfig, this.instanceSettings);
|
||||
const childLogger = this.internalLogger.child({ scope });
|
||||
const childLogger = this.internalLogger.child({ scopes });
|
||||
|
||||
scopedLogger.setInternalLogger(childLogger);
|
||||
|
||||
|
@ -106,11 +108,14 @@ export class Logger {
|
|||
|
||||
private scopeFilter() {
|
||||
return winston.format((info: TransformableInfo & { metadata: LogMetadata }) => {
|
||||
const shouldIncludeScope = info.metadata.scope && this.scopes.has(info.metadata.scope);
|
||||
if (!this.isScopingEnabled) return info;
|
||||
|
||||
if (this.isScopingEnabled && !shouldIncludeScope) return false;
|
||||
const { scopes } = info.metadata;
|
||||
|
||||
return info;
|
||||
const shouldIncludeScope =
|
||||
scopes && scopes?.length > 0 && scopes.some((s) => this.scopes.has(s));
|
||||
|
||||
return shouldIncludeScope ? info : false;
|
||||
})();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ export type LogLevel = (typeof LOG_LEVELS)[number];
|
|||
|
||||
export type LogMetadata = {
|
||||
[key: string]: unknown;
|
||||
scope?: LogScope;
|
||||
scopes?: LogScope[];
|
||||
file?: string;
|
||||
function?: string;
|
||||
};
|
||||
|
|
|
@ -40,7 +40,7 @@ export class JobProcessor {
|
|||
private readonly nodeTypes: NodeTypes,
|
||||
private readonly instanceSettings: InstanceSettings,
|
||||
) {
|
||||
this.logger = this.logger.withScope('scaling');
|
||||
this.logger = this.logger.scoped('scaling');
|
||||
}
|
||||
|
||||
async processJob(job: Job): Promise<JobResult> {
|
||||
|
|
|
@ -36,7 +36,7 @@ export class MultiMainSetup extends TypedEmitter<MultiMainEvents> {
|
|||
private readonly globalConfig: GlobalConfig,
|
||||
) {
|
||||
super();
|
||||
this.logger = this.logger.withScope('scaling');
|
||||
this.logger = this.logger.scoped(['scaling', 'multi-main-setup']);
|
||||
}
|
||||
|
||||
private leaderKey: string;
|
||||
|
|
|
@ -26,7 +26,7 @@ export class Publisher {
|
|||
// @TODO: Once this class is only ever initialized in scaling mode, throw in the next line instead.
|
||||
if (config.getEnv('executions.mode') !== 'queue') return;
|
||||
|
||||
this.logger = this.logger.withScope('scaling');
|
||||
this.logger = this.logger.scoped(['scaling', 'pubsub']);
|
||||
|
||||
this.client = this.redisClientService.createClient({ type: 'publisher(n8n)' });
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export class Subscriber {
|
|||
// @TODO: Once this class is only ever initialized in scaling mode, throw in the next line instead.
|
||||
if (config.getEnv('executions.mode') !== 'queue') return;
|
||||
|
||||
this.logger = this.logger.withScope('scaling');
|
||||
this.logger = this.logger.scoped(['scaling', 'pubsub']);
|
||||
|
||||
this.client = this.redisClientService.createClient({ type: 'subscriber(n8n)' });
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ export class ScalingService {
|
|||
private readonly orchestrationService: OrchestrationService,
|
||||
private readonly eventService: EventService,
|
||||
) {
|
||||
this.logger = this.logger.withScope('scaling');
|
||||
this.logger = this.logger.scoped('scaling');
|
||||
}
|
||||
|
||||
// #region Lifecycle
|
||||
|
|
|
@ -58,7 +58,7 @@ export class WorkerServer {
|
|||
) {
|
||||
assert(this.instanceSettings.instanceType === 'worker');
|
||||
|
||||
this.logger = this.logger.withScope('scaling');
|
||||
this.logger = this.logger.scoped('scaling');
|
||||
|
||||
this.app = express();
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ export class RedisClientService extends TypedEmitter<RedisEventMap> {
|
|||
private readonly globalConfig: GlobalConfig,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.logger = this.logger.scoped(['redis', 'scaling']);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
|
@ -99,9 +102,11 @@ export class RedisClientService extends TypedEmitter<RedisEventMap> {
|
|||
options.host = host;
|
||||
options.port = port;
|
||||
|
||||
this.logger.debug('[Redis] Initializing regular client', { type, host, port });
|
||||
const client = new ioRedis(options);
|
||||
|
||||
return new ioRedis(options);
|
||||
this.logger.debug(`Started Redis client ${type}`, { type, host, port });
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private createClusterClient({
|
||||
|
@ -115,12 +120,14 @@ export class RedisClientService extends TypedEmitter<RedisEventMap> {
|
|||
|
||||
const clusterNodes = this.clusterNodes();
|
||||
|
||||
this.logger.debug('[Redis] Initializing cluster client', { type, clusterNodes });
|
||||
|
||||
return new ioRedis.Cluster(clusterNodes, {
|
||||
const clusterClient = new ioRedis.Cluster(clusterNodes, {
|
||||
redisOptions: options,
|
||||
clusterRetryStrategy: this.retryStrategy(),
|
||||
});
|
||||
|
||||
this.logger.debug(`Started Redis cluster client ${type}`, { type, clusterNodes });
|
||||
|
||||
return clusterClient;
|
||||
}
|
||||
|
||||
private getOptions({ extraOptions }: { extraOptions?: RedisOptions }) {
|
||||
|
|
|
@ -31,7 +31,7 @@ export class WaitTracker {
|
|||
private readonly orchestrationService: OrchestrationService,
|
||||
private readonly instanceSettings: InstanceSettings,
|
||||
) {
|
||||
this.logger = this.logger.withScope('executions');
|
||||
this.logger = this.logger.scoped('waiting-executions');
|
||||
}
|
||||
|
||||
has(executionId: string) {
|
||||
|
|
|
@ -25,5 +25,4 @@ export const mockEntityManager = (entityClass: Class) => {
|
|||
return entityManager;
|
||||
};
|
||||
|
||||
export const mockLogger = () =>
|
||||
mock<Logger>({ withScope: jest.fn().mockReturnValue(mock<Logger>()) });
|
||||
export const mockLogger = () => mock<Logger>({ scoped: jest.fn().mockReturnValue(mock<Logger>()) });
|
||||
|
|
Loading…
Reference in a new issue