Skip to content

Commit 3f4336d

Browse files
committed
feat: Allow logging errors when passed as message parameter
Signed-off-by: Ferdinand Thiessen <rpm@fthiessen.de>
1 parent 07b02c2 commit 3f4336d

File tree

3 files changed

+100
-14
lines changed

3 files changed

+100
-14
lines changed

lib/ConsoleLogger.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,33 @@ export class ConsoleLogger implements ILogger {
55
private context: any
66

77
constructor(context: any) {
8-
this.context = context
8+
this.context = context || {}
99
}
1010

11-
private formatMessage(message: string, level: LogLevel, context: any): string {
11+
private formatMessage(message: string|Error, level: LogLevel, context: any): string {
1212
let msg = '[' + LogLevel[level].toUpperCase() + ']'
13+
1314
if (context && context.app) {
1415
msg += ' ' + context.app + ': '
1516
}
16-
return msg + message
17+
18+
if (typeof message === 'string') return msg + message
19+
20+
// basic error formatting
21+
msg += `Unexpected ${message.name}`
22+
if (message.message) msg += ` "${message.message}"`
23+
// only add stack trace when debugging
24+
if (level === LogLevel.Debug && message.stack) msg += `\n\nStack trace:\n${message.stack}`
25+
26+
return msg
1727
}
1828

19-
log(level: LogLevel, message: string, context: object) {
29+
log(level: LogLevel, message: string|Error, context: object) {
2030
if (level < this.context?.level) return;
2131

32+
// Add error object to context
33+
if (typeof message === 'object' && (context as any).error === undefined) (context as any).error = message
34+
2235
switch (level) {
2336
case LogLevel.Debug:
2437
console.debug(this.formatMessage(message, LogLevel.Debug, context), context)
@@ -39,23 +52,23 @@ export class ConsoleLogger implements ILogger {
3952
}
4053
}
4154

42-
debug(message: string, context?: object): void {
55+
debug(message: string|Error, context?: object): void {
4356
this.log(LogLevel.Debug, message, Object.assign({}, this.context, context))
4457
}
4558

46-
info(message: string, context?: object): void {
59+
info(message: string|Error, context?: object): void {
4760
this.log(LogLevel.Info, message, Object.assign({}, this.context, context))
4861
}
4962

50-
warn(message: string, context?: object): void {
63+
warn(message: string|Error, context?: object): void {
5164
this.log(LogLevel.Warn, message, Object.assign({}, this.context, context))
5265
}
5366

54-
error(message: string, context?: object): void {
67+
error(message: string|Error, context?: object): void {
5568
this.log(LogLevel.Error, message, Object.assign({}, this.context, context))
5669
}
5770

58-
fatal(message: string, context?: object): void {
71+
fatal(message: string|Error, context?: object): void {
5972
this.log(LogLevel.Fatal, message, Object.assign({}, this.context, context))
6073
}
6174

lib/contracts.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ export enum LogLevel {
88

99
export interface ILogger {
1010

11-
debug(message: string, context?: object): void
12-
info(message: string, context?: object): void
13-
warn(message: string, context?: object): void
14-
error(message: string, context?: object): void
15-
fatal(message: string, context?: object): void
11+
debug(message: string|Error, context?: object): void
12+
info(message: string|Error, context?: object): void
13+
warn(message: string|Error, context?: object): void
14+
error(message: string|Error, context?: object): void
15+
fatal(message: string|Error, context?: object): void
1616

1717
}
1818

tests/ConsoleLogger.spec.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { ConsoleLogger, buildConsoleLogger } from '../lib/ConsoleLogger'
2+
3+
// Dummy Error
4+
class MyError extends Error {
5+
constructor(msg: string) {
6+
super(msg)
7+
this.name = 'MyError'
8+
}
9+
}
10+
11+
afterEach(() => {
12+
jest.resetAllMocks()
13+
})
14+
15+
test('building the console logger', () => {
16+
const logger = buildConsoleLogger({ app: 'myapp' })
17+
expect(logger).toBeInstanceOf(ConsoleLogger)
18+
19+
// ensure initial context is preserved
20+
const consoleArgs = [] as any[]
21+
const warn = jest.spyOn(console, "warn").mockImplementation((...args) => consoleArgs.push(...args))
22+
logger.warn('some message', { foo: 'bar' })
23+
expect(warn).toHaveBeenCalledTimes(1)
24+
expect(consoleArgs).toHaveLength(2)
25+
expect(consoleArgs[1]).toHaveProperty('app', 'myapp')
26+
})
27+
28+
describe('ConsoleLogger', () => {
29+
afterEach(() => jest.resetAllMocks())
30+
31+
it('logs errors', () => {
32+
const error = new MyError('some message')
33+
const logger = new ConsoleLogger({})
34+
35+
const console = [] as any[][]
36+
const warn = jest.spyOn(window.console, "warn").mockImplementation((msg, ctx) => console.push([msg, ctx]))
37+
38+
logger.warn(error)
39+
expect(warn).toHaveBeenCalledTimes(1)
40+
expect(console[0][0]).toContain('MyError')
41+
expect(console[0][0]).toContain('some message')
42+
expect(console[0][0]).not.toContain('Stack trace')
43+
expect(console[0][1]).toHaveProperty('error', error)
44+
})
45+
46+
it('logs error and stack trace on debug', () => {
47+
const error = new MyError('some message')
48+
const logger = new ConsoleLogger({})
49+
50+
const console = [] as any[][]
51+
const debug = jest.spyOn(window.console, "debug").mockImplementation((msg, ctx) => console.push([msg, ctx]))
52+
53+
logger.debug(error)
54+
expect(debug).toHaveBeenCalledTimes(1)
55+
expect(console[0][0]).toContain('MyError')
56+
expect(console[0][0]).toContain('some message')
57+
expect(console[0][0]).toContain('Stack trace:')
58+
})
59+
60+
it('logs error and does not override context', () => {
61+
const error = new MyError('some message')
62+
const logger = new ConsoleLogger({ error: 'none' })
63+
64+
const console = [] as any[][]
65+
const warn = jest.spyOn(window.console, "warn").mockImplementation((msg, ctx) => console.push([msg, ctx]))
66+
67+
logger.warn(error)
68+
expect(warn).toHaveBeenCalledTimes(1)
69+
expect(console[0][0]).toContain('MyError')
70+
expect(console[0][0]).toContain('some message')
71+
expect(console[0][1]).toHaveProperty('error', 'none')
72+
})
73+
})

0 commit comments

Comments
 (0)