Skip to content

Commit 3d4d4a6

Browse files
authored
feat: introduce logger (#367)
1 parent 878f5b4 commit 3d4d4a6

File tree

14 files changed

+973
-2
lines changed

14 files changed

+973
-2
lines changed

bin/commands/interactive.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from '@clack/prompts';
1414

1515
import commands from './index.mjs';
16+
import { Logger } from '../../src/logger/index.mjs';
1617

1718
/**
1819
* Validates that a string is not empty.
@@ -165,7 +166,7 @@ export default async function interactive() {
165166

166167
const finalCommand = cmdParts.join(' ');
167168

168-
console.log(`\nGenerated command:\n${finalCommand}\n`);
169+
Logger.getInstance().info(`\nGenerated command:\n${finalCommand}\n`);
169170

170171
// Step 5: Confirm and execute the generated command
171172
if (await confirm({ message: 'Run now?', initialValue: true })) {

bin/utils.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import createMarkdownLoader from '../src/loaders/markdown.mjs';
2+
import { Logger } from '../src/logger/index.mjs';
23
import createMarkdownParser from '../src/parsers/markdown.mjs';
34

45
/**
@@ -42,7 +43,7 @@ export const errorWrap =
4243
try {
4344
return await fn(...args);
4445
} catch (err) {
45-
console.error(err);
46+
Logger.getInstance().error(err);
4647
process.exit(1);
4748
}
4849
};
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import { deepStrictEqual, strictEqual } from 'node:assert';
2+
import { describe, it } from 'node:test';
3+
4+
import { LogLevel } from '../constants.mjs';
5+
import { createLogger } from '../logger.mjs';
6+
7+
/**
8+
* @type {import('../types').Metadata}
9+
*/
10+
const metadata = {
11+
file: {
12+
path: 'test.md',
13+
position: {
14+
start: { line: 1 },
15+
end: { line: 1 },
16+
},
17+
},
18+
};
19+
20+
describe('createLogger', () => {
21+
describe('DEBUG', () => {
22+
it('should log DEBUG messages when logger level is set to DEBUG', t => {
23+
t.mock.timers.enable({ apis: ['Date'] });
24+
25+
const transport = t.mock.fn();
26+
27+
const logger = createLogger(transport, LogLevel.debug);
28+
29+
logger.debug('Hello, World!', metadata);
30+
31+
strictEqual(transport.mock.callCount(), 1);
32+
33+
const call = transport.mock.calls[0];
34+
deepStrictEqual(call.arguments, [
35+
{
36+
level: LogLevel.debug,
37+
message: 'Hello, World!',
38+
metadata,
39+
module: undefined,
40+
timestamp: 0,
41+
},
42+
]);
43+
});
44+
45+
it('should filter DEBUG messages when logger level is set to INFO or higher', t => {
46+
[LogLevel.info, LogLevel.warn, LogLevel.error, LogLevel.fatal].forEach(
47+
loggerLevel => {
48+
const transport = t.mock.fn();
49+
50+
const logger = createLogger(transport, loggerLevel);
51+
52+
logger.debug('Hello, World!');
53+
54+
strictEqual(transport.mock.callCount(), 0);
55+
}
56+
);
57+
});
58+
});
59+
60+
describe('INFO', () => {
61+
it('should log INFO messages when logger level is set to INFO or lower', t => {
62+
t.mock.timers.enable({ apis: ['Date'] });
63+
[LogLevel.info, LogLevel.debug].forEach(loggerLevel => {
64+
const transport = t.mock.fn();
65+
66+
const logger = createLogger(transport, loggerLevel);
67+
68+
logger.info('Hello, World!', metadata);
69+
70+
strictEqual(transport.mock.callCount(), 1);
71+
72+
const call = transport.mock.calls[0];
73+
deepStrictEqual(call.arguments, [
74+
{
75+
level: LogLevel.info,
76+
message: 'Hello, World!',
77+
metadata,
78+
module: undefined,
79+
timestamp: 0,
80+
},
81+
]);
82+
});
83+
});
84+
85+
it('should filter INFO messages when logger level is set to WARN or higher', t => {
86+
[LogLevel.warn, LogLevel.error, LogLevel.fatal].forEach(loggerLevel => {
87+
const transport = t.mock.fn();
88+
89+
const logger = createLogger(transport, loggerLevel);
90+
91+
logger.info('Hello, World!');
92+
93+
strictEqual(transport.mock.callCount(), 0);
94+
});
95+
});
96+
});
97+
98+
describe('WARN', () => {
99+
it('should log WARN messages when logger level is set to WARN or lower', t => {
100+
t.mock.timers.enable({ apis: ['Date'] });
101+
102+
[LogLevel.warn, LogLevel.info, LogLevel.debug].forEach(loggerLevel => {
103+
const transport = t.mock.fn();
104+
105+
const logger = createLogger(transport, loggerLevel);
106+
107+
logger.warn('Hello, World!', metadata);
108+
109+
strictEqual(transport.mock.callCount(), 1);
110+
111+
const call = transport.mock.calls[0];
112+
deepStrictEqual(call.arguments, [
113+
{
114+
level: LogLevel.warn,
115+
message: 'Hello, World!',
116+
metadata,
117+
module: undefined,
118+
timestamp: 0,
119+
},
120+
]);
121+
});
122+
});
123+
124+
it('should filter WARN messages when logger level is set to ERROR or higher', t => {
125+
[LogLevel.error, LogLevel.fatal].forEach(loggerLevel => {
126+
const transport = t.mock.fn();
127+
128+
const logger = createLogger(transport, loggerLevel);
129+
130+
logger.warn('Hello, World!');
131+
132+
strictEqual(transport.mock.callCount(), 0);
133+
});
134+
});
135+
});
136+
137+
describe('ERROR', () => {
138+
it('should log ERROR messages when logger level is set to ERROR or lower', t => {
139+
t.mock.timers.enable({ apis: ['Date'] });
140+
141+
[LogLevel.error, LogLevel.warn, LogLevel.info, LogLevel.debug].forEach(
142+
loggerLevel => {
143+
const transport = t.mock.fn();
144+
145+
const logger = createLogger(transport, loggerLevel);
146+
147+
logger.error('Hello, World!', metadata);
148+
149+
strictEqual(transport.mock.callCount(), 1);
150+
151+
const call = transport.mock.calls[0];
152+
deepStrictEqual(call.arguments, [
153+
{
154+
level: LogLevel.error,
155+
message: 'Hello, World!',
156+
metadata,
157+
module: undefined,
158+
timestamp: 0,
159+
},
160+
]);
161+
}
162+
);
163+
});
164+
165+
it('should filter ERROR messages when logger level is set to FATAL', t => {
166+
const transport = t.mock.fn();
167+
168+
const logger = createLogger(transport, LogLevel.fatal);
169+
170+
logger.warn('Hello, World!');
171+
172+
strictEqual(transport.mock.callCount(), 0);
173+
});
174+
});
175+
176+
it('should filter all messages when minimum level is set above FATAL', t => {
177+
const transport = t.mock.fn();
178+
179+
// silent logs
180+
const logger = createLogger(transport, 100);
181+
182+
Object.keys(LogLevel).forEach(level => {
183+
logger[level]('Hello, World!');
184+
});
185+
186+
strictEqual(transport.mock.callCount(), 0);
187+
});
188+
189+
it('should log all messages if message is a string array', t => {
190+
const transport = t.mock.fn();
191+
192+
const logger = createLogger(transport, LogLevel.info);
193+
194+
logger.info(['Hello, 1!', 'Hello, 2!', 'Hello, 3!']);
195+
196+
strictEqual(transport.mock.callCount(), 3);
197+
});
198+
199+
it('should log error message', t => {
200+
t.mock.timers.enable({ apis: ['Date'] });
201+
202+
const transport = t.mock.fn();
203+
204+
const logger = createLogger(transport, LogLevel.error);
205+
206+
logger.error(new Error('Hello, World!'));
207+
208+
strictEqual(transport.mock.callCount(), 1);
209+
210+
const call = transport.mock.calls[0];
211+
deepStrictEqual(call.arguments, [
212+
{
213+
level: LogLevel.error,
214+
message: 'Hello, World!',
215+
metadata: {},
216+
module: undefined,
217+
timestamp: 0,
218+
},
219+
]);
220+
});
221+
});

0 commit comments

Comments
 (0)