@mks2508/better-logger 2.1.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +8 -1
- package/CHANGELOG.json +168 -1
- package/CHANGELOG.md +127 -0
- package/CLAUDE.md +14 -1
- package/README.md +111 -20
- package/dist/Logger.d.ts +217 -70
- package/dist/Logger.d.ts.map +1 -1
- package/dist/ScopedLogger.d.ts +23 -276
- package/dist/ScopedLogger.d.ts.map +1 -1
- package/dist/chunks/Logger-BV0v1AyC.js +2 -0
- package/dist/chunks/Logger-BV0v1AyC.js.map +1 -0
- package/dist/chunks/{Logger-B7L-ujY4.js → Logger-C086w3EM.js} +868 -102
- package/dist/chunks/Logger-C086w3EM.js.map +1 -0
- package/dist/chunks/environment-C_8J-zQ_.js +4 -0
- package/dist/chunks/environment-C_8J-zQ_.js.map +1 -0
- package/dist/chunks/{environment-TI2ByCPT.js → environment-DXHouGD4.js} +468 -100
- package/dist/chunks/environment-DXHouGD4.js.map +1 -0
- package/dist/chunks/{formatting-CuNUqGks.js → formatting-BNnVUPcw.js} +2 -2
- package/dist/chunks/{formatting-CuNUqGks.js.map → formatting-BNnVUPcw.js.map} +1 -1
- package/dist/chunks/{formatting-Blwy-f0W.js → formatting-Cg5YhB9Y.js} +2 -2
- package/dist/chunks/{formatting-Blwy-f0W.js.map → formatting-Cg5YhB9Y.js.map} +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.js +2 -2
- package/dist/exports-module.d.ts +1 -1
- package/dist/exports-module.d.ts.map +1 -1
- package/dist/exports.cjs +1 -1
- package/dist/exports.js +2 -2
- package/dist/hooks/HookManager.d.ts +21 -0
- package/dist/hooks/HookManager.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +30 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +433 -20
- package/dist/index.js.map +1 -1
- package/dist/serializers/SerializerRegistry.d.ts +16 -0
- package/dist/serializers/SerializerRegistry.d.ts.map +1 -0
- package/dist/serializers/index.d.ts +2 -0
- package/dist/serializers/index.d.ts.map +1 -0
- package/dist/styling/StyleCache.d.ts +42 -0
- package/dist/styling/StyleCache.d.ts.map +1 -0
- package/dist/styling-module.d.ts +1 -1
- package/dist/styling-module.d.ts.map +1 -1
- package/dist/styling.cjs +1 -1
- package/dist/styling.js +3 -3
- package/dist/terminal/color-converter.d.ts +73 -0
- package/dist/terminal/color-converter.d.ts.map +1 -0
- package/dist/terminal/formatter.d.ts +20 -0
- package/dist/terminal/formatter.d.ts.map +1 -0
- package/dist/terminal/terminal-renderer.d.ts +20 -6
- package/dist/terminal/terminal-renderer.d.ts.map +1 -1
- package/dist/transports/ConsoleTransport.d.ts +9 -0
- package/dist/transports/ConsoleTransport.d.ts.map +1 -0
- package/dist/transports/FileTransport.d.ts +15 -0
- package/dist/transports/FileTransport.d.ts.map +1 -0
- package/dist/transports/HttpTransport.d.ts +16 -0
- package/dist/transports/HttpTransport.d.ts.map +1 -0
- package/dist/transports/TransportManager.d.ts +14 -0
- package/dist/transports/TransportManager.d.ts.map +1 -0
- package/dist/transports/index.d.ts +5 -0
- package/dist/transports/index.d.ts.map +1 -0
- package/dist/types/core.d.ts +55 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/hooks.d.ts +36 -0
- package/dist/types/hooks.d.ts.map +1 -0
- package/dist/types/index.d.ts +5 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/serializers.d.ts +25 -0
- package/dist/types/serializers.d.ts.map +1 -0
- package/dist/types/transports.d.ts +47 -0
- package/dist/types/transports.d.ts.map +1 -0
- package/dist/utils/environment-detector.d.ts +10 -0
- package/dist/utils/environment-detector.d.ts.map +1 -1
- package/docs/API.md +227 -0
- package/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/styling/package.json +2 -2
- package/src/Logger.ts +403 -114
- package/src/ScopedLogger.ts +78 -318
- package/src/hooks/HookManager.ts +177 -0
- package/src/hooks/index.ts +1 -0
- package/src/index.ts +133 -3
- package/src/serializers/SerializerRegistry.ts +173 -0
- package/src/serializers/index.ts +1 -0
- package/src/styling/StyleCache.ts +131 -0
- package/src/terminal/color-converter.ts +315 -0
- package/src/terminal/formatter.ts +236 -0
- package/src/terminal/terminal-renderer.ts +74 -79
- package/src/transports/ConsoleTransport.ts +28 -0
- package/src/transports/FileTransport.ts +53 -0
- package/src/transports/HttpTransport.ts +56 -0
- package/src/transports/TransportManager.ts +130 -0
- package/src/transports/index.ts +4 -0
- package/src/types/core.ts +68 -0
- package/src/types/hooks.ts +45 -0
- package/src/types/index.ts +44 -1
- package/src/types/serializers.ts +30 -0
- package/src/types/transports.ts +52 -0
- package/src/utils/environment-detector.ts +23 -1
- package/dist/chunks/Logger-B7L-ujY4.js.map +0 -1
- package/dist/chunks/Logger-DzU_c5sX.js +0 -2
- package/dist/chunks/Logger-DzU_c5sX.js.map +0 -1
- package/dist/chunks/ScopedLogger-BY3-E8Ov.js +0 -2
- package/dist/chunks/ScopedLogger-BY3-E8Ov.js.map +0 -1
- package/dist/chunks/ScopedLogger-D-RbiFZn.js +0 -361
- package/dist/chunks/ScopedLogger-D-RbiFZn.js.map +0 -1
- package/dist/chunks/environment-Ba5kShbx.js +0 -4
- package/dist/chunks/environment-Ba5kShbx.js.map +0 -1
- package/dist/chunks/environment-TI2ByCPT.js.map +0 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { LogLevel } from '../types/index.js';
|
|
7
7
|
import type { LogStyles } from '../types/index.js';
|
|
8
|
+
import { getANSIForeground, getANSIBackground, ANSI, type ColorCapability } from './color-converter.js';
|
|
8
9
|
|
|
9
10
|
export type ANSIStyle = {
|
|
10
11
|
text: string;
|
|
@@ -17,32 +18,19 @@ export type ANSIStyle = {
|
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
export class TerminalRenderer {
|
|
20
|
-
private colorCapability:
|
|
21
|
+
private colorCapability: ColorCapability;
|
|
21
22
|
|
|
22
|
-
constructor(colorCapability:
|
|
23
|
+
constructor(colorCapability: ColorCapability = 'full') {
|
|
23
24
|
this.colorCapability = colorCapability;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
|
-
* Get ANSI color codes
|
|
28
|
+
* Get ANSI color codes - now supports truecolor/256-color
|
|
28
29
|
*/
|
|
29
30
|
private getColorCode(color: string, background: boolean = false): string {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
'green': { normal: '32', bright: '92' },
|
|
34
|
-
'yellow': { normal: '33', bright: '93' },
|
|
35
|
-
'blue': { normal: '34', bright: '94' },
|
|
36
|
-
'magenta': { normal: '35', bright: '95' },
|
|
37
|
-
'cyan': { normal: '36', bright: '96' },
|
|
38
|
-
'white': { normal: '37', bright: '97' },
|
|
39
|
-
'gray': { normal: '90', bright: '37' },
|
|
40
|
-
'grey': { normal: '90', bright: '37' }
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const codes = colorCodes[color.toLowerCase()] || { normal: '37', bright: '97' };
|
|
44
|
-
const prefix = background ? '4' : '3';
|
|
45
|
-
return `\x1b[${codes.normal}m`;
|
|
31
|
+
return background
|
|
32
|
+
? getANSIBackground(color, this.colorCapability)
|
|
33
|
+
: getANSIForeground(color, this.colorCapability);
|
|
46
34
|
}
|
|
47
35
|
|
|
48
36
|
/**
|
|
@@ -207,7 +195,7 @@ export class TerminalRenderer {
|
|
|
207
195
|
}
|
|
208
196
|
|
|
209
197
|
/**
|
|
210
|
-
* Create cyberpunk-style ANSI rendering
|
|
198
|
+
* Create cyberpunk-style ANSI rendering with truecolor support
|
|
211
199
|
*/
|
|
212
200
|
renderCyberpunk(
|
|
213
201
|
level: LogLevel,
|
|
@@ -216,35 +204,36 @@ export class TerminalRenderer {
|
|
|
216
204
|
prefix?: string,
|
|
217
205
|
location?: string
|
|
218
206
|
): ANSIStyle {
|
|
219
|
-
const reset =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
'
|
|
223
|
-
'
|
|
224
|
-
'
|
|
225
|
-
'
|
|
207
|
+
const reset = ANSI.reset;
|
|
208
|
+
|
|
209
|
+
const cyberpunkColors: Record<LogLevel, string> = {
|
|
210
|
+
'debug': '#ff00ff', // Neon magenta
|
|
211
|
+
'info': '#00ffff', // Neon cyan
|
|
212
|
+
'warn': '#ffff00', // Neon yellow
|
|
213
|
+
'error': '#ff0040', // Neon red-pink
|
|
214
|
+
'critical': '#ff0000' // Pure red
|
|
226
215
|
};
|
|
227
216
|
|
|
228
217
|
const parts: string[] = [];
|
|
229
218
|
|
|
230
219
|
if (timestamp) {
|
|
231
|
-
parts.push(
|
|
220
|
+
parts.push(`${ANSI.dim}${timestamp}${reset}`);
|
|
232
221
|
}
|
|
233
222
|
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
parts.push(`${bgBlack}${levelColor} ${this.getLevelText(level)} ${reset}`);
|
|
223
|
+
const levelColor = this.getColorCode(cyberpunkColors[level] || '#ffffff');
|
|
224
|
+
const bgBlack = ANSI.bg.black;
|
|
225
|
+
const bold = level === 'critical' ? ANSI.bold : '';
|
|
226
|
+
parts.push(`${bgBlack}${levelColor}${bold} ${this.getLevelText(level)} ${reset}`);
|
|
238
227
|
|
|
239
228
|
if (prefix) {
|
|
240
|
-
const
|
|
241
|
-
parts.push(`${bgBlack}
|
|
229
|
+
const cyanColor = this.getColorCode('#00ffff');
|
|
230
|
+
parts.push(`${bgBlack}${cyanColor}[${prefix.toUpperCase()}]${reset}`);
|
|
242
231
|
}
|
|
243
232
|
|
|
244
233
|
parts.push(message);
|
|
245
234
|
|
|
246
235
|
if (location) {
|
|
247
|
-
parts.push(
|
|
236
|
+
parts.push(`${ANSI.dim}(${location})${reset}`);
|
|
248
237
|
}
|
|
249
238
|
|
|
250
239
|
return {
|
|
@@ -254,7 +243,7 @@ export class TerminalRenderer {
|
|
|
254
243
|
}
|
|
255
244
|
|
|
256
245
|
/**
|
|
257
|
-
* Create minimal ANSI rendering
|
|
246
|
+
* Create minimal ANSI rendering with truecolor support
|
|
258
247
|
*/
|
|
259
248
|
renderMinimal(
|
|
260
249
|
level: LogLevel,
|
|
@@ -262,26 +251,29 @@ export class TerminalRenderer {
|
|
|
262
251
|
timestamp?: string,
|
|
263
252
|
prefix?: string
|
|
264
253
|
): ANSIStyle {
|
|
265
|
-
const reset =
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
'
|
|
269
|
-
'
|
|
270
|
-
'
|
|
271
|
-
'
|
|
254
|
+
const reset = ANSI.reset;
|
|
255
|
+
|
|
256
|
+
const minimalColors: Record<LogLevel, string> = {
|
|
257
|
+
'debug': '#c678dd', // Soft purple
|
|
258
|
+
'info': '#61afef', // Soft blue
|
|
259
|
+
'warn': '#e5c07b', // Soft yellow
|
|
260
|
+
'error': '#e06c75', // Soft red
|
|
261
|
+
'critical': '#be5046' // Dark red
|
|
272
262
|
};
|
|
273
263
|
|
|
274
264
|
const parts: string[] = [];
|
|
275
265
|
|
|
276
266
|
if (timestamp) {
|
|
277
|
-
parts.push(
|
|
267
|
+
parts.push(`${ANSI.dim}${timestamp}${reset}`);
|
|
278
268
|
}
|
|
279
269
|
|
|
280
|
-
const levelColor =
|
|
281
|
-
|
|
270
|
+
const levelColor = this.getColorCode(minimalColors[level] || '#abb2bf');
|
|
271
|
+
const bold = level === 'critical' ? ANSI.bold : '';
|
|
272
|
+
parts.push(`${levelColor}${bold}${this.getLevelText(level)}:${reset}`);
|
|
282
273
|
|
|
283
274
|
if (prefix) {
|
|
284
|
-
|
|
275
|
+
const cyanColor = this.getColorCode('#56b6c2');
|
|
276
|
+
parts.push(`${cyanColor}[${prefix.toUpperCase()}]${reset}`);
|
|
285
277
|
}
|
|
286
278
|
|
|
287
279
|
parts.push(message);
|
|
@@ -293,55 +285,58 @@ export class TerminalRenderer {
|
|
|
293
285
|
}
|
|
294
286
|
|
|
295
287
|
/**
|
|
296
|
-
* Get chalk
|
|
288
|
+
* Get chalk-like interface for log level with truecolor support
|
|
297
289
|
*/
|
|
298
|
-
public getChalkForLevel(level: LogLevel):
|
|
290
|
+
public getChalkForLevel(level: LogLevel): ChalkLikeInterface {
|
|
299
291
|
const levelColors: Record<LogLevel, string> = {
|
|
300
|
-
'debug': '
|
|
301
|
-
'info': '
|
|
302
|
-
'warn': '
|
|
303
|
-
'error': '
|
|
304
|
-
'critical': '
|
|
292
|
+
'debug': '#c678dd',
|
|
293
|
+
'info': '#61afef',
|
|
294
|
+
'warn': '#e5c07b',
|
|
295
|
+
'error': '#e06c75',
|
|
296
|
+
'critical': '#be5046'
|
|
305
297
|
};
|
|
306
298
|
|
|
299
|
+
const reset = ANSI.reset;
|
|
300
|
+
|
|
307
301
|
return {
|
|
308
302
|
color: (text: string) => {
|
|
309
|
-
const color = levelColors[level] || '
|
|
303
|
+
const color = levelColors[level] || '#abb2bf';
|
|
310
304
|
const colorCode = this.getColorCode(color);
|
|
311
|
-
return `${colorCode}${text}${
|
|
312
|
-
},
|
|
313
|
-
bold: (text: string) => {
|
|
314
|
-
const boldStyle = this.getStyleCode(['bold']);
|
|
315
|
-
return `${boldStyle}${text}${'\x1b[0m'}`;
|
|
316
|
-
},
|
|
317
|
-
dim: (text: string) => {
|
|
318
|
-
const dimStyle = this.getStyleCode(['dim']);
|
|
319
|
-
return `${dimStyle}${text}${'\x1b[0m'}`;
|
|
320
|
-
},
|
|
321
|
-
bgGray: (text: string) => {
|
|
322
|
-
return `\x1b[100m${text}${'\x1b[0m'}`;
|
|
323
|
-
},
|
|
324
|
-
bgBlue: (text: string) => {
|
|
325
|
-
return `\x1b[104m${text}${'\x1b[0m'}`;
|
|
305
|
+
return `${colorCode}${text}${reset}`;
|
|
326
306
|
},
|
|
307
|
+
bold: (text: string) => `${ANSI.bold}${text}${reset}`,
|
|
308
|
+
dim: (text: string) => `${ANSI.dim}${text}${reset}`,
|
|
309
|
+
bgGray: (text: string) => `${ANSI.bg.gray}${text}${reset}`,
|
|
310
|
+
bgBlue: (text: string) => `${this.getColorCode('#1e3a5f', true)}${text}${reset}`,
|
|
327
311
|
reset: (text: string) => text,
|
|
328
312
|
cyan: {
|
|
329
313
|
bold: (text: string) => {
|
|
330
|
-
const cyan = this.getColorCode('
|
|
331
|
-
|
|
332
|
-
return `${cyan}${bold}${text}${'\x1b[0m'}`;
|
|
314
|
+
const cyan = this.getColorCode('#00ffff');
|
|
315
|
+
return `${cyan}${ANSI.bold}${text}${reset}`;
|
|
333
316
|
},
|
|
334
317
|
dim: (text: string) => {
|
|
335
|
-
const cyan = this.getColorCode('
|
|
336
|
-
|
|
337
|
-
return `${cyan}${dim}${text}${'\x1b[0m'}`;
|
|
318
|
+
const cyan = this.getColorCode('#00ffff');
|
|
319
|
+
return `${cyan}${ANSI.dim}${text}${reset}`;
|
|
338
320
|
},
|
|
339
321
|
bg: (text: string) => {
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
return `${bgBlack}${cyan}[${text.toUpperCase()}]${'\x1b[0m'}`;
|
|
322
|
+
const cyan = this.getColorCode('#00ffff');
|
|
323
|
+
return `${ANSI.bg.black}${cyan}[${text.toUpperCase()}]${reset}`;
|
|
343
324
|
}
|
|
344
325
|
}
|
|
345
326
|
};
|
|
346
327
|
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export interface ChalkLikeInterface {
|
|
331
|
+
color: (text: string) => string;
|
|
332
|
+
bold: (text: string) => string;
|
|
333
|
+
dim: (text: string) => string;
|
|
334
|
+
bgGray: (text: string) => string;
|
|
335
|
+
bgBlue: (text: string) => string;
|
|
336
|
+
reset: (text: string) => string;
|
|
337
|
+
cyan: {
|
|
338
|
+
bold: (text: string) => string;
|
|
339
|
+
dim: (text: string) => string;
|
|
340
|
+
bg: (text: string) => string;
|
|
341
|
+
};
|
|
347
342
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { TransportRecord, TransportOptions, ITransport, LogLevel } from '../types/index.js';
|
|
2
|
+
|
|
3
|
+
export class ConsoleTransport implements ITransport {
|
|
4
|
+
readonly name = 'console';
|
|
5
|
+
|
|
6
|
+
constructor(private options?: TransportOptions) {}
|
|
7
|
+
|
|
8
|
+
write(record: TransportRecord): void {
|
|
9
|
+
const method = this.getConsoleMethod(record.level);
|
|
10
|
+
const prefix = record.prefix ? `[${record.prefix}] ` : '';
|
|
11
|
+
const location = record.location
|
|
12
|
+
? ` (${record.location.file}:${record.location.line})`
|
|
13
|
+
: '';
|
|
14
|
+
|
|
15
|
+
console[method](`[${record.level.toUpperCase()}]${prefix} ${record.msg}${location}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private getConsoleMethod(level: LogLevel): 'log' | 'info' | 'warn' | 'error' {
|
|
19
|
+
switch (level) {
|
|
20
|
+
case 'debug': return 'log';
|
|
21
|
+
case 'info': return 'info';
|
|
22
|
+
case 'warn': return 'warn';
|
|
23
|
+
case 'error':
|
|
24
|
+
case 'critical': return 'error';
|
|
25
|
+
default: return 'log';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { TransportRecord, TransportOptions, ITransport } from '../types/index.js';
|
|
2
|
+
|
|
3
|
+
export interface FileTransportOptions extends TransportOptions {
|
|
4
|
+
destination?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class FileTransport implements ITransport {
|
|
8
|
+
readonly name = 'file';
|
|
9
|
+
private buffer: string[] = [];
|
|
10
|
+
private flushTimer?: ReturnType<typeof setInterval>;
|
|
11
|
+
private options: FileTransportOptions;
|
|
12
|
+
|
|
13
|
+
constructor(options?: FileTransportOptions) {
|
|
14
|
+
this.options = options || {};
|
|
15
|
+
|
|
16
|
+
if (this.options.flushInterval) {
|
|
17
|
+
this.flushTimer = setInterval(() => this.flush(), this.options.flushInterval);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
write(record: TransportRecord): void {
|
|
22
|
+
const line = JSON.stringify(record) + '\n';
|
|
23
|
+
this.buffer.push(line);
|
|
24
|
+
|
|
25
|
+
if (this.buffer.length >= (this.options.batchSize || 100)) {
|
|
26
|
+
this.flush();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async flush(): Promise<void> {
|
|
31
|
+
if (this.buffer.length === 0) return;
|
|
32
|
+
|
|
33
|
+
const lines = this.buffer.join('');
|
|
34
|
+
this.buffer = [];
|
|
35
|
+
|
|
36
|
+
if (typeof globalThis.process !== 'undefined' && typeof require === 'function') {
|
|
37
|
+
try {
|
|
38
|
+
const fs = require('fs');
|
|
39
|
+
const dest = this.options.destination || 'app.log';
|
|
40
|
+
fs.appendFileSync(dest, lines);
|
|
41
|
+
} catch {
|
|
42
|
+
// Silent fail in browser or if fs not available
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
close(): void {
|
|
48
|
+
if (this.flushTimer) {
|
|
49
|
+
clearInterval(this.flushTimer);
|
|
50
|
+
}
|
|
51
|
+
this.flush();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { TransportRecord, TransportOptions, ITransport } from '../types/index.js';
|
|
2
|
+
|
|
3
|
+
export interface HttpTransportOptions extends TransportOptions {
|
|
4
|
+
url?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class HttpTransport implements ITransport {
|
|
9
|
+
readonly name = 'http';
|
|
10
|
+
private buffer: TransportRecord[] = [];
|
|
11
|
+
private flushTimer?: ReturnType<typeof setInterval>;
|
|
12
|
+
private options: HttpTransportOptions;
|
|
13
|
+
|
|
14
|
+
constructor(options?: HttpTransportOptions) {
|
|
15
|
+
this.options = options || {};
|
|
16
|
+
|
|
17
|
+
if (this.options.flushInterval) {
|
|
18
|
+
this.flushTimer = setInterval(() => this.flush(), this.options.flushInterval);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
write(record: TransportRecord): void {
|
|
23
|
+
this.buffer.push(record);
|
|
24
|
+
|
|
25
|
+
if (this.buffer.length >= (this.options.batchSize || 50)) {
|
|
26
|
+
this.flush();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async flush(): Promise<void> {
|
|
31
|
+
if (this.buffer.length === 0 || !this.options.url) return;
|
|
32
|
+
|
|
33
|
+
const records = [...this.buffer];
|
|
34
|
+
this.buffer = [];
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
await fetch(this.options.url, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
...this.options.headers
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({ logs: records })
|
|
44
|
+
});
|
|
45
|
+
} catch {
|
|
46
|
+
this.buffer.unshift(...records);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
close(): void {
|
|
51
|
+
if (this.flushTimer) {
|
|
52
|
+
clearInterval(this.flushTimer);
|
|
53
|
+
}
|
|
54
|
+
this.flush();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TransportRecord,
|
|
3
|
+
TransportTarget,
|
|
4
|
+
TransportOptions,
|
|
5
|
+
ITransport,
|
|
6
|
+
ITransportManager,
|
|
7
|
+
LogLevel
|
|
8
|
+
} from '../types/index.js';
|
|
9
|
+
import { LOG_LEVELS } from '../types/index.js';
|
|
10
|
+
|
|
11
|
+
function generateTransportId(): string {
|
|
12
|
+
return `transport-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface TransportEntry {
|
|
16
|
+
id: string;
|
|
17
|
+
transport: ITransport;
|
|
18
|
+
options: TransportOptions;
|
|
19
|
+
level: LogLevel;
|
|
20
|
+
levelValue: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class TransportManager implements ITransportManager {
|
|
24
|
+
private transports: Map<string, TransportEntry> = new Map();
|
|
25
|
+
private defaultLevel: LogLevel = 'info';
|
|
26
|
+
|
|
27
|
+
constructor(defaultLevel?: LogLevel) {
|
|
28
|
+
if (defaultLevel) {
|
|
29
|
+
this.defaultLevel = defaultLevel;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
add(target: TransportTarget): string {
|
|
34
|
+
const id = generateTransportId();
|
|
35
|
+
const level = target.level || this.defaultLevel;
|
|
36
|
+
|
|
37
|
+
let transport: ITransport;
|
|
38
|
+
|
|
39
|
+
if (typeof target.target === 'string') {
|
|
40
|
+
throw new Error(`String transport targets not supported. Use inline transport object.`);
|
|
41
|
+
} else {
|
|
42
|
+
transport = target.target;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const entry: TransportEntry = {
|
|
46
|
+
id,
|
|
47
|
+
transport,
|
|
48
|
+
options: target.options || {},
|
|
49
|
+
level,
|
|
50
|
+
levelValue: LOG_LEVELS[level]
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
this.transports.set(id, entry);
|
|
54
|
+
return id;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
remove(id: string): boolean {
|
|
58
|
+
const entry = this.transports.get(id);
|
|
59
|
+
if (entry) {
|
|
60
|
+
entry.transport.close?.();
|
|
61
|
+
return this.transports.delete(id);
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async write(record: TransportRecord): Promise<void> {
|
|
67
|
+
const promises: Promise<void>[] = [];
|
|
68
|
+
|
|
69
|
+
for (const entry of this.transports.values()) {
|
|
70
|
+
if (record.levelValue < entry.levelValue) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let transformedRecord = record;
|
|
75
|
+
if (entry.options.transform) {
|
|
76
|
+
const result = entry.options.transform(record);
|
|
77
|
+
if (result === null) continue;
|
|
78
|
+
transformedRecord = result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result = entry.transport.write(transformedRecord);
|
|
82
|
+
if (result instanceof Promise) {
|
|
83
|
+
promises.push(result);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await Promise.all(promises);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async flush(): Promise<void> {
|
|
91
|
+
const promises: Promise<void>[] = [];
|
|
92
|
+
|
|
93
|
+
for (const entry of this.transports.values()) {
|
|
94
|
+
if (entry.transport.flush) {
|
|
95
|
+
const result = entry.transport.flush();
|
|
96
|
+
if (result instanceof Promise) {
|
|
97
|
+
promises.push(result);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await Promise.all(promises);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async close(): Promise<void> {
|
|
106
|
+
await this.flush();
|
|
107
|
+
|
|
108
|
+
const promises: Promise<void>[] = [];
|
|
109
|
+
|
|
110
|
+
for (const entry of this.transports.values()) {
|
|
111
|
+
if (entry.transport.close) {
|
|
112
|
+
const result = entry.transport.close();
|
|
113
|
+
if (result instanceof Promise) {
|
|
114
|
+
promises.push(result);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await Promise.all(promises);
|
|
120
|
+
this.transports.clear();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get count(): number {
|
|
124
|
+
return this.transports.size;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
list(): string[] {
|
|
128
|
+
return Array.from(this.transports.keys());
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { TransportManager } from './TransportManager.js';
|
|
2
|
+
export { ConsoleTransport } from './ConsoleTransport.js';
|
|
3
|
+
export { FileTransport, type FileTransportOptions } from './FileTransport.js';
|
|
4
|
+
export { HttpTransport, type HttpTransportOptions } from './HttpTransport.js';
|
package/src/types/core.ts
CHANGED
|
@@ -247,4 +247,72 @@ export interface LogStyles {
|
|
|
247
247
|
location?: LogPartConfig;
|
|
248
248
|
backdrop?: string;
|
|
249
249
|
transparency?: number;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface TimerResult {
|
|
253
|
+
label: string;
|
|
254
|
+
duration: number;
|
|
255
|
+
startTime: number;
|
|
256
|
+
endTime: number;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface IScopedLogger {
|
|
260
|
+
debug(...args: any[]): void;
|
|
261
|
+
info(...args: any[]): void;
|
|
262
|
+
warn(...args: any[]): void;
|
|
263
|
+
error(...args: any[]): void;
|
|
264
|
+
success(...args: any[]): void;
|
|
265
|
+
critical(...args: any[]): void;
|
|
266
|
+
trace(...args: any[]): void;
|
|
267
|
+
|
|
268
|
+
badges(badges: string[]): this;
|
|
269
|
+
badge(badge: string): this;
|
|
270
|
+
clearBadges(): this;
|
|
271
|
+
|
|
272
|
+
time(label: string): void;
|
|
273
|
+
timeEnd(label: string): number | undefined;
|
|
274
|
+
|
|
275
|
+
style(presetName: string): this;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export interface IAPILogger extends IScopedLogger {
|
|
279
|
+
slow(message: string, duration?: number): void;
|
|
280
|
+
rateLimit(message: string): void;
|
|
281
|
+
auth(message: string): void;
|
|
282
|
+
deprecated(message: string): void;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export interface IComponentLogger extends IScopedLogger {
|
|
286
|
+
lifecycle(event: string, message?: string): void;
|
|
287
|
+
stateChange(from: string, to: string, data?: any): void;
|
|
288
|
+
propsChange(changes: Record<string, any>): void;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export interface Bindings {
|
|
292
|
+
scope?: string;
|
|
293
|
+
badges?: string[];
|
|
294
|
+
type?: 'scope' | 'api' | 'component';
|
|
295
|
+
context?: string[];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export type BadgeStyle = 'brackets' | 'rounded' | 'plain' | 'unicode' | 'pill';
|
|
299
|
+
|
|
300
|
+
export type TimestampFormat = 'iso' | 'time' | 'timeMs' | 'relative' | 'elapsed' | 'date' | 'custom';
|
|
301
|
+
|
|
302
|
+
export type ColumnAlign = 'left' | 'right' | 'center';
|
|
303
|
+
|
|
304
|
+
export interface ColumnConfig {
|
|
305
|
+
content: string;
|
|
306
|
+
width?: number;
|
|
307
|
+
align?: ColumnAlign;
|
|
308
|
+
color?: string;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export interface LogOptions {
|
|
312
|
+
rightAlign?: string;
|
|
313
|
+
columns?: ColumnConfig[];
|
|
314
|
+
maxWidth?: number;
|
|
315
|
+
keyValue?: boolean;
|
|
316
|
+
badgeStyle?: BadgeStyle;
|
|
317
|
+
timestampFormat?: TimestampFormat;
|
|
250
318
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { LogLevel, StackInfo } from './core.js';
|
|
2
|
+
|
|
3
|
+
export interface HookLogEntry {
|
|
4
|
+
level: LogLevel;
|
|
5
|
+
message: string;
|
|
6
|
+
args: any[];
|
|
7
|
+
timestamp: string;
|
|
8
|
+
prefix?: string;
|
|
9
|
+
stackInfo?: StackInfo;
|
|
10
|
+
correlationId?: string;
|
|
11
|
+
context?: Record<string, any>;
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type HookEvent = 'beforeLog' | 'afterLog' | 'onError';
|
|
16
|
+
|
|
17
|
+
export type HookCallback = (entry: HookLogEntry) => void | HookLogEntry | Promise<void | HookLogEntry>;
|
|
18
|
+
|
|
19
|
+
export type MiddlewareFn = (
|
|
20
|
+
entry: HookLogEntry,
|
|
21
|
+
next: () => void | Promise<void>
|
|
22
|
+
) => void | Promise<void>;
|
|
23
|
+
|
|
24
|
+
export interface HookRegistration {
|
|
25
|
+
id: string;
|
|
26
|
+
event: HookEvent;
|
|
27
|
+
callback: HookCallback;
|
|
28
|
+
priority: number;
|
|
29
|
+
once: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface MiddlewareRegistration {
|
|
33
|
+
id: string;
|
|
34
|
+
fn: MiddlewareFn;
|
|
35
|
+
priority: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface IHookManager {
|
|
39
|
+
on(event: HookEvent, callback: HookCallback, priority?: number): () => void;
|
|
40
|
+
once(event: HookEvent, callback: HookCallback, priority?: number): () => void;
|
|
41
|
+
off(event: HookEvent, callback: HookCallback): boolean;
|
|
42
|
+
use(middleware: MiddlewareFn, priority?: number): () => void;
|
|
43
|
+
emit(event: HookEvent, entry: HookLogEntry): Promise<HookLogEntry>;
|
|
44
|
+
process(entry: HookLogEntry): Promise<HookLogEntry>;
|
|
45
|
+
}
|