Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions packages/logger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,28 @@ setLogger(
);
```

Example for forwarding only some log levels
```typescript
import {
ConsoleLogger,
GrayLogLogger,
LEVEL,
LevelFilterLogger,
setLogger,
} from '@user-office-software/duo-logger';

// log everything to console, but send only WARN and above to Graylog
setLogger([
new ConsoleLogger(),
new LevelFilterLogger(
new GrayLogLogger('my-graylog-server.com', 12201, {}, []),
LEVEL.WARN
),
]);
```

Severity order is `DEBUG < INFO < WARN < ERROR < EXCEPTION < FATAL`.


## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Expand Down
2 changes: 1 addition & 1 deletion packages/logger/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@user-office-software/duo-logger",
"version": "2.3.2",
"version": "2.4.0",
"description": "Logger implementation",
"author": "SWAP",
"license": "ISC",
Expand Down
2 changes: 2 additions & 0 deletions packages/logger/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ export * from './loggers/Logger';
export * from './loggers/implementation/GrayLogLogger';
export * from './loggers/implementation/ConsoleLogger';
export * from './loggers/implementation/MultiLogger';
export * from './loggers/implementation/LevelFilterLogger';
export * from './loggers/implementation/WinstonLogger';
export * from './logger';
export * from './enum/Level';
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { LevelFilterLogger } from './LevelFilterLogger';
import { LEVEL } from '../../enum/Level';
import { Logger } from '../Logger';

describe('LevelFilterLogger', () => {
let inner: jest.Mocked<Logger>;

beforeEach(() => {
inner = {
logInfo: jest.fn(),
logWarn: jest.fn(),
logDebug: jest.fn(),
logError: jest.fn(),
logException: jest.fn(),
};
});

const logAllLevels = (logger: Logger) => {
logger.logDebug('debug', {});
logger.logInfo('info', {});
logger.logWarn('warn', {});
logger.logError('error', {});
logger.logException('exception', new Error('boom'), {});
};

test('forwards all messages when min level is DEBUG', () => {
logAllLevels(new LevelFilterLogger(inner, LEVEL.DEBUG));

expect(inner.logDebug).toHaveBeenCalledTimes(1);
expect(inner.logInfo).toHaveBeenCalledTimes(1);
expect(inner.logWarn).toHaveBeenCalledTimes(1);
expect(inner.logError).toHaveBeenCalledTimes(1);
expect(inner.logException).toHaveBeenCalledTimes(1);
});

test('drops messages below WARN when min level is WARN', () => {
logAllLevels(new LevelFilterLogger(inner, LEVEL.WARN));

expect(inner.logDebug).not.toHaveBeenCalled();
expect(inner.logInfo).not.toHaveBeenCalled();
expect(inner.logWarn).toHaveBeenCalledTimes(1);
expect(inner.logError).toHaveBeenCalledTimes(1);
expect(inner.logException).toHaveBeenCalledTimes(1);
});

test('forwards exceptions when min level is ERROR', () => {
logAllLevels(new LevelFilterLogger(inner, LEVEL.ERROR));

expect(inner.logWarn).not.toHaveBeenCalled();
expect(inner.logError).toHaveBeenCalledTimes(1);
expect(inner.logException).toHaveBeenCalledTimes(1);
});

test('drops everything when min level is FATAL', () => {
logAllLevels(new LevelFilterLogger(inner, LEVEL.FATAL));

expect(inner.logDebug).not.toHaveBeenCalled();
expect(inner.logInfo).not.toHaveBeenCalled();
expect(inner.logWarn).not.toHaveBeenCalled();
expect(inner.logError).not.toHaveBeenCalled();
expect(inner.logException).not.toHaveBeenCalled();
});

test('passes arguments through unchanged', () => {
const logger = new LevelFilterLogger(inner, LEVEL.DEBUG);
const context = { key: 'value' };
const exception = new Error('boom');

logger.logInfo('message', context);
logger.logException('failed', exception, context);

expect(inner.logInfo).toHaveBeenCalledWith('message', context);
expect(inner.logException).toHaveBeenCalledWith(
'failed',
exception,
context
);
});
});
57 changes: 57 additions & 0 deletions packages/logger/src/loggers/implementation/LevelFilterLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { LEVEL } from '../../enum/Level';
import { Logger } from '../Logger';

const SEVERITY: Record<LEVEL, number> = {
[LEVEL.DEBUG]: 0,
[LEVEL.INFO]: 1,
[LEVEL.WARN]: 2,
[LEVEL.ERROR]: 3,
[LEVEL.EXCEPTION]: 4,
[LEVEL.FATAL]: 5,
};

/**
* A logger that forwards to another logger only the messages
* at or above the given minimum level.
* Severity order: DEBUG < INFO < WARN < ERROR < EXCEPTION < FATAL
*/
export class LevelFilterLogger implements Logger {
constructor(
private logger: Logger,
private minLevel: LEVEL
) {}

private isEnabled(level: LEVEL) {
return SEVERITY[level] >= SEVERITY[this.minLevel];
}

logInfo(message: string, context: Record<string, unknown>): void {
if (this.isEnabled(LEVEL.INFO)) {
this.logger.logInfo(message, context);
}
}
logWarn(message: string, context: Record<string, unknown>): void {
if (this.isEnabled(LEVEL.WARN)) {
this.logger.logWarn(message, context);
}
}
logDebug(message: string, context: Record<string, unknown>): void {
if (this.isEnabled(LEVEL.DEBUG)) {
this.logger.logDebug(message, context);
}
}
logError(message: string, context: Record<string, unknown>): void {
if (this.isEnabled(LEVEL.ERROR)) {
this.logger.logError(message, context);
}
}
logException(
message: string,
exception: unknown,
context?: Record<string, unknown> | undefined
): void {
if (this.isEnabled(LEVEL.EXCEPTION)) {
this.logger.logException(message, exception, context);
}
}
}
Loading