feature: add level-based logger methods for convenience

This commit is contained in:
Ruben Taelman 2020-09-23 09:11:44 +02:00 committed by Ruben Taelman
parent 6212c15352
commit db9aefe551
8 changed files with 114 additions and 12 deletions

View File

@ -52,7 +52,7 @@ export const runCustom = function(
}) as Setup; }) as Setup;
return await setup.setup(); return await setup.setup();
})().then((base: string): void => { })().then((base: string): void => {
logger.log('info', `Running at ${base}`); logger.info(`Running at ${base}`);
}).catch((error): void => { }).catch((error): void => {
// This is the only time we can *not* use the logger to print error messages, as dependency injection has failed. // This is the only time we can *not* use the logger to print error messages, as dependency injection has failed.
stderr.write(`${error}\n`); stderr.write(`${error}\n`);

View File

@ -71,7 +71,7 @@ export class Setup {
}, },
); );
}; };
this.logger.log('debug', 'Setup default ACL settings'); this.logger.debug('Setup default ACL settings');
await aclSetup(); await aclSetup();
this.httpServer.listen(this.port); this.httpServer.listen(this.port);

View File

@ -1,5 +1,5 @@
import type { LazyLoggerFactory } from './LazyLoggerFactory'; import type { LazyLoggerFactory } from './LazyLoggerFactory';
import type { Logger } from './Logger'; import { Logger } from './Logger';
import type { LogLevel } from './LogLevel'; import type { LogLevel } from './LogLevel';
/** /**
@ -8,13 +8,14 @@ import type { LogLevel } from './LogLevel';
* An error will be thrown if {@link LazyLogger.log} is invoked * An error will be thrown if {@link LazyLogger.log} is invoked
* before a {@link LoggerFactory} is set in {@link LazyLoggerFactory}. * before a {@link LoggerFactory} is set in {@link LazyLoggerFactory}.
*/ */
export class LazyLogger implements Logger { export class LazyLogger extends Logger {
private readonly lazyLoggerFactory: LazyLoggerFactory; private readonly lazyLoggerFactory: LazyLoggerFactory;
private readonly label: string; private readonly label: string;
private logger: Logger | undefined; private logger: Logger | undefined;
public constructor(lazyLoggerFactory: LazyLoggerFactory, label: string) { public constructor(lazyLoggerFactory: LazyLoggerFactory, label: string) {
super();
this.lazyLoggerFactory = lazyLoggerFactory; this.lazyLoggerFactory = lazyLoggerFactory;
this.label = label; this.label = label;
} }

View File

@ -5,7 +5,7 @@ import type { LogLevel } from './LogLevel';
* *
* @see getLoggerFor on how to instantiate loggers. * @see getLoggerFor on how to instantiate loggers.
*/ */
export interface Logger { export abstract class Logger {
/** /**
* Log the given message at the given level. * Log the given message at the given level.
* If the internal level is higher than the given level, the message may be voided. * If the internal level is higher than the given level, the message may be voided.
@ -13,5 +13,59 @@ export interface Logger {
* @param message - The message to log. * @param message - The message to log.
* @param meta - Optional metadata to include in the log message. * @param meta - Optional metadata to include in the log message.
*/ */
log: (level: LogLevel, message: string, meta?: any) => Logger; public abstract log(level: LogLevel, message: string, meta?: any): Logger;
/**
* Log a message at the 'error' level.
* @param message - The message to log.
* @param meta - Optional metadata to include in the log message.
*/
public error(message: string, meta?: any): Logger {
return this.log('error', message, meta);
}
/**
* Log a message at the 'warn' level.
* @param message - The message to log.
* @param meta - Optional metadata to include in the log message.
*/
public warn(message: string, meta?: any): Logger {
return this.log('warn', message, meta);
}
/**
* Log a message at the 'info' level.
* @param message - The message to log.
* @param meta - Optional metadata to include in the log message.
*/
public info(message: string, meta?: any): Logger {
return this.log('info', message, meta);
}
/**
* Log a message at the 'verbose' level.
* @param message - The message to log.
* @param meta - Optional metadata to include in the log message.
*/
public verbose(message: string, meta?: any): Logger {
return this.log('verbose', message, meta);
}
/**
* Log a message at the 'debug' level.
* @param message - The message to log.
* @param meta - Optional metadata to include in the log message.
*/
public debug(message: string, meta?: any): Logger {
return this.log('debug', message, meta);
}
/**
* Log a message at the 'silly' level.
* @param message - The message to log.
* @param meta - Optional metadata to include in the log message.
*/
public silly(message: string, meta?: any): Logger {
return this.log('silly', message, meta);
}
} }

View File

@ -1,10 +1,10 @@
import type { Logger } from './Logger'; import { Logger } from './Logger';
import type { LogLevel } from './LogLevel'; import type { LogLevel } from './LogLevel';
/** /**
* A logger that does nothing on a log message. * A logger that does nothing on a log message.
*/ */
export class VoidLogger implements Logger { export class VoidLogger extends Logger {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
public log(level: LogLevel, message: string, meta?: any): Logger { public log(level: LogLevel, message: string, meta?: any): Logger {
// Do nothing // Do nothing

View File

@ -1,14 +1,15 @@
import type { Logger as WinstonInnerLogger } from 'winston'; import type { Logger as WinstonInnerLogger } from 'winston';
import type { Logger } from './Logger'; import { Logger } from './Logger';
import type { LogLevel } from './LogLevel'; import type { LogLevel } from './LogLevel';
/** /**
* A WinstonLogger implements the {@link Logger} interface using a given winston logger. * A WinstonLogger implements the {@link Logger} interface using a given winston logger.
*/ */
export class WinstonLogger implements Logger { export class WinstonLogger extends Logger {
private readonly logger: WinstonInnerLogger; private readonly logger: WinstonInnerLogger;
public constructor(logger: WinstonInnerLogger) { public constructor(logger: WinstonInnerLogger) {
super();
this.logger = logger; this.logger = logger;
} }

View File

@ -38,11 +38,11 @@ export class ExpressHttpServer {
// Delegate to the main handler // Delegate to the main handler
app.use(async(request, response, done): Promise<void> => { app.use(async(request, response, done): Promise<void> => {
try { try {
this.logger.log('info', `Received request for ${request.url}`); this.logger.info(`Received request for ${request.url}`);
await this.handler.handleSafe({ request, response }); await this.handler.handleSafe({ request, response });
} catch (error: unknown) { } catch (error: unknown) {
const errMsg = error instanceof Error ? `${error.name}: ${error.message}\n${error.stack}` : 'Unknown error.'; const errMsg = error instanceof Error ? `${error.name}: ${error.message}\n${error.stack}` : 'Unknown error.';
this.logger.log('error', errMsg); this.logger.error(errMsg);
response.status(500).contentType('text/plain').send(errMsg); response.status(500).contentType('text/plain').send(errMsg);
} finally { } finally {
done(); done();

View File

@ -0,0 +1,46 @@
import { Logger } from '../../../src/logging/Logger';
describe('Logger', (): void => {
let logger: Logger;
let meta: any;
beforeEach(async(): Promise<void> => {
logger = new (Logger as any)();
logger.log = jest.fn();
meta = { abc: 123 };
});
it('Error delegates to log.', async(): Promise<void> => {
logger.error('my message', meta);
expect(logger.log).toHaveBeenCalledTimes(1);
expect(logger.log).toHaveBeenCalledWith('error', 'my message', meta);
});
it('Warn delegates to log.', async(): Promise<void> => {
logger.warn('my message', meta);
expect(logger.log).toHaveBeenCalledTimes(1);
expect(logger.log).toHaveBeenCalledWith('warn', 'my message', meta);
});
it('Info delegates to log.', async(): Promise<void> => {
logger.info('my message', meta);
expect(logger.log).toHaveBeenCalledTimes(1);
expect(logger.log).toHaveBeenCalledWith('info', 'my message', meta);
});
it('Verbose delegates to log.', async(): Promise<void> => {
logger.verbose('my message', meta);
expect(logger.log).toHaveBeenCalledTimes(1);
expect(logger.log).toHaveBeenCalledWith('verbose', 'my message', meta);
});
it('Debug delegates to log.', async(): Promise<void> => {
logger.debug('my message', meta);
expect(logger.log).toHaveBeenCalledTimes(1);
expect(logger.log).toHaveBeenCalledWith('debug', 'my message', meta);
});
it('Silly delegates to log.', async(): Promise<void> => {
logger.silly('my message', meta);
expect(logger.log).toHaveBeenCalledTimes(1);
expect(logger.log).toHaveBeenCalledWith('silly', 'my message', meta);
});
});