chore(core): Better debug logs for local dev (#11096)

This commit is contained in:
Iván Ovejero 2024-10-07 16:57:06 +02:00 committed by GitHub
parent 19fb728da0
commit 4c7caf734c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 19 deletions

View file

@ -3,9 +3,11 @@ import callsites from 'callsites';
import { InstanceSettings } from 'n8n-core'; import { InstanceSettings } from 'n8n-core';
import { LoggerProxy, LOG_LEVELS } from 'n8n-workflow'; import { LoggerProxy, LOG_LEVELS } from 'n8n-workflow';
import path, { basename } from 'node:path'; import path, { basename } from 'node:path';
import pc from 'picocolors';
import { Service } from 'typedi'; import { Service } from 'typedi';
import winston from 'winston'; import winston from 'winston';
import { inDevelopment, inProduction } from '@/constants';
import { isObjectLiteral } from '@/utils'; import { isObjectLiteral } from '@/utils';
import { noOp } from './constants'; import { noOp } from './constants';
@ -61,8 +63,7 @@ export class Logger {
for (const logLevel of LOG_LEVELS) { for (const logLevel of LOG_LEVELS) {
if (levels[logLevel] > levels[this.level]) { if (levels[logLevel] > levels[this.level]) {
// winston defines `{ error: 0, warn: 1, info: 2, debug: 5 }` // numerically higher (less severe) log levels become no-op
// so numerically higher (less severe) log levels become no-op
// to prevent overhead from `callsites` calls // to prevent overhead from `callsites` calls
Object.defineProperty(this, logLevel, { value: noOp }); Object.defineProperty(this, logLevel, { value: noOp });
} }
@ -71,24 +72,60 @@ export class Logger {
private setConsoleTransport() { private setConsoleTransport() {
const format = const format =
this.level === 'debug' this.level === 'debug' && inDevelopment
? winston.format.combine( ? this.debugDevConsoleFormat()
winston.format.metadata(), : this.level === 'debug' && inProduction
winston.format.timestamp(), ? this.debugProdConsoleFormat()
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); : winston.format.printf(({ message }: { message: string }) => message);
this.internalLogger.add(new winston.transports.Console({ format })); this.internalLogger.add(new winston.transports.Console({ format }));
} }
private debugDevConsoleFormat() {
return winston.format.combine(
winston.format.metadata(),
winston.format.timestamp({ format: () => this.devTsFormat() }),
winston.format.colorize({ all: true }),
winston.format.printf(({ level: _level, message, timestamp, metadata: _metadata }) => {
const SEPARATOR = ' '.repeat(3);
const LOG_LEVEL_COLUMN_WIDTH = 15; // 5 columns + ANSI color codes
const level = _level.toLowerCase().padEnd(LOG_LEVEL_COLUMN_WIDTH, ' ');
const metadata = this.toPrintable(_metadata);
return [timestamp, level, message + ' ' + pc.dim(metadata)].join(SEPARATOR);
}),
);
}
private debugProdConsoleFormat() {
return winston.format.combine(
winston.format.metadata(),
winston.format.timestamp(),
winston.format.printf(({ level, message, timestamp, metadata }) => {
const _metadata = this.toPrintable(metadata);
return `${timestamp} | ${level.padEnd(5)} | ${message}${_metadata ? ' ' + _metadata : ''}`;
}),
);
}
private devTsFormat() {
const now = new Date();
const pad = (num: number, digits: number = 2) => num.toString().padStart(digits, '0');
const hours = pad(now.getHours());
const minutes = pad(now.getMinutes());
const seconds = pad(now.getSeconds());
const milliseconds = pad(now.getMilliseconds(), 3);
return `${hours}:${minutes}:${seconds}.${milliseconds}`;
}
private toPrintable(metadata: unknown) { private toPrintable(metadata: unknown) {
if (isObjectLiteral(metadata) && Object.keys(metadata).length > 0) { if (isObjectLiteral(metadata) && Object.keys(metadata).length > 0) {
return ' ' + JSON.stringify(metadata); return inProduction
? JSON.stringify(metadata)
: JSON.stringify(metadata)
.replace(/{"/g, '{ "')
.replace(/,"/g, ', "')
.replace(/:/g, ': ')
.replace(/}/g, ' }'); // spacing for readability
} }
return ''; return '';

View file

@ -5,17 +5,13 @@ interface ErrorReporter {
report: (error: Error | string, options?: ReportingOptions) => void; report: (error: Error | string, options?: ReportingOptions) => void;
} }
const { NODE_ENV } = process.env;
const inDevelopment = !NODE_ENV || NODE_ENV === 'development';
const instance: ErrorReporter = { const instance: ErrorReporter = {
report: (error) => { report: (error) => {
if (error instanceof Error) { if (error instanceof Error) {
let e = error; let e = error;
do { do {
const meta = e instanceof ApplicationError ? e.extra : undefined; const meta = e instanceof ApplicationError ? e.extra : undefined;
if (inDevelopment) console.log(e, meta); Logger.error(`${e.constructor.name}: ${e.message}`, meta);
else Logger.error(`${e.constructor.name}: ${e.message}`, meta);
e = e.cause as Error; e = e.cause as Error;
} while (e); } while (e);
} }