mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: add lazy logger wrapper
This commit is contained in:
parent
5b825bc2d4
commit
09ac83caa5
29
src/logging/LazyLogger.ts
Normal file
29
src/logging/LazyLogger.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import type { LazyLoggerFactory } from './LazyLoggerFactory';
|
||||||
|
import type { Logger } from './Logger';
|
||||||
|
import type { LogLevel } from './LogLevel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a logger lazily using a reference to {@link LazyLoggerFactory}.
|
||||||
|
*
|
||||||
|
* An error will be thrown if {@link LazyLogger.log} is invoked
|
||||||
|
* before a {@link LoggerFactory} is set in {@link LazyLoggerFactory}.
|
||||||
|
*/
|
||||||
|
export class LazyLogger implements Logger {
|
||||||
|
private readonly lazyLoggerFactory: LazyLoggerFactory;
|
||||||
|
private readonly label: string;
|
||||||
|
|
||||||
|
private logger: Logger | undefined;
|
||||||
|
|
||||||
|
public constructor(lazyLoggerFactory: LazyLoggerFactory, label: string) {
|
||||||
|
this.lazyLoggerFactory = lazyLoggerFactory;
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public log(level: LogLevel, message: string, meta: any): Logger {
|
||||||
|
if (!this.logger) {
|
||||||
|
this.logger = this.lazyLoggerFactory.getLoggerFactoryOrThrow()
|
||||||
|
.createLogger(this.label);
|
||||||
|
}
|
||||||
|
return this.logger.log(level, message, meta);
|
||||||
|
}
|
||||||
|
}
|
41
src/logging/LazyLoggerFactory.ts
Normal file
41
src/logging/LazyLoggerFactory.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { LazyLogger } from './LazyLogger';
|
||||||
|
import type { Logger } from './Logger';
|
||||||
|
import type { LoggerFactory } from './LoggerFactory';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps over another {@link LoggerFactory} that can be set lazily.
|
||||||
|
* This is a singleton class, for which the instance can be retrieved using {@link LazyLoggerFactory.getInstance}.
|
||||||
|
*
|
||||||
|
* Loggers can safely be created before a {@link LoggerFactory} is set.
|
||||||
|
* But an error will be thrown if {@link Logger.log} is invoked before a {@link LoggerFactory} is set.
|
||||||
|
*
|
||||||
|
* This creates instances of {@link LazyLogger}.
|
||||||
|
*/
|
||||||
|
export class LazyLoggerFactory implements LoggerFactory {
|
||||||
|
private static readonly instance = new LazyLoggerFactory();
|
||||||
|
|
||||||
|
private loggerFactory: LoggerFactory | undefined;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
// Singleton instance
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): LazyLoggerFactory {
|
||||||
|
return LazyLoggerFactory.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createLogger(label: string): Logger {
|
||||||
|
return new LazyLogger(this, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLoggerFactory(loggerFactory: LoggerFactory | undefined): void {
|
||||||
|
this.loggerFactory = loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLoggerFactoryOrThrow(): LoggerFactory {
|
||||||
|
if (!this.loggerFactory) {
|
||||||
|
throw new Error('Illegal logging during initialization');
|
||||||
|
}
|
||||||
|
return this.loggerFactory;
|
||||||
|
}
|
||||||
|
}
|
53
test/unit/logging/LazyLogger.test.ts
Normal file
53
test/unit/logging/LazyLogger.test.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { LazyLogger } from '../../../src/logging/LazyLogger';
|
||||||
|
import { LazyLoggerFactory } from '../../../src/logging/LazyLoggerFactory';
|
||||||
|
|
||||||
|
describe('LazyLogger', (): void => {
|
||||||
|
let lazyLoggerFactory: LazyLoggerFactory;
|
||||||
|
let logger: LazyLogger;
|
||||||
|
beforeEach(async(): Promise<void> => {
|
||||||
|
lazyLoggerFactory = LazyLoggerFactory.getInstance();
|
||||||
|
lazyLoggerFactory.setLoggerFactory(undefined);
|
||||||
|
logger = new LazyLogger(lazyLoggerFactory, 'MyLabel');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when no logger factory is set in the lazy logger factory.', async(): Promise<void> => {
|
||||||
|
expect((): any => logger.log('debug', 'my message', { abc: true }))
|
||||||
|
.toThrow(new Error('Illegal logging during initialization'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a new logger using the factory.', async(): Promise<void> => {
|
||||||
|
const dummyLogger: any = {
|
||||||
|
log: jest.fn((): any => dummyLogger),
|
||||||
|
};
|
||||||
|
const dummyLoggerFactory: any = {
|
||||||
|
createLogger: jest.fn((): any => dummyLogger),
|
||||||
|
};
|
||||||
|
lazyLoggerFactory.setLoggerFactory(dummyLoggerFactory);
|
||||||
|
|
||||||
|
expect(logger.log('debug', 'my message', { abc: true })).toBe(dummyLogger);
|
||||||
|
expect(dummyLoggerFactory.createLogger).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dummyLoggerFactory.createLogger).toHaveBeenCalledWith('MyLabel');
|
||||||
|
expect(dummyLogger.log).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dummyLogger.log).toHaveBeenCalledWith('debug', 'my message', { abc: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reuses the logger for repeated calls.', async(): Promise<void> => {
|
||||||
|
const dummyLogger: any = {
|
||||||
|
log: jest.fn((): any => dummyLogger),
|
||||||
|
};
|
||||||
|
const dummyLoggerFactory: any = {
|
||||||
|
createLogger: jest.fn((): any => dummyLogger),
|
||||||
|
};
|
||||||
|
lazyLoggerFactory.setLoggerFactory(dummyLoggerFactory);
|
||||||
|
|
||||||
|
expect(logger.log('debug', 'my message 1', { abc: true })).toBe(dummyLogger);
|
||||||
|
expect(logger.log('debug', 'my message 2', { abc: true })).toBe(dummyLogger);
|
||||||
|
expect(logger.log('debug', 'my message 3', { abc: true })).toBe(dummyLogger);
|
||||||
|
expect(dummyLoggerFactory.createLogger).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dummyLoggerFactory.createLogger).toHaveBeenCalledWith('MyLabel');
|
||||||
|
expect(dummyLogger.log).toHaveBeenCalledTimes(3);
|
||||||
|
expect(dummyLogger.log).toHaveBeenNthCalledWith(1, 'debug', 'my message 1', { abc: true });
|
||||||
|
expect(dummyLogger.log).toHaveBeenNthCalledWith(2, 'debug', 'my message 2', { abc: true });
|
||||||
|
expect(dummyLogger.log).toHaveBeenNthCalledWith(3, 'debug', 'my message 3', { abc: true });
|
||||||
|
});
|
||||||
|
});
|
65
test/unit/logging/LazyLoggerFactory.test.ts
Normal file
65
test/unit/logging/LazyLoggerFactory.test.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { LazyLogger } from '../../../src/logging/LazyLogger';
|
||||||
|
import { LazyLoggerFactory } from '../../../src/logging/LazyLoggerFactory';
|
||||||
|
|
||||||
|
describe('LazyLoggerFactory', (): void => {
|
||||||
|
let dummyLogger: any;
|
||||||
|
let dummyLoggerFactory: any;
|
||||||
|
beforeEach(async(): Promise<void> => {
|
||||||
|
LazyLoggerFactory.getInstance().setLoggerFactory(undefined);
|
||||||
|
dummyLogger = {
|
||||||
|
log: jest.fn((): any => dummyLogger),
|
||||||
|
};
|
||||||
|
dummyLoggerFactory = {
|
||||||
|
createLogger: jest.fn((): any => dummyLogger),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is a singleton.', async(): Promise<void> => {
|
||||||
|
expect(LazyLoggerFactory.getInstance()).toBeInstanceOf(LazyLoggerFactory);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows LazyLoggers to be created before an inner factory was set.', async(): Promise<void> => {
|
||||||
|
const logger = LazyLoggerFactory.getInstance().createLogger('MyLabel');
|
||||||
|
expect(logger).toBeInstanceOf(LazyLogger);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows LazyLoggers to be created after an inner factory was set.', async(): Promise<void> => {
|
||||||
|
LazyLoggerFactory.getInstance().setLoggerFactory(dummyLoggerFactory);
|
||||||
|
const logger = LazyLoggerFactory.getInstance().createLogger('MyLabel');
|
||||||
|
expect(logger).toBeInstanceOf(LazyLogger);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when retrieving the inner factory if none has been set.', async(): Promise<void> => {
|
||||||
|
expect((): any => LazyLoggerFactory.getInstance().getLoggerFactoryOrThrow())
|
||||||
|
.toThrow(new Error('Illegal logging during initialization'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Returns the inner factory if one has been set.', async(): Promise<void> => {
|
||||||
|
LazyLoggerFactory.getInstance().setLoggerFactory(dummyLoggerFactory);
|
||||||
|
expect(LazyLoggerFactory.getInstance().getLoggerFactoryOrThrow()).toBe(dummyLoggerFactory);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows LazyLoggers to be invoked if a factory has been set beforehand.', async(): Promise<void> => {
|
||||||
|
LazyLoggerFactory.getInstance().setLoggerFactory(dummyLoggerFactory);
|
||||||
|
const logger = LazyLoggerFactory.getInstance().createLogger('MyLabel');
|
||||||
|
logger.log('debug', 'my message', { abc: true });
|
||||||
|
|
||||||
|
expect(dummyLogger.log).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dummyLogger.log).toHaveBeenCalledWith('debug', 'my message', { abc: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows LazyLoggers to be invoked if a factory has been after lazy logger creation.', async(): Promise<void> => {
|
||||||
|
const logger = LazyLoggerFactory.getInstance().createLogger('MyLabel');
|
||||||
|
LazyLoggerFactory.getInstance().setLoggerFactory(dummyLoggerFactory);
|
||||||
|
logger.log('debug', 'my message', { abc: true });
|
||||||
|
|
||||||
|
expect(dummyLogger.log).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dummyLogger.log).toHaveBeenCalledWith('debug', 'my message', { abc: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors on invoking LazyLoggers if a factory has not been set yet.', async(): Promise<void> => {
|
||||||
|
const logger = LazyLoggerFactory.getInstance().createLogger('MyLabel');
|
||||||
|
expect((): any => logger.log('debug', 'my message', { abc: true }))
|
||||||
|
.toThrow(new Error('Illegal logging during initialization'));
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user