@mks2508/better-logger 0.0.1
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 +13 -0
- package/CLAUDE.md +113 -0
- package/dist/assets/index-DxvJByYN.js +183 -0
- package/dist/index.html +334 -0
- package/dist/vite.svg +1 -0
- package/index.html +334 -0
- package/package.json +35 -0
- package/public/vite.svg +1 -0
- package/src/Logger.ts +577 -0
- package/src/Logger.ts.backup +1684 -0
- package/src/cli/CommandProcessor.ts +77 -0
- package/src/cli/commands/ConfigCommand.ts +93 -0
- package/src/cli/commands/ExportCommand.ts +271 -0
- package/src/cli/commands/StatusCommand.ts +111 -0
- package/src/cli/commands/ThemeCommand.ts +88 -0
- package/src/cli/help.ts +127 -0
- package/src/cli/index.ts +59 -0
- package/src/constants.ts +89 -0
- package/src/example.ts +88 -0
- package/src/handlers/AnalyticsLogHandler.ts +22 -0
- package/src/handlers/ExportLogHandler.ts +447 -0
- package/src/handlers/FileLogHandler.ts +30 -0
- package/src/handlers/RemoteLogHandler.ts +42 -0
- package/src/handlers/index.ts +8 -0
- package/src/index.ts +122 -0
- package/src/main.ts +126 -0
- package/src/style.css +96 -0
- package/src/styling/StyleBuilder.ts +305 -0
- package/src/styling/banners.ts +168 -0
- package/src/styling/index.ts +12 -0
- package/src/styling/themes.ts +235 -0
- package/src/types/core.ts +80 -0
- package/src/types/handlers.ts +95 -0
- package/src/types/index.ts +29 -0
- package/src/typescript.svg +1 -0
- package/src/utils/index.ts +18 -0
- package/src/utils/output.ts +127 -0
- package/src/utils/stackTrace.ts +66 -0
- package/src/utils/timestamps.ts +80 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,1684 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview State-of-the-art Logger with advanced console styling and modern TypeScript patterns
|
|
3
|
+
* @version 2.0.0
|
|
4
|
+
* @author Bolt AI
|
|
5
|
+
* @date 2025
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Supported log levels in hierarchical order (debug < info < warn < error < critical)
|
|
10
|
+
*/
|
|
11
|
+
export const LOG_LEVELS = {
|
|
12
|
+
debug: 0,
|
|
13
|
+
info: 1,
|
|
14
|
+
warn: 2,
|
|
15
|
+
error: 3,
|
|
16
|
+
critical: 4,
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Log level type derived from LOG_LEVELS keys
|
|
21
|
+
*/
|
|
22
|
+
export type LogLevel = keyof typeof LOG_LEVELS;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Verbosity level type for filtering logs
|
|
26
|
+
*/
|
|
27
|
+
export type Verbosity = LogLevel | 'silent';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Theme variants for different visual styles
|
|
31
|
+
*/
|
|
32
|
+
export type ThemeVariant = 'default' | 'dark' | 'light' | 'neon' | 'minimal' | 'cyberpunk';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Banner types for different visual approaches
|
|
36
|
+
*/
|
|
37
|
+
export type BannerType = 'simple' | 'ascii' | 'unicode' | 'svg' | 'animated';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Configuration interface for logger instances
|
|
41
|
+
*/
|
|
42
|
+
interface LoggerConfig {
|
|
43
|
+
globalPrefix?: string;
|
|
44
|
+
verbosity: Verbosity;
|
|
45
|
+
enableColors: boolean;
|
|
46
|
+
enableTimestamps: boolean;
|
|
47
|
+
enableStackTrace: boolean;
|
|
48
|
+
theme?: ThemeVariant;
|
|
49
|
+
bannerType?: BannerType;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Interface for custom log handlers (extensibility)
|
|
54
|
+
*/
|
|
55
|
+
export interface ILogHandler {
|
|
56
|
+
handle(level: LogLevel, message: string, args: any[], metadata: LogMetadata): void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Metadata associated with each log entry
|
|
61
|
+
*/
|
|
62
|
+
interface LogMetadata {
|
|
63
|
+
timestamp: string;
|
|
64
|
+
level: LogLevel;
|
|
65
|
+
prefix?: string;
|
|
66
|
+
stackInfo?: StackInfo;
|
|
67
|
+
group?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parsed stack trace information
|
|
72
|
+
*/
|
|
73
|
+
interface StackInfo {
|
|
74
|
+
file: string;
|
|
75
|
+
line: number;
|
|
76
|
+
column: number;
|
|
77
|
+
function?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Timer entry for performance measurement
|
|
82
|
+
*/
|
|
83
|
+
interface TimerEntry {
|
|
84
|
+
label: string;
|
|
85
|
+
startTime: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Theme configurations for different visual styles
|
|
90
|
+
*/
|
|
91
|
+
const THEME_PRESETS = {
|
|
92
|
+
default: {
|
|
93
|
+
debug: {
|
|
94
|
+
emoji: 'š', label: 'DEBUG',
|
|
95
|
+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
96
|
+
color: '#ffffff', border: '1px solid #667eea',
|
|
97
|
+
shadow: '0 2px 4px rgba(102, 126, 234, 0.3)',
|
|
98
|
+
},
|
|
99
|
+
info: {
|
|
100
|
+
emoji: 'ā¹ļø', label: 'INFO',
|
|
101
|
+
background: 'linear-gradient(135deg, #74b9ff 0%, #0984e3 100%)',
|
|
102
|
+
color: '#ffffff', border: '1px solid #74b9ff',
|
|
103
|
+
shadow: '0 2px 4px rgba(116, 185, 255, 0.3)',
|
|
104
|
+
},
|
|
105
|
+
warn: {
|
|
106
|
+
emoji: 'ā ļø', label: 'WARN',
|
|
107
|
+
background: 'linear-gradient(135deg, #fdcb6e 0%, #e17055 100%)',
|
|
108
|
+
color: '#2d3436', border: '1px solid #fdcb6e',
|
|
109
|
+
shadow: '0 2px 4px rgba(253, 203, 110, 0.3)',
|
|
110
|
+
},
|
|
111
|
+
error: {
|
|
112
|
+
emoji: 'ā', label: 'ERROR',
|
|
113
|
+
background: 'linear-gradient(135deg, #e84393 0%, #d63031 100%)',
|
|
114
|
+
color: '#ffffff', border: '1px solid #e84393',
|
|
115
|
+
shadow: '0 2px 4px rgba(232, 67, 147, 0.3)',
|
|
116
|
+
},
|
|
117
|
+
success: {
|
|
118
|
+
emoji: 'ā
', label: 'SUCCESS',
|
|
119
|
+
background: 'linear-gradient(135deg, #00b894 0%, #00a085 100%)',
|
|
120
|
+
color: '#ffffff', border: '1px solid #00b894',
|
|
121
|
+
shadow: '0 2px 4px rgba(0, 184, 148, 0.3)',
|
|
122
|
+
},
|
|
123
|
+
critical: {
|
|
124
|
+
emoji: 'š„', label: 'CRITICAL',
|
|
125
|
+
background: 'linear-gradient(135deg, #ff3838 0%, #ff1744 100%)',
|
|
126
|
+
color: '#ffffff', border: '2px solid #ff3838',
|
|
127
|
+
shadow: '0 4px 8px rgba(255, 56, 56, 0.5)',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
dark: {
|
|
131
|
+
debug: {
|
|
132
|
+
emoji: 'š', label: 'DEBUG',
|
|
133
|
+
background: 'linear-gradient(135deg, #2d3748 0%, #4a5568 100%)',
|
|
134
|
+
color: '#e2e8f0', border: '1px solid #4a5568',
|
|
135
|
+
shadow: '0 2px 4px rgba(45, 55, 72, 0.8)',
|
|
136
|
+
},
|
|
137
|
+
info: {
|
|
138
|
+
emoji: 'š”', label: 'INFO',
|
|
139
|
+
background: 'linear-gradient(135deg, #1a202c 0%, #2d3748 100%)',
|
|
140
|
+
color: '#90cdf4', border: '1px solid #3182ce',
|
|
141
|
+
shadow: '0 2px 4px rgba(26, 32, 44, 0.8)',
|
|
142
|
+
},
|
|
143
|
+
warn: {
|
|
144
|
+
emoji: 'ā”', label: 'WARN',
|
|
145
|
+
background: 'linear-gradient(135deg, #744210 0%, #975a16 100%)',
|
|
146
|
+
color: '#faf089', border: '1px solid #d69e2e',
|
|
147
|
+
shadow: '0 2px 4px rgba(116, 66, 16, 0.8)',
|
|
148
|
+
},
|
|
149
|
+
error: {
|
|
150
|
+
emoji: 'š', label: 'ERROR',
|
|
151
|
+
background: 'linear-gradient(135deg, #742a2a 0%, #9b2c2c 100%)',
|
|
152
|
+
color: '#feb2b2', border: '1px solid #e53e3e',
|
|
153
|
+
shadow: '0 2px 4px rgba(116, 42, 42, 0.8)',
|
|
154
|
+
},
|
|
155
|
+
success: {
|
|
156
|
+
emoji: 'šÆ', label: 'SUCCESS',
|
|
157
|
+
background: 'linear-gradient(135deg, #276749 0%, #2f855a 100%)',
|
|
158
|
+
color: '#9ae6b4', border: '1px solid #38a169',
|
|
159
|
+
shadow: '0 2px 4px rgba(39, 103, 73, 0.8)',
|
|
160
|
+
},
|
|
161
|
+
critical: {
|
|
162
|
+
emoji: 'š„', label: 'CRITICAL',
|
|
163
|
+
background: 'linear-gradient(135deg, #1a1a1a 0%, #ff0000 100%)',
|
|
164
|
+
color: '#ffffff', border: '2px solid #ff0000',
|
|
165
|
+
shadow: '0 4px 8px rgba(255, 0, 0, 0.9)',
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
neon: {
|
|
169
|
+
debug: {
|
|
170
|
+
emoji: 'ā”', label: 'DEBUG',
|
|
171
|
+
background: 'linear-gradient(135deg, #0f3460 0%, #e94560 100%)',
|
|
172
|
+
color: '#00ffff', border: '1px solid #00ffff',
|
|
173
|
+
shadow: '0 0 10px rgba(0, 255, 255, 0.5)',
|
|
174
|
+
},
|
|
175
|
+
info: {
|
|
176
|
+
emoji: 'š®', label: 'INFO',
|
|
177
|
+
background: 'linear-gradient(135deg, #16213e 0%, #0f3460 100%)',
|
|
178
|
+
color: '#00ff41', border: '1px solid #00ff41',
|
|
179
|
+
shadow: '0 0 10px rgba(0, 255, 65, 0.5)',
|
|
180
|
+
},
|
|
181
|
+
warn: {
|
|
182
|
+
emoji: 'ā ļø', label: 'WARN',
|
|
183
|
+
background: 'linear-gradient(135deg, #533a03 0%, #e94560 100%)',
|
|
184
|
+
color: '#ffff00', border: '1px solid #ffff00',
|
|
185
|
+
shadow: '0 0 10px rgba(255, 255, 0, 0.5)',
|
|
186
|
+
},
|
|
187
|
+
error: {
|
|
188
|
+
emoji: 'š„', label: 'ERROR',
|
|
189
|
+
background: 'linear-gradient(135deg, #5c0a0a 0%, #ff073a 100%)',
|
|
190
|
+
color: '#ff073a', border: '1px solid #ff073a',
|
|
191
|
+
shadow: '0 0 10px rgba(255, 7, 58, 0.8)',
|
|
192
|
+
},
|
|
193
|
+
success: {
|
|
194
|
+
emoji: 'āØ', label: 'SUCCESS',
|
|
195
|
+
background: 'linear-gradient(135deg, #0a5c0a 0%, #39ff14 100%)',
|
|
196
|
+
color: '#39ff14', border: '1px solid #39ff14',
|
|
197
|
+
shadow: '0 0 10px rgba(57, 255, 20, 0.8)',
|
|
198
|
+
},
|
|
199
|
+
critical: {
|
|
200
|
+
emoji: 'š', label: 'CRITICAL',
|
|
201
|
+
background: 'linear-gradient(135deg, #000000 0%, #ff0080 100%)',
|
|
202
|
+
color: '#ff0080', border: '2px solid #ff0080',
|
|
203
|
+
shadow: '0 0 20px rgba(255, 0, 128, 1)',
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
minimal: {
|
|
207
|
+
debug: {
|
|
208
|
+
emoji: '', label: 'DEBUG',
|
|
209
|
+
background: '#f7fafc', color: '#4a5568',
|
|
210
|
+
border: '1px solid #e2e8f0', shadow: 'none',
|
|
211
|
+
},
|
|
212
|
+
info: {
|
|
213
|
+
emoji: '', label: 'INFO',
|
|
214
|
+
background: '#ebf8ff', color: '#2b6cb0',
|
|
215
|
+
border: '1px solid #bee3f8', shadow: 'none',
|
|
216
|
+
},
|
|
217
|
+
warn: {
|
|
218
|
+
emoji: '', label: 'WARN',
|
|
219
|
+
background: '#fffbf0', color: '#c05621',
|
|
220
|
+
border: '1px solid #fed7aa', shadow: 'none',
|
|
221
|
+
},
|
|
222
|
+
error: {
|
|
223
|
+
emoji: '', label: 'ERROR',
|
|
224
|
+
background: '#fef5f5', color: '#c53030',
|
|
225
|
+
border: '1px solid #fca5a5', shadow: 'none',
|
|
226
|
+
},
|
|
227
|
+
success: {
|
|
228
|
+
emoji: '', label: 'SUCCESS',
|
|
229
|
+
background: '#f0fff4', color: '#2f855a',
|
|
230
|
+
border: '1px solid #9ae6b4', shadow: 'none',
|
|
231
|
+
},
|
|
232
|
+
critical: {
|
|
233
|
+
emoji: '', label: 'CRITICAL',
|
|
234
|
+
background: '#fef5f5', color: '#e53e3e',
|
|
235
|
+
border: '2px solid #f56565', shadow: 'none',
|
|
236
|
+
},
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Current active theme styles
|
|
242
|
+
*/
|
|
243
|
+
let LEVEL_STYLES = THEME_PRESETS.default;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Theme-specific banners for enhanced visual theming
|
|
247
|
+
*/
|
|
248
|
+
const THEME_BANNERS = {
|
|
249
|
+
default: {
|
|
250
|
+
simple: 'š ADVANCED LOGGER v2.0.0 - State-of-the-art Console Styling š',
|
|
251
|
+
style: 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 20px; border-radius: 8px; font-weight: bold;'
|
|
252
|
+
},
|
|
253
|
+
dark: {
|
|
254
|
+
simple: 'š ADVANCED LOGGER v2.0.0 - Dark Mode Console Excellence š',
|
|
255
|
+
style: 'background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%); color: #e2e8f0; padding: 12px 20px; border-radius: 8px; font-weight: bold; border: 1px solid #4a5568;'
|
|
256
|
+
},
|
|
257
|
+
neon: {
|
|
258
|
+
simple: 'ā” ADVANCED LOGGER v2.0.0 - Cyberpunk Console Experience ā”',
|
|
259
|
+
style: 'background: linear-gradient(135deg, #0f3460 0%, #e94560 100%); color: #00ffff; padding: 12px 20px; border-radius: 8px; font-weight: bold; text-shadow: 0 0 10px #00ffff;'
|
|
260
|
+
},
|
|
261
|
+
minimal: {
|
|
262
|
+
simple: 'ADVANCED LOGGER v2.0.0 - Clean Console Styling',
|
|
263
|
+
style: 'background: #f7fafc; color: #2d3748; padding: 8px 16px; border: 1px solid #e2e8f0; border-radius: 4px; font-weight: 500;'
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Utility class for creating dynamic console styles with method chaining
|
|
269
|
+
*/
|
|
270
|
+
class StyleBuilder {
|
|
271
|
+
private styles: string[] = [];
|
|
272
|
+
|
|
273
|
+
constructor(baseStyle = '') {
|
|
274
|
+
if (baseStyle) this.styles.push(baseStyle);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Add background color or gradient
|
|
279
|
+
*/
|
|
280
|
+
bg(background: string): StyleBuilder {
|
|
281
|
+
this.styles.push(`background: ${background}`);
|
|
282
|
+
return this;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Add text color
|
|
287
|
+
*/
|
|
288
|
+
color(color: string): StyleBuilder {
|
|
289
|
+
this.styles.push(`color: ${color}`);
|
|
290
|
+
return this;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Add border styling
|
|
295
|
+
*/
|
|
296
|
+
border(border: string): StyleBuilder {
|
|
297
|
+
this.styles.push(`border: ${border}`);
|
|
298
|
+
return this;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Add box shadow
|
|
303
|
+
*/
|
|
304
|
+
shadow(shadow: string): StyleBuilder {
|
|
305
|
+
this.styles.push(`box-shadow: ${shadow}`);
|
|
306
|
+
return this;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Add padding
|
|
311
|
+
*/
|
|
312
|
+
padding(padding: string): StyleBuilder {
|
|
313
|
+
this.styles.push(`padding: ${padding}`);
|
|
314
|
+
return this;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Add border radius
|
|
319
|
+
*/
|
|
320
|
+
rounded(radius: string = '4px'): StyleBuilder {
|
|
321
|
+
this.styles.push(`border-radius: ${radius}`);
|
|
322
|
+
return this;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Add font weight
|
|
327
|
+
*/
|
|
328
|
+
bold(): StyleBuilder {
|
|
329
|
+
this.styles.push('font-weight: bold');
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Add font styling
|
|
335
|
+
*/
|
|
336
|
+
font(font: string): StyleBuilder {
|
|
337
|
+
this.styles.push(`font-family: ${font}`);
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Add font size
|
|
343
|
+
*/
|
|
344
|
+
size(size: string): StyleBuilder {
|
|
345
|
+
this.styles.push(`font-size: ${size}`);
|
|
346
|
+
return this;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Build the final CSS string
|
|
351
|
+
*/
|
|
352
|
+
build(): string {
|
|
353
|
+
return this.styles.join('; ');
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Proxy-based dynamic styler for chainable console styling
|
|
359
|
+
*/
|
|
360
|
+
const createStyler = (): any => {
|
|
361
|
+
const builder = new StyleBuilder();
|
|
362
|
+
return new Proxy(builder, {
|
|
363
|
+
get(target: StyleBuilder, prop: string) {
|
|
364
|
+
if (prop in target) {
|
|
365
|
+
const method = (target as any)[prop];
|
|
366
|
+
if (typeof method === 'function') {
|
|
367
|
+
return method.bind(target);
|
|
368
|
+
}
|
|
369
|
+
return method;
|
|
370
|
+
}
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Dynamic styler instance for external use
|
|
378
|
+
*/
|
|
379
|
+
export const $ = createStyler();
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Parses the current stack trace to extract caller information
|
|
383
|
+
*/
|
|
384
|
+
function parseStackTrace(): StackInfo | null {
|
|
385
|
+
try {
|
|
386
|
+
const stack = new Error().stack;
|
|
387
|
+
if (!stack) return null;
|
|
388
|
+
|
|
389
|
+
const lines = stack.split('\n').filter(line => line.trim());
|
|
390
|
+
|
|
391
|
+
// Find the first caller that's not from Logger methods
|
|
392
|
+
for (let i = 1; i < lines.length; i++) {
|
|
393
|
+
const line = lines[i];
|
|
394
|
+
|
|
395
|
+
// Skip if line contains Logger methods or parseStackTrace
|
|
396
|
+
if (line.includes('parseStackTrace') ||
|
|
397
|
+
line.includes('Logger.') ||
|
|
398
|
+
line.includes('.log(') ||
|
|
399
|
+
line.includes('createStyledOutput')) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Parse different stack trace formats
|
|
404
|
+
let match;
|
|
405
|
+
|
|
406
|
+
// Chrome format: "at functionName (file:line:column)" or "at file:line:column"
|
|
407
|
+
const chromeMatch = line.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/);
|
|
408
|
+
if (chromeMatch) {
|
|
409
|
+
match = chromeMatch;
|
|
410
|
+
} else {
|
|
411
|
+
// Firefox format: "functionName@file:line:column"
|
|
412
|
+
const firefoxMatch = line.match(/(.+?)@(.+?):(\d+):(\d+)$/);
|
|
413
|
+
if (firefoxMatch) {
|
|
414
|
+
match = firefoxMatch;
|
|
415
|
+
} else {
|
|
416
|
+
// Safari/other formats
|
|
417
|
+
const safariMatch = line.match(/(\S+)?@(.+?):(\d+):(\d+)$/);
|
|
418
|
+
if (safariMatch) {
|
|
419
|
+
match = safariMatch;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (match) {
|
|
425
|
+
const [, functionName, file, line, column] = match;
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
file: file?.split('/').pop()?.split('?')[0] || 'unknown',
|
|
429
|
+
line: parseInt(line, 10) || 0,
|
|
430
|
+
column: parseInt(column, 10) || 0,
|
|
431
|
+
function: functionName?.trim() || undefined,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return null;
|
|
437
|
+
} catch {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Formats the timestamp using modern Date API
|
|
444
|
+
*/
|
|
445
|
+
function formatTimestamp(): string {
|
|
446
|
+
try {
|
|
447
|
+
// Use modern Temporal API if available, fallback to Date
|
|
448
|
+
const now = new Date();
|
|
449
|
+
return now.toISOString();
|
|
450
|
+
} catch {
|
|
451
|
+
return new Date().toISOString();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Creates styled console output with multiple %c formatters
|
|
457
|
+
*/
|
|
458
|
+
function createStyledOutput(
|
|
459
|
+
level: LogLevel,
|
|
460
|
+
prefix: string | undefined,
|
|
461
|
+
message: string,
|
|
462
|
+
stackInfo: StackInfo | null
|
|
463
|
+
): [string, ...string[]] {
|
|
464
|
+
const levelConfig = LEVEL_STYLES[level];
|
|
465
|
+
const timestamp = formatTimestamp();
|
|
466
|
+
|
|
467
|
+
// Base styles
|
|
468
|
+
const timestampStyle = new StyleBuilder()
|
|
469
|
+
.color('#666')
|
|
470
|
+
.size('11px')
|
|
471
|
+
.font('Monaco, Consolas, monospace')
|
|
472
|
+
.build();
|
|
473
|
+
|
|
474
|
+
const levelStyle = new StyleBuilder()
|
|
475
|
+
.bg(levelConfig.background)
|
|
476
|
+
.color(levelConfig.color)
|
|
477
|
+
.border(levelConfig.border)
|
|
478
|
+
.shadow(levelConfig.shadow)
|
|
479
|
+
.padding('2px 8px')
|
|
480
|
+
.rounded('4px')
|
|
481
|
+
.bold()
|
|
482
|
+
.font('Monaco, Consolas, monospace')
|
|
483
|
+
.size('12px')
|
|
484
|
+
.build();
|
|
485
|
+
|
|
486
|
+
const prefixStyle = new StyleBuilder()
|
|
487
|
+
.bg('#2d3748')
|
|
488
|
+
.color('#e2e8f0')
|
|
489
|
+
.padding('2px 6px')
|
|
490
|
+
.rounded('3px')
|
|
491
|
+
.bold()
|
|
492
|
+
.font('Monaco, Consolas, monospace')
|
|
493
|
+
.size('11px')
|
|
494
|
+
.build();
|
|
495
|
+
|
|
496
|
+
const messageStyle = new StyleBuilder()
|
|
497
|
+
.color('#2d3748')
|
|
498
|
+
.font('system-ui, -apple-system, sans-serif')
|
|
499
|
+
.size('14px')
|
|
500
|
+
.build();
|
|
501
|
+
|
|
502
|
+
const locationStyle = new StyleBuilder()
|
|
503
|
+
.color('#718096')
|
|
504
|
+
.size('11px')
|
|
505
|
+
.font('Monaco, Consolas, monospace')
|
|
506
|
+
.build();
|
|
507
|
+
|
|
508
|
+
// Build format string and styles
|
|
509
|
+
let format = `%c${timestamp.slice(11, 23)} %c${levelConfig.emoji} ${levelConfig.label}`;
|
|
510
|
+
const styles = [timestampStyle, levelStyle];
|
|
511
|
+
|
|
512
|
+
if (prefix) {
|
|
513
|
+
format += ` %c${prefix}`;
|
|
514
|
+
styles.push(prefixStyle);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
format += ` %c${message}`;
|
|
518
|
+
styles.push(messageStyle);
|
|
519
|
+
|
|
520
|
+
if (stackInfo) {
|
|
521
|
+
format += ` %c(${stackInfo.file}:${stackInfo.line}:${stackInfo.column})`;
|
|
522
|
+
styles.push(locationStyle);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return [format, ...styles];
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Main Logger class implementing state-of-the-art logging capabilities
|
|
530
|
+
*/
|
|
531
|
+
export class Logger {
|
|
532
|
+
private config: LoggerConfig;
|
|
533
|
+
private scopedPrefix?: string;
|
|
534
|
+
private handlers: ILogHandler[] = [];
|
|
535
|
+
private timers: Map<string, TimerEntry> = new Map();
|
|
536
|
+
private groupDepth: number = 0;
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Creates a new Logger instance
|
|
540
|
+
* @param config - Configuration options for the logger
|
|
541
|
+
*/
|
|
542
|
+
constructor(config: Partial<LoggerConfig> = {}) {
|
|
543
|
+
this.config = {
|
|
544
|
+
verbosity: 'info',
|
|
545
|
+
enableColors: true,
|
|
546
|
+
enableTimestamps: true,
|
|
547
|
+
enableStackTrace: true,
|
|
548
|
+
theme: 'default',
|
|
549
|
+
bannerType: 'simple',
|
|
550
|
+
...config,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Sets the global prefix for all log messages
|
|
556
|
+
* @param prefix - The prefix to add to all log messages
|
|
557
|
+
*/
|
|
558
|
+
setGlobalPrefix(prefix: string): void {
|
|
559
|
+
this.config.globalPrefix = prefix;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Creates a scoped logger with a specific prefix
|
|
564
|
+
* @param prefix - The prefix for the scoped logger
|
|
565
|
+
* @returns A new Logger instance with the specified prefix
|
|
566
|
+
*/
|
|
567
|
+
createScopedLogger(prefix: string): Logger {
|
|
568
|
+
const scopedLogger = new Logger(this.config);
|
|
569
|
+
scopedLogger.scopedPrefix = prefix;
|
|
570
|
+
scopedLogger.handlers = [...this.handlers];
|
|
571
|
+
return scopedLogger;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Sets the verbosity level for filtering log output
|
|
576
|
+
* @param level - The minimum log level to output
|
|
577
|
+
*/
|
|
578
|
+
setVerbosity(level: Verbosity): void {
|
|
579
|
+
this.config.verbosity = level;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Adds a custom log handler for extensibility
|
|
584
|
+
* @param handler - The log handler to add
|
|
585
|
+
*/
|
|
586
|
+
addHandler(handler: ILogHandler): void {
|
|
587
|
+
this.handlers.push(handler);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Checks if a log level should be output based on current verbosity
|
|
592
|
+
* @private
|
|
593
|
+
*/
|
|
594
|
+
private shouldLog(level: LogLevel): boolean {
|
|
595
|
+
if (this.config.verbosity === 'silent') return false;
|
|
596
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[this.config.verbosity];
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Gets the effective prefix (global + scoped)
|
|
601
|
+
* @private
|
|
602
|
+
*/
|
|
603
|
+
private getEffectivePrefix(): string | undefined {
|
|
604
|
+
const parts = [this.config.globalPrefix, this.scopedPrefix].filter(Boolean);
|
|
605
|
+
return parts.length > 0 ? parts.join(':') : undefined;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Core logging method that handles styling and formatting
|
|
610
|
+
* @private
|
|
611
|
+
*/
|
|
612
|
+
private log(level: LogLevel, ...args: any[]): void {
|
|
613
|
+
if (!this.shouldLog(level)) return;
|
|
614
|
+
|
|
615
|
+
const stackInfo = this.config.enableStackTrace ? parseStackTrace() : null;
|
|
616
|
+
const prefix = this.getEffectivePrefix();
|
|
617
|
+
const message = args.length > 0 ? String(args[0]) : '';
|
|
618
|
+
const additionalArgs = args.slice(1);
|
|
619
|
+
|
|
620
|
+
// Create styled output
|
|
621
|
+
const [format, ...styles] = createStyledOutput(level, prefix, message, stackInfo);
|
|
622
|
+
|
|
623
|
+
// Add group indentation
|
|
624
|
+
const groupIndent = ' '.repeat(this.groupDepth);
|
|
625
|
+
const finalFormat = groupIndent + format;
|
|
626
|
+
|
|
627
|
+
// Output to console
|
|
628
|
+
if (additionalArgs.length > 0) {
|
|
629
|
+
console.log(finalFormat, ...styles, ...additionalArgs);
|
|
630
|
+
} else {
|
|
631
|
+
console.log(finalFormat, ...styles);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Call custom handlers
|
|
635
|
+
const metadata: LogMetadata = {
|
|
636
|
+
timestamp: formatTimestamp(),
|
|
637
|
+
level,
|
|
638
|
+
prefix,
|
|
639
|
+
stackInfo: stackInfo || undefined,
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
this.handlers.forEach(handler => {
|
|
643
|
+
try {
|
|
644
|
+
handler.handle(level, message, args, metadata);
|
|
645
|
+
} catch (error) {
|
|
646
|
+
console.error('Log handler failed:', error);
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Logs debug information (lowest priority)
|
|
653
|
+
* @param args - Arguments to log
|
|
654
|
+
*/
|
|
655
|
+
debug(...args: any[]): void {
|
|
656
|
+
this.log('debug', ...args);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Logs informational messages
|
|
661
|
+
* @param args - Arguments to log
|
|
662
|
+
*/
|
|
663
|
+
info(...args: any[]): void {
|
|
664
|
+
this.log('info', ...args);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Logs warning messages
|
|
669
|
+
* @param args - Arguments to log
|
|
670
|
+
*/
|
|
671
|
+
warn(...args: any[]): void {
|
|
672
|
+
this.log('warn', ...args);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Logs error messages
|
|
677
|
+
* @param args - Arguments to log
|
|
678
|
+
*/
|
|
679
|
+
error(...args: any[]): void {
|
|
680
|
+
this.log('error', ...args);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Logs success messages (special info level)
|
|
685
|
+
* @param args - Arguments to log
|
|
686
|
+
*/
|
|
687
|
+
success(...args: any[]): void {
|
|
688
|
+
if (!this.shouldLog('info')) return;
|
|
689
|
+
|
|
690
|
+
const stackInfo = this.config.enableStackTrace ? parseStackTrace() : null;
|
|
691
|
+
const prefix = this.getEffectivePrefix();
|
|
692
|
+
const message = args.length > 0 ? String(args[0]) : '';
|
|
693
|
+
const additionalArgs = args.slice(1);
|
|
694
|
+
|
|
695
|
+
const successConfig = LEVEL_STYLES.success;
|
|
696
|
+
const timestamp = formatTimestamp();
|
|
697
|
+
|
|
698
|
+
const timestampStyle = new StyleBuilder()
|
|
699
|
+
.color('#666')
|
|
700
|
+
.size('11px')
|
|
701
|
+
.font('Monaco, Consolas, monospace')
|
|
702
|
+
.build();
|
|
703
|
+
|
|
704
|
+
const levelStyle = new StyleBuilder()
|
|
705
|
+
.bg(successConfig.background)
|
|
706
|
+
.color(successConfig.color)
|
|
707
|
+
.border(successConfig.border)
|
|
708
|
+
.shadow(successConfig.shadow)
|
|
709
|
+
.padding('2px 8px')
|
|
710
|
+
.rounded('4px')
|
|
711
|
+
.bold()
|
|
712
|
+
.font('Monaco, Consolas, monospace')
|
|
713
|
+
.size('12px')
|
|
714
|
+
.build();
|
|
715
|
+
|
|
716
|
+
const prefixStyle = new StyleBuilder()
|
|
717
|
+
.bg('#2d3748')
|
|
718
|
+
.color('#e2e8f0')
|
|
719
|
+
.padding('2px 6px')
|
|
720
|
+
.rounded('3px')
|
|
721
|
+
.bold()
|
|
722
|
+
.font('Monaco, Consolas, monospace')
|
|
723
|
+
.size('11px')
|
|
724
|
+
.build();
|
|
725
|
+
|
|
726
|
+
const messageStyle = new StyleBuilder()
|
|
727
|
+
.color('#2d3748')
|
|
728
|
+
.font('system-ui, -apple-system, sans-serif')
|
|
729
|
+
.size('14px')
|
|
730
|
+
.build();
|
|
731
|
+
|
|
732
|
+
const locationStyle = new StyleBuilder()
|
|
733
|
+
.color('#718096')
|
|
734
|
+
.size('11px')
|
|
735
|
+
.font('Monaco, Consolas, monospace')
|
|
736
|
+
.build();
|
|
737
|
+
|
|
738
|
+
let format = `%c${timestamp.slice(11, 23)} %c${successConfig.emoji} ${successConfig.label}`;
|
|
739
|
+
const styles = [timestampStyle, levelStyle];
|
|
740
|
+
|
|
741
|
+
if (prefix) {
|
|
742
|
+
format += ` %c${prefix}`;
|
|
743
|
+
styles.push(prefixStyle);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
format += ` %c${message}`;
|
|
747
|
+
styles.push(messageStyle);
|
|
748
|
+
|
|
749
|
+
if (stackInfo) {
|
|
750
|
+
format += ` %c(${stackInfo.file}:${stackInfo.line}:${stackInfo.column})`;
|
|
751
|
+
styles.push(locationStyle);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const groupIndent = ' '.repeat(this.groupDepth);
|
|
755
|
+
const finalFormat = groupIndent + format;
|
|
756
|
+
|
|
757
|
+
if (additionalArgs.length > 0) {
|
|
758
|
+
console.log(finalFormat, ...styles, ...additionalArgs);
|
|
759
|
+
} else {
|
|
760
|
+
console.log(finalFormat, ...styles);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const metadata: LogMetadata = {
|
|
764
|
+
timestamp: formatTimestamp(),
|
|
765
|
+
level: 'info',
|
|
766
|
+
prefix,
|
|
767
|
+
stackInfo: stackInfo || undefined,
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
this.handlers.forEach(handler => {
|
|
771
|
+
try {
|
|
772
|
+
handler.handle('info', message, args, metadata);
|
|
773
|
+
} catch (error) {
|
|
774
|
+
console.error('Log handler failed:', error);
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Logs trace information (detailed debugging)
|
|
781
|
+
* @param args - Arguments to log
|
|
782
|
+
*/
|
|
783
|
+
trace(...args: any[]): void {
|
|
784
|
+
this.log('debug', ...args);
|
|
785
|
+
if (this.shouldLog('debug')) {
|
|
786
|
+
console.trace(...args);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* Logs critical errors (highest priority)
|
|
792
|
+
* @param args - Arguments to log
|
|
793
|
+
*/
|
|
794
|
+
critical(...args: any[]): void {
|
|
795
|
+
this.log('critical', ...args);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Displays data in a table format
|
|
800
|
+
* @param data - The data to display in table format
|
|
801
|
+
* @param columns - Optional column names to display
|
|
802
|
+
*/
|
|
803
|
+
table(data: any, columns?: string[]): void {
|
|
804
|
+
if (!this.shouldLog('info')) return;
|
|
805
|
+
|
|
806
|
+
const prefix = this.getEffectivePrefix();
|
|
807
|
+
const tableStyle = new StyleBuilder()
|
|
808
|
+
.bg('linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%)')
|
|
809
|
+
.color('#495057')
|
|
810
|
+
.border('1px solid #dee2e6')
|
|
811
|
+
.padding('4px 8px')
|
|
812
|
+
.rounded('4px')
|
|
813
|
+
.bold()
|
|
814
|
+
.build();
|
|
815
|
+
|
|
816
|
+
const format = `%cš TABLE${prefix ? ` [${prefix}]` : ''}`;
|
|
817
|
+
console.log(format, tableStyle);
|
|
818
|
+
|
|
819
|
+
if (columns) {
|
|
820
|
+
console.table(data, columns);
|
|
821
|
+
} else {
|
|
822
|
+
console.table(data);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Starts a collapsible group in the console
|
|
828
|
+
* @param label - The label for the group
|
|
829
|
+
* @param collapsed - Whether the group should start collapsed
|
|
830
|
+
*/
|
|
831
|
+
group(label: string, collapsed: boolean = false): void {
|
|
832
|
+
const groupStyle = new StyleBuilder()
|
|
833
|
+
.bg('linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%)')
|
|
834
|
+
.color('#1565c0')
|
|
835
|
+
.border('1px solid #90caf9')
|
|
836
|
+
.padding('4px 12px')
|
|
837
|
+
.rounded('6px')
|
|
838
|
+
.bold()
|
|
839
|
+
.build();
|
|
840
|
+
|
|
841
|
+
const format = `%cš ${label}`;
|
|
842
|
+
|
|
843
|
+
if (collapsed) {
|
|
844
|
+
console.groupCollapsed(format, groupStyle);
|
|
845
|
+
} else {
|
|
846
|
+
console.group(format, groupStyle);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
this.groupDepth++;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Ends the current console group
|
|
854
|
+
*/
|
|
855
|
+
groupEnd(): void {
|
|
856
|
+
if (this.groupDepth > 0) {
|
|
857
|
+
console.groupEnd();
|
|
858
|
+
this.groupDepth--;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Starts a timer with the given label
|
|
864
|
+
* @param label - The timer label
|
|
865
|
+
*/
|
|
866
|
+
time(label: string): void {
|
|
867
|
+
const timer: TimerEntry = {
|
|
868
|
+
label,
|
|
869
|
+
startTime: performance.now(),
|
|
870
|
+
};
|
|
871
|
+
this.timers.set(label, timer);
|
|
872
|
+
|
|
873
|
+
const timerStyle = new StyleBuilder()
|
|
874
|
+
.bg('linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%)')
|
|
875
|
+
.color('#856404')
|
|
876
|
+
.border('1px solid #ffeaa7')
|
|
877
|
+
.padding('2px 6px')
|
|
878
|
+
.rounded('3px')
|
|
879
|
+
.font('Monaco, Consolas, monospace')
|
|
880
|
+
.size('12px')
|
|
881
|
+
.build();
|
|
882
|
+
|
|
883
|
+
console.log(`%cā±ļø Timer started: ${label}`, timerStyle);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Ends a timer and logs the elapsed time
|
|
888
|
+
* @param label - The timer label to end
|
|
889
|
+
*/
|
|
890
|
+
timeEnd(label: string): void {
|
|
891
|
+
const timer = this.timers.get(label);
|
|
892
|
+
if (!timer) {
|
|
893
|
+
this.warn(`Timer '${label}' does not exist`);
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
const elapsed = performance.now() - timer.startTime;
|
|
898
|
+
this.timers.delete(label);
|
|
899
|
+
|
|
900
|
+
const timerStyle = new StyleBuilder()
|
|
901
|
+
.bg('linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%)')
|
|
902
|
+
.color('#155724')
|
|
903
|
+
.border('1px solid #c3e6cb')
|
|
904
|
+
.padding('2px 6px')
|
|
905
|
+
.rounded('3px')
|
|
906
|
+
.font('Monaco, Consolas, monospace')
|
|
907
|
+
.size('12px')
|
|
908
|
+
.bold()
|
|
909
|
+
.build();
|
|
910
|
+
|
|
911
|
+
console.log(`%cā±ļø Timer ended: ${label} - ${elapsed.toFixed(2)}ms`, timerStyle);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Logs with custom SVG animation (experimental feature)
|
|
916
|
+
* @param message - The message to log
|
|
917
|
+
* @param svgAnimation - SVG with animation as data URI
|
|
918
|
+
*/
|
|
919
|
+
logWithAnimation(message: string, svgAnimation?: string): void {
|
|
920
|
+
if (svgAnimation) {
|
|
921
|
+
const animatedStyle = new StyleBuilder()
|
|
922
|
+
.bg(`url("${svgAnimation}") no-repeat left center`)
|
|
923
|
+
.padding('20px 20px 20px 40px')
|
|
924
|
+
.border('2px solid #ff6b6b')
|
|
925
|
+
.rounded('8px')
|
|
926
|
+
.color('#2d3748')
|
|
927
|
+
.bold()
|
|
928
|
+
.build();
|
|
929
|
+
|
|
930
|
+
console.log(`%c${message}`, animatedStyle);
|
|
931
|
+
} else {
|
|
932
|
+
this.info(message);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Groups logs by a specific property using modern Object.groupBy (when available)
|
|
938
|
+
* @param items - Items to group and log
|
|
939
|
+
* @param groupBy - Function to extract grouping key
|
|
940
|
+
*/
|
|
941
|
+
logGrouped<T>(items: T[], groupBy: (item: T) => string): void {
|
|
942
|
+
try {
|
|
943
|
+
// Use Object.groupBy if available (ES2024)
|
|
944
|
+
const grouped = (Object as any).groupBy?.(items, groupBy) ||
|
|
945
|
+
items.reduce((acc, item) => {
|
|
946
|
+
const key = groupBy(item);
|
|
947
|
+
if (!acc[key]) acc[key] = [];
|
|
948
|
+
acc[key].push(item);
|
|
949
|
+
return acc;
|
|
950
|
+
}, {} as Record<string, T[]>);
|
|
951
|
+
|
|
952
|
+
Object.entries(grouped).forEach(([group, groupItems]) => {
|
|
953
|
+
this.group(`Group: ${group}`);
|
|
954
|
+
this.table(groupItems);
|
|
955
|
+
this.groupEnd();
|
|
956
|
+
});
|
|
957
|
+
} catch {
|
|
958
|
+
// Fallback to simple logging
|
|
959
|
+
this.info('Grouped data:', items);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Sets the logger theme
|
|
965
|
+
* @param theme - Theme variant to apply
|
|
966
|
+
*/
|
|
967
|
+
setTheme(theme: ThemeVariant): void {
|
|
968
|
+
if (theme in THEME_PRESETS) {
|
|
969
|
+
LEVEL_STYLES = (THEME_PRESETS as any)[theme];
|
|
970
|
+
this.config.theme = theme;
|
|
971
|
+
|
|
972
|
+
// Show theme-specific banner
|
|
973
|
+
if (theme in THEME_BANNERS) {
|
|
974
|
+
const themeBanner = (THEME_BANNERS as any)[theme];
|
|
975
|
+
console.log(`%c${themeBanner.simple}`, themeBanner.style);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
this.success(`Theme changed to: ${theme}`);
|
|
979
|
+
} else {
|
|
980
|
+
this.error(`Invalid theme: ${theme}. Available themes:`, Object.keys(THEME_PRESETS));
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* CLI command processor for logger configuration
|
|
986
|
+
* @param command - Command string starting with /
|
|
987
|
+
*/
|
|
988
|
+
cli(command: string): void {
|
|
989
|
+
if (!command.startsWith('/')) {
|
|
990
|
+
this.error('Invalid command. Commands must start with /');
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
const parts = command.slice(1).split(' ');
|
|
995
|
+
const cmd = parts[0];
|
|
996
|
+
const args = parts.slice(1).join(' ');
|
|
997
|
+
|
|
998
|
+
switch (cmd) {
|
|
999
|
+
case 'config':
|
|
1000
|
+
this.handleConfigCommand(args);
|
|
1001
|
+
break;
|
|
1002
|
+
case 'help':
|
|
1003
|
+
this.showHelp();
|
|
1004
|
+
break;
|
|
1005
|
+
case 'themes':
|
|
1006
|
+
this.showThemes();
|
|
1007
|
+
break;
|
|
1008
|
+
case 'banners':
|
|
1009
|
+
this.showBanners();
|
|
1010
|
+
break;
|
|
1011
|
+
case 'banner':
|
|
1012
|
+
this.handleBannerCommand(args);
|
|
1013
|
+
break;
|
|
1014
|
+
case 'status':
|
|
1015
|
+
this.showStatus();
|
|
1016
|
+
break;
|
|
1017
|
+
case 'reset':
|
|
1018
|
+
this.resetConfig();
|
|
1019
|
+
break;
|
|
1020
|
+
case 'demo':
|
|
1021
|
+
this.showDemo();
|
|
1022
|
+
break;
|
|
1023
|
+
default:
|
|
1024
|
+
this.error(`Unknown command: ${cmd}. Type /help for available commands.`);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Handle /config command with JSON or key-value pairs
|
|
1030
|
+
* @private
|
|
1031
|
+
*/
|
|
1032
|
+
private handleConfigCommand(args: string): void {
|
|
1033
|
+
if (!args) {
|
|
1034
|
+
this.showStatus();
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
try {
|
|
1039
|
+
// Try to parse as JSON first
|
|
1040
|
+
if (args.startsWith('{')) {
|
|
1041
|
+
const config = JSON.parse(args);
|
|
1042
|
+
this.applyConfig(config);
|
|
1043
|
+
} else {
|
|
1044
|
+
// Parse key=value pairs
|
|
1045
|
+
const pairs = args.split(',').map(pair => pair.trim().split('='));
|
|
1046
|
+
const config: any = {};
|
|
1047
|
+
pairs.forEach(([key, value]) => {
|
|
1048
|
+
if (key && value) {
|
|
1049
|
+
config[key.trim()] = value.trim().replace(/["']/g, '');
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
this.applyConfig(config);
|
|
1053
|
+
}
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
this.error('Invalid config format. Use JSON or key=value pairs:', error);
|
|
1056
|
+
this.info('Examples: /config {"theme":"dark"} or /config theme=neon,verbosity=debug');
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* Apply configuration object to logger
|
|
1062
|
+
* @private
|
|
1063
|
+
*/
|
|
1064
|
+
private applyConfig(config: any): void {
|
|
1065
|
+
const validKeys = ['theme', 'verbosity', 'enableColors', 'enableTimestamps', 'enableStackTrace', 'globalPrefix', 'bannerType'];
|
|
1066
|
+
const applied: string[] = [];
|
|
1067
|
+
|
|
1068
|
+
Object.entries(config).forEach(([key, value]) => {
|
|
1069
|
+
if (validKeys.includes(key)) {
|
|
1070
|
+
if (key === 'theme' && typeof value === 'string') {
|
|
1071
|
+
this.setTheme(value as ThemeVariant);
|
|
1072
|
+
applied.push(`${key}=${value}`);
|
|
1073
|
+
} else if (key === 'bannerType' && typeof value === 'string') {
|
|
1074
|
+
this.setBannerType(value as BannerType);
|
|
1075
|
+
applied.push(`${key}=${value}`);
|
|
1076
|
+
} else if (key === 'verbosity') {
|
|
1077
|
+
this.setVerbosity(value as Verbosity);
|
|
1078
|
+
applied.push(`${key}=${value}`);
|
|
1079
|
+
} else if (key === 'globalPrefix') {
|
|
1080
|
+
this.setGlobalPrefix(value as string);
|
|
1081
|
+
applied.push(`${key}=${value}`);
|
|
1082
|
+
} else {
|
|
1083
|
+
(this.config as any)[key] = value;
|
|
1084
|
+
applied.push(`${key}=${value}`);
|
|
1085
|
+
}
|
|
1086
|
+
} else {
|
|
1087
|
+
this.warn(`Invalid config key: ${key}`);
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
if (applied.length > 0) {
|
|
1092
|
+
this.success(`Configuration updated: ${applied.join(', ')}`);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* Show CLI help information
|
|
1098
|
+
* @private
|
|
1099
|
+
*/
|
|
1100
|
+
private showHelp(): void {
|
|
1101
|
+
const helpStyle = new StyleBuilder()
|
|
1102
|
+
.bg('linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%)')
|
|
1103
|
+
.color('#495057')
|
|
1104
|
+
.padding('15px 20px')
|
|
1105
|
+
.rounded('8px')
|
|
1106
|
+
.border('1px solid #dee2e6')
|
|
1107
|
+
.font('Monaco, Consolas, monospace')
|
|
1108
|
+
.size('13px')
|
|
1109
|
+
.build();
|
|
1110
|
+
|
|
1111
|
+
const helpText = `
|
|
1112
|
+
āāāāāāāāāāāāāāāā LOGGER CLI COMMANDS āāāāāāāāāāāāāāāā®
|
|
1113
|
+
ā ā
|
|
1114
|
+
ā /help Show this help message ā
|
|
1115
|
+
ā /config Show current configuration ā
|
|
1116
|
+
ā /config {json} Apply JSON configuration ā
|
|
1117
|
+
ā /config key=val Apply key-value config ā
|
|
1118
|
+
ā /themes Show available themes ā
|
|
1119
|
+
ā /banners Show available banner types ā
|
|
1120
|
+
ā /banner [type] Change/show banner type ā
|
|
1121
|
+
ā /status Show logger status ā
|
|
1122
|
+
ā /demo Show feature demonstration ā
|
|
1123
|
+
ā /reset Reset to default config ā
|
|
1124
|
+
ā ā
|
|
1125
|
+
āāāāāāāāāāāāāāāā CONFIGURATION OPTIONS āāāāāāāāāāāāāā¤
|
|
1126
|
+
ā ā
|
|
1127
|
+
ā theme: default | dark | neon | minimal ā
|
|
1128
|
+
ā bannerType: simple | ascii | unicode | svg ā
|
|
1129
|
+
ā verbosity: debug | info | warn | error | silent ā
|
|
1130
|
+
ā enableColors: true | false ā
|
|
1131
|
+
ā enableTimestamps: true | false ā
|
|
1132
|
+
ā enableStackTrace: true | false ā
|
|
1133
|
+
ā globalPrefix: "string" ā
|
|
1134
|
+
ā ā
|
|
1135
|
+
āāāāāāāāāāāāāāāāāāāāā EXAMPLES āāāāāāāāāāāāāāāāāāāāāā¤
|
|
1136
|
+
ā ā
|
|
1137
|
+
ā /config {"theme":"dark","bannerType":"animated"} ā
|
|
1138
|
+
ā /config theme=neon,verbosity=debug ā
|
|
1139
|
+
ā /banner svg Change to SVG banner ā
|
|
1140
|
+
ā /demo Show all features ā
|
|
1141
|
+
ā ā
|
|
1142
|
+
ā°āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāÆ`;
|
|
1143
|
+
|
|
1144
|
+
console.log(`%c${helpText}`, helpStyle);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* Show available themes with previews
|
|
1149
|
+
* @private
|
|
1150
|
+
*/
|
|
1151
|
+
private showThemes(): void {
|
|
1152
|
+
this.group('šØ Available Themes');
|
|
1153
|
+
Object.keys(THEME_PRESETS).forEach(themeName => {
|
|
1154
|
+
const preview = (THEME_PRESETS as any)[themeName];
|
|
1155
|
+
const previewStyle = new StyleBuilder()
|
|
1156
|
+
.bg(preview.info.background)
|
|
1157
|
+
.color(preview.info.color)
|
|
1158
|
+
.padding('4px 8px')
|
|
1159
|
+
.rounded('4px')
|
|
1160
|
+
.border(preview.info.border)
|
|
1161
|
+
.build();
|
|
1162
|
+
|
|
1163
|
+
console.log(`%c${themeName}`, previewStyle, `- ${themeName} theme preview`);
|
|
1164
|
+
});
|
|
1165
|
+
this.groupEnd();
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Show available banner types with previews
|
|
1170
|
+
* @private
|
|
1171
|
+
*/
|
|
1172
|
+
private showBanners(): void {
|
|
1173
|
+
this.group('š¼ļø Available Banner Types');
|
|
1174
|
+
Object.keys(BANNER_VARIANTS).forEach(bannerName => {
|
|
1175
|
+
const banner = (BANNER_VARIANTS as any)[bannerName];
|
|
1176
|
+
console.log(`%c${bannerName}`, 'font-weight: bold; color: #667eea;');
|
|
1177
|
+
console.log(`%cPreview:`, 'color: #666; font-size: 12px;');
|
|
1178
|
+
|
|
1179
|
+
// Show a mini preview
|
|
1180
|
+
if (bannerName === 'simple') {
|
|
1181
|
+
console.log(`%c${banner.text}`, banner.style);
|
|
1182
|
+
} else if (bannerName === 'ascii') {
|
|
1183
|
+
console.log(`%c${banner.text.split('\n').slice(1, 4).join('\n')}...`, 'font-family: monospace; color: #667eea; font-size: 10px;');
|
|
1184
|
+
} else if (bannerName === 'unicode') {
|
|
1185
|
+
console.log(`%c${banner.text}`, banner.style);
|
|
1186
|
+
} else {
|
|
1187
|
+
console.log(`%c${bannerName} banner`, 'color: #666; font-style: italic;');
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
this.groupEnd();
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* Handle banner command
|
|
1195
|
+
* @private
|
|
1196
|
+
*/
|
|
1197
|
+
private handleBannerCommand(args: string): void {
|
|
1198
|
+
if (!args) {
|
|
1199
|
+
this.showBanner();
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (args in BANNER_VARIANTS) {
|
|
1204
|
+
this.setBannerType(args as BannerType);
|
|
1205
|
+
this.showBanner();
|
|
1206
|
+
} else {
|
|
1207
|
+
this.error(`Invalid banner type: ${args}. Available: ${Object.keys(BANNER_VARIANTS).join(', ')}`);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Show demonstration of all logger features
|
|
1213
|
+
* @private
|
|
1214
|
+
*/
|
|
1215
|
+
private showDemo(): void {
|
|
1216
|
+
this.group('šŖ Advanced Logger Demo');
|
|
1217
|
+
|
|
1218
|
+
// Basic logging demo
|
|
1219
|
+
this.debug('Debug message with detailed information');
|
|
1220
|
+
this.info('Informational message about system state');
|
|
1221
|
+
this.warn('Warning about deprecated feature');
|
|
1222
|
+
this.error('Error processing user request');
|
|
1223
|
+
this.success('Operation completed successfully');
|
|
1224
|
+
this.critical('Critical system failure detected');
|
|
1225
|
+
|
|
1226
|
+
// Advanced features demo
|
|
1227
|
+
this.group('š Advanced Features Demo');
|
|
1228
|
+
|
|
1229
|
+
// Table demo
|
|
1230
|
+
this.table([
|
|
1231
|
+
{ feature: 'Styled Console', status: 'ā
Active', performance: 'Excellent' },
|
|
1232
|
+
{ feature: 'Theme System', status: 'ā
Active', performance: 'Great' },
|
|
1233
|
+
{ feature: 'CLI Interface', status: 'ā
Active', performance: 'Good' }
|
|
1234
|
+
]);
|
|
1235
|
+
|
|
1236
|
+
// Timer demo
|
|
1237
|
+
this.time('demo-operation');
|
|
1238
|
+
setTimeout(() => {
|
|
1239
|
+
this.timeEnd('demo-operation');
|
|
1240
|
+
}, 100);
|
|
1241
|
+
|
|
1242
|
+
// SVG demo
|
|
1243
|
+
this.logWithSVG('SVG Demo');
|
|
1244
|
+
|
|
1245
|
+
// Animated demo
|
|
1246
|
+
this.logAnimated('š Animated Logger Demo š', 2);
|
|
1247
|
+
|
|
1248
|
+
this.groupEnd();
|
|
1249
|
+
this.groupEnd();
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* Show current logger status and configuration
|
|
1254
|
+
* @private
|
|
1255
|
+
*/
|
|
1256
|
+
private showStatus(): void {
|
|
1257
|
+
const statusData = {
|
|
1258
|
+
theme: this.config.theme || 'default',
|
|
1259
|
+
verbosity: this.config.verbosity,
|
|
1260
|
+
colors: this.config.enableColors,
|
|
1261
|
+
timestamps: this.config.enableTimestamps,
|
|
1262
|
+
stackTrace: this.config.enableStackTrace,
|
|
1263
|
+
globalPrefix: this.config.globalPrefix || 'none',
|
|
1264
|
+
handlers: this.handlers.length
|
|
1265
|
+
};
|
|
1266
|
+
|
|
1267
|
+
this.group('āļø Logger Configuration');
|
|
1268
|
+
this.table(statusData);
|
|
1269
|
+
this.groupEnd();
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
/**
|
|
1273
|
+
* Reset logger to default configuration
|
|
1274
|
+
* @private
|
|
1275
|
+
*/
|
|
1276
|
+
private resetConfig(): void {
|
|
1277
|
+
this.config = {
|
|
1278
|
+
verbosity: 'info',
|
|
1279
|
+
enableColors: true,
|
|
1280
|
+
enableTimestamps: true,
|
|
1281
|
+
enableStackTrace: true,
|
|
1282
|
+
theme: 'default',
|
|
1283
|
+
bannerType: 'simple',
|
|
1284
|
+
};
|
|
1285
|
+
LEVEL_STYLES = THEME_PRESETS.default;
|
|
1286
|
+
this.success('Logger configuration reset to defaults');
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* Set banner type for initialization display
|
|
1291
|
+
* @param bannerType - Type of banner to display
|
|
1292
|
+
*/
|
|
1293
|
+
setBannerType(bannerType: BannerType): void {
|
|
1294
|
+
this.config.bannerType = bannerType;
|
|
1295
|
+
this.success(`Banner type changed to: ${bannerType}`);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/**
|
|
1299
|
+
* Display banner with specified or configured type
|
|
1300
|
+
* @param bannerType - Optional banner type override
|
|
1301
|
+
*/
|
|
1302
|
+
showBanner(bannerType?: BannerType): void {
|
|
1303
|
+
displayInitBanner(bannerType || this.config.bannerType);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Log with SVG background image
|
|
1308
|
+
* @param message - The message to log
|
|
1309
|
+
* @param svgContent - SVG content as string
|
|
1310
|
+
* @param options - Additional styling options
|
|
1311
|
+
*/
|
|
1312
|
+
logWithSVG(message: string, svgContent?: string, options: { width?: number, height?: number, padding?: string } = {}): void {
|
|
1313
|
+
const { width = 300, height = 60, padding = '30px 150px' } = options;
|
|
1314
|
+
|
|
1315
|
+
let svgDataUri = '';
|
|
1316
|
+
if (svgContent) {
|
|
1317
|
+
// Encode SVG for data URI
|
|
1318
|
+
const encodedSVG = encodeURIComponent(svgContent);
|
|
1319
|
+
svgDataUri = `data:image/svg+xml,${encodedSVG}`;
|
|
1320
|
+
} else {
|
|
1321
|
+
// Default animated SVG
|
|
1322
|
+
const defaultSVG = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${width} ${height}'><defs><linearGradient id='grad' x1='0%' y1='0%' x2='100%' y2='0%'><stop offset='0%' style='stop-color:%23667eea'/><stop offset='100%' style='stop-color:%23764ba2'/></linearGradient></defs><rect width='100%' height='100%' fill='url(%23grad)' rx='4'/><text x='${width/2}' y='${height/2 + 5}' text-anchor='middle' fill='white' font-family='monospace' font-size='14' font-weight='bold'>${message}</text></svg>`;
|
|
1323
|
+
svgDataUri = `data:image/svg+xml,${encodeURIComponent(defaultSVG)}`;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
const svgStyle = new StyleBuilder()
|
|
1327
|
+
.bg(`url("${svgDataUri}") no-repeat center center`)
|
|
1328
|
+
.size(`${width}px ${height}px`)
|
|
1329
|
+
.padding(padding)
|
|
1330
|
+
.color('transparent')
|
|
1331
|
+
.rounded('4px')
|
|
1332
|
+
.build();
|
|
1333
|
+
|
|
1334
|
+
console.log(`%c${message}`, svgStyle);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Log with animated background gradient
|
|
1339
|
+
* @param message - The message to log
|
|
1340
|
+
* @param duration - Animation duration in seconds
|
|
1341
|
+
*/
|
|
1342
|
+
logAnimated(message: string, duration: number = 3): void {
|
|
1343
|
+
// Inject CSS animation if not already present
|
|
1344
|
+
if (!document.getElementById('logger-animations')) {
|
|
1345
|
+
const style = document.createElement('style');
|
|
1346
|
+
style.id = 'logger-animations';
|
|
1347
|
+
style.textContent = `
|
|
1348
|
+
@keyframes loggerGradient {
|
|
1349
|
+
0% { background-position: 0% 50%; }
|
|
1350
|
+
50% { background-position: 100% 50%; }
|
|
1351
|
+
100% { background-position: 0% 50%; }
|
|
1352
|
+
}
|
|
1353
|
+
@keyframes loggerPulse {
|
|
1354
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
1355
|
+
50% { opacity: 0.8; transform: scale(1.05); }
|
|
1356
|
+
}
|
|
1357
|
+
@keyframes loggerSlide {
|
|
1358
|
+
0% { transform: translateX(-100%); opacity: 0; }
|
|
1359
|
+
100% { transform: translateX(0); opacity: 1; }
|
|
1360
|
+
}
|
|
1361
|
+
`;
|
|
1362
|
+
document.head.appendChild(style);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
const animatedStyle = new StyleBuilder()
|
|
1366
|
+
.bg('linear-gradient(-45deg, #667eea, #764ba2, #667eea, #764ba2)')
|
|
1367
|
+
.size('400% 400%')
|
|
1368
|
+
.color('#ffffff')
|
|
1369
|
+
.padding('12px 20px')
|
|
1370
|
+
.rounded('8px')
|
|
1371
|
+
.bold()
|
|
1372
|
+
.font('Monaco, Consolas, monospace')
|
|
1373
|
+
.build() + `; animation: loggerGradient ${duration}s ease infinite; display: inline-block;`;
|
|
1374
|
+
|
|
1375
|
+
console.log(`%c${message}`, animatedStyle);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* File-based log handler for persistent logging
|
|
1381
|
+
*/
|
|
1382
|
+
export class FileLogHandler implements ILogHandler {
|
|
1383
|
+
private filename: string;
|
|
1384
|
+
|
|
1385
|
+
constructor(filename: string = 'app.log') {
|
|
1386
|
+
this.filename = filename;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
handle(level: LogLevel, message: string, args: any[], metadata: LogMetadata): void {
|
|
1390
|
+
const logEntry = {
|
|
1391
|
+
filename: this.filename,
|
|
1392
|
+
timestamp: metadata.timestamp,
|
|
1393
|
+
level: level.toUpperCase(),
|
|
1394
|
+
prefix: metadata.prefix,
|
|
1395
|
+
message,
|
|
1396
|
+
args: args.slice(1),
|
|
1397
|
+
location: metadata.stackInfo,
|
|
1398
|
+
};
|
|
1399
|
+
|
|
1400
|
+
console.debug('File log entry:', logEntry);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
* Remote log handler for sending logs to external services
|
|
1406
|
+
*/
|
|
1407
|
+
export class RemoteLogHandler implements ILogHandler {
|
|
1408
|
+
private endpoint: string;
|
|
1409
|
+
private apiKey?: string;
|
|
1410
|
+
|
|
1411
|
+
constructor(endpoint: string, apiKey?: string) {
|
|
1412
|
+
this.endpoint = endpoint;
|
|
1413
|
+
this.apiKey = apiKey;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
async handle(level: LogLevel, message: string, args: any[], metadata: LogMetadata): Promise<void> {
|
|
1417
|
+
try {
|
|
1418
|
+
const payload = {
|
|
1419
|
+
timestamp: metadata.timestamp,
|
|
1420
|
+
level,
|
|
1421
|
+
message,
|
|
1422
|
+
prefix: metadata.prefix,
|
|
1423
|
+
location: metadata.stackInfo,
|
|
1424
|
+
additional: args.slice(1),
|
|
1425
|
+
};
|
|
1426
|
+
|
|
1427
|
+
await fetch(this.endpoint, {
|
|
1428
|
+
method: 'POST',
|
|
1429
|
+
headers: {
|
|
1430
|
+
'Content-Type': 'application/json',
|
|
1431
|
+
...(this.apiKey && { 'Authorization': `Bearer ${this.apiKey}` }),
|
|
1432
|
+
},
|
|
1433
|
+
body: JSON.stringify(payload),
|
|
1434
|
+
});
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
// Silently fail to avoid infinite logging loops
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* Banner variants for different display capabilities
|
|
1443
|
+
*/
|
|
1444
|
+
const BANNER_VARIANTS = {
|
|
1445
|
+
simple: {
|
|
1446
|
+
text: 'š ADVANCED LOGGER v2.0.0 - State-of-the-art Console Styling š',
|
|
1447
|
+
style: 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 20px; border-radius: 8px; font-weight: bold; font-size: 14px;'
|
|
1448
|
+
},
|
|
1449
|
+
ascii: {
|
|
1450
|
+
text: `
|
|
1451
|
+
___ ____ _ __ ___ _ __ _____ _____ ____ __ ___ _____ _____ _____ ____
|
|
1452
|
+
/ _ \\ / __ \\| | / / / _ \\ | \\ | |/ ____| ____| _ \\ | | / _ \\ / ____| ___| _ | _ \\
|
|
1453
|
+
/ /_\\ \\ / / _\` | |/ / / /_\\ \\ | \\| | | | |__ | | | | | | / / \\ \\| | __| |_ | |_| | |_) |
|
|
1454
|
+
| _ || | (_| | < | _ | | . \` | | | __| | | | | | | | | | | | |_ | _| | /| _ <
|
|
1455
|
+
| | | |\\ \\__,_|_|\\_\\ | | | | | |\\ | |___| |____| |_| | | |__\\ \\_/ /| |__| | |___| |\\ \\| |_) |
|
|
1456
|
+
\\_| |_/ \\____/ \\_| |_/ |_| \\_|\\_____|______|____/ |_____/\\___/ \\_____|_____|_| \\_|____/
|
|
1457
|
+
|
|
1458
|
+
Advanced Logger v2.0.0 - Console Excellence`,
|
|
1459
|
+
style: 'font-family: "Courier New", Consolas, Monaco, monospace; color: #667eea; font-size: 11px; line-height: 1.2;'
|
|
1460
|
+
},
|
|
1461
|
+
unicode: {
|
|
1462
|
+
text: `
|
|
1463
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1464
|
+
ā š ADVANCED LOGGER v2.0.0 ā
|
|
1465
|
+
ā State-of-the-art Console Styling ā
|
|
1466
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`,
|
|
1467
|
+
style: 'font-family: "Courier New", Consolas, Monaco, monospace; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px; border-radius: 8px; font-size: 12px; line-height: 1.3;'
|
|
1468
|
+
},
|
|
1469
|
+
svg: {
|
|
1470
|
+
text: ' ',
|
|
1471
|
+
style: `
|
|
1472
|
+
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 80'><defs><linearGradient id='grad' x1='0%' y1='0%' x2='100%' y2='0%'><stop offset='0%' style='stop-color:%23667eea'/><stop offset='100%' style='stop-color:%23764ba2'/></linearGradient></defs><rect width='100%' height='100%' fill='url(%23grad)' rx='8'/><text x='200' y='30' text-anchor='middle' fill='white' font-family='monospace' font-size='14' font-weight='bold'>š ADVANCED LOGGER</text><text x='200' y='50' text-anchor='middle' fill='white' font-family='monospace' font-size='12'>State-of-the-art Console Styling</text><text x='200' y='65' text-anchor='middle' fill='white' font-family='monospace' font-size='10'>v2.0.0</text></svg>");
|
|
1473
|
+
background-repeat: no-repeat;
|
|
1474
|
+
background-size: 400px 80px;
|
|
1475
|
+
padding: 40px 200px;
|
|
1476
|
+
color: transparent;
|
|
1477
|
+
display: inline-block;
|
|
1478
|
+
border-radius: 8px;
|
|
1479
|
+
`
|
|
1480
|
+
},
|
|
1481
|
+
animated: {
|
|
1482
|
+
text: ' š ADVANCED LOGGER v2.0.0 ',
|
|
1483
|
+
style: `
|
|
1484
|
+
background: linear-gradient(-45deg, #667eea, #764ba2, #667eea, #764ba2);
|
|
1485
|
+
background-size: 400% 400%;
|
|
1486
|
+
color: white;
|
|
1487
|
+
padding: 15px 25px;
|
|
1488
|
+
border-radius: 10px;
|
|
1489
|
+
font-weight: bold;
|
|
1490
|
+
font-size: 14px;
|
|
1491
|
+
font-family: monospace;
|
|
1492
|
+
animation: gradientShift 3s ease infinite;
|
|
1493
|
+
display: inline-block;
|
|
1494
|
+
`
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* Feature detection for banner capabilities
|
|
1500
|
+
*/
|
|
1501
|
+
function detectBannerCapabilities(): BannerType {
|
|
1502
|
+
// Try to detect browser capabilities
|
|
1503
|
+
const userAgent = navigator.userAgent;
|
|
1504
|
+
const isChrome = /Chrome/.test(userAgent);
|
|
1505
|
+
const isFirefox = /Firefox/.test(userAgent);
|
|
1506
|
+
const isSafari = /Safari/.test(userAgent) && !/Chrome/.test(userAgent);
|
|
1507
|
+
|
|
1508
|
+
// Check for SVG support (most modern browsers)
|
|
1509
|
+
const supportsSVG = !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;
|
|
1510
|
+
|
|
1511
|
+
// Check for CSS animation support
|
|
1512
|
+
const supportsAnimations = typeof document !== 'undefined' &&
|
|
1513
|
+
'animationName' in document.createElement('div').style;
|
|
1514
|
+
|
|
1515
|
+
// Progressive enhancement
|
|
1516
|
+
if (supportsAnimations && isChrome) {
|
|
1517
|
+
return 'animated';
|
|
1518
|
+
} else if (supportsSVG && (isChrome || isFirefox)) {
|
|
1519
|
+
return 'svg';
|
|
1520
|
+
} else if (isChrome || isFirefox) {
|
|
1521
|
+
return 'unicode';
|
|
1522
|
+
} else if (isSafari) {
|
|
1523
|
+
return 'ascii';
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
return 'simple';
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/**
|
|
1530
|
+
* Display initialization banner with advanced styling
|
|
1531
|
+
*/
|
|
1532
|
+
function displayInitBanner(bannerType?: BannerType): void {
|
|
1533
|
+
const selectedType = bannerType || detectBannerCapabilities();
|
|
1534
|
+
const banner = BANNER_VARIANTS[selectedType];
|
|
1535
|
+
|
|
1536
|
+
// Add CSS animation keyframes if needed
|
|
1537
|
+
if (selectedType === 'animated') {
|
|
1538
|
+
const style = document.createElement('style');
|
|
1539
|
+
style.textContent = `
|
|
1540
|
+
@keyframes gradientShift {
|
|
1541
|
+
0% { background-position: 0% 50%; }
|
|
1542
|
+
50% { background-position: 100% 50%; }
|
|
1543
|
+
100% { background-position: 0% 50%; }
|
|
1544
|
+
}
|
|
1545
|
+
`;
|
|
1546
|
+
document.head.appendChild(style);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
console.log(`%c${banner.text}`, banner.style);
|
|
1550
|
+
|
|
1551
|
+
// Show feature highlights
|
|
1552
|
+
const features = [
|
|
1553
|
+
'šØ Advanced CSS Console Styling',
|
|
1554
|
+
'š Automatic Stack Trace Parsing',
|
|
1555
|
+
'š§ Scoped Loggers & Prefixes',
|
|
1556
|
+
'ā” Performance Timers',
|
|
1557
|
+
'šÆ Verbosity Filtering',
|
|
1558
|
+
'š Extensible Handlers',
|
|
1559
|
+
'š± Modern TypeScript Patterns'
|
|
1560
|
+
];
|
|
1561
|
+
|
|
1562
|
+
console.group(`%c⨠Features`, new StyleBuilder()
|
|
1563
|
+
.bg('#f8f9fa')
|
|
1564
|
+
.color('#495057')
|
|
1565
|
+
.padding('4px 8px')
|
|
1566
|
+
.rounded('4px')
|
|
1567
|
+
.bold()
|
|
1568
|
+
.build());
|
|
1569
|
+
|
|
1570
|
+
features.forEach(feature => {
|
|
1571
|
+
console.log(`%c${feature}`, new StyleBuilder()
|
|
1572
|
+
.color('#6c757d')
|
|
1573
|
+
.size('13px')
|
|
1574
|
+
.build());
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1577
|
+
console.groupEnd();
|
|
1578
|
+
console.log(''); // Add spacing
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/**
|
|
1582
|
+
* Create and export the singleton logger instance
|
|
1583
|
+
*/
|
|
1584
|
+
const defaultLogger = new Logger({
|
|
1585
|
+
verbosity: 'info',
|
|
1586
|
+
enableColors: true,
|
|
1587
|
+
enableTimestamps: true,
|
|
1588
|
+
enableStackTrace: true,
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
// Display initialization banner with auto-detection
|
|
1592
|
+
displayInitBanner();
|
|
1593
|
+
|
|
1594
|
+
// Export the singleton instance as default
|
|
1595
|
+
export default defaultLogger;
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* Export individual methods for convenience (with proper binding)
|
|
1599
|
+
*/
|
|
1600
|
+
export const debug = (...args: any[]) => defaultLogger.debug(...args);
|
|
1601
|
+
export const info = (...args: any[]) => defaultLogger.info(...args);
|
|
1602
|
+
export const warn = (...args: any[]) => defaultLogger.warn(...args);
|
|
1603
|
+
export const error = (...args: any[]) => defaultLogger.error(...args);
|
|
1604
|
+
export const success = (...args: any[]) => defaultLogger.success(...args);
|
|
1605
|
+
export const trace = (...args: any[]) => defaultLogger.trace(...args);
|
|
1606
|
+
export const critical = (...args: any[]) => defaultLogger.critical(...args);
|
|
1607
|
+
export const table = (data: any, columns?: string[]) => defaultLogger.table(data, columns);
|
|
1608
|
+
export const group = (label: string, collapsed?: boolean) => defaultLogger.group(label, collapsed);
|
|
1609
|
+
export const groupEnd = () => defaultLogger.groupEnd();
|
|
1610
|
+
export const time = (label: string) => defaultLogger.time(label);
|
|
1611
|
+
export const timeEnd = (label: string) => defaultLogger.timeEnd(label);
|
|
1612
|
+
export const setGlobalPrefix = (prefix: string) => defaultLogger.setGlobalPrefix(prefix);
|
|
1613
|
+
export const createScopedLogger = (prefix: string) => defaultLogger.createScopedLogger(prefix);
|
|
1614
|
+
export const setVerbosity = (level: Verbosity) => defaultLogger.setVerbosity(level);
|
|
1615
|
+
export const addHandler = (handler: ILogHandler) => defaultLogger.addHandler(handler);
|
|
1616
|
+
export const setTheme = (theme: ThemeVariant) => defaultLogger.setTheme(theme);
|
|
1617
|
+
export const setBannerType = (bannerType: BannerType) => defaultLogger.setBannerType(bannerType);
|
|
1618
|
+
export const showBanner = (bannerType?: BannerType) => defaultLogger.showBanner(bannerType);
|
|
1619
|
+
export const logWithSVG = (message: string, svgContent?: string, options?: { width?: number, height?: number, padding?: string }) => defaultLogger.logWithSVG(message, svgContent, options);
|
|
1620
|
+
export const logAnimated = (message: string, duration?: number) => defaultLogger.logAnimated(message, duration);
|
|
1621
|
+
export const cli = (command: string) => defaultLogger.cli(command);
|
|
1622
|
+
|
|
1623
|
+
/**
|
|
1624
|
+
* Advanced styling utilities for external use
|
|
1625
|
+
*/
|
|
1626
|
+
export const Styles = {
|
|
1627
|
+
/**
|
|
1628
|
+
* Creates a new StyleBuilder instance
|
|
1629
|
+
*/
|
|
1630
|
+
create(): StyleBuilder {
|
|
1631
|
+
return new StyleBuilder();
|
|
1632
|
+
},
|
|
1633
|
+
|
|
1634
|
+
/**
|
|
1635
|
+
* Pre-defined common styles
|
|
1636
|
+
*/
|
|
1637
|
+
presets: {
|
|
1638
|
+
success: new StyleBuilder()
|
|
1639
|
+
.bg('linear-gradient(135deg, #00b894 0%, #00a085 100%)')
|
|
1640
|
+
.color('#ffffff')
|
|
1641
|
+
.padding('4px 8px')
|
|
1642
|
+
.rounded('4px')
|
|
1643
|
+
.bold()
|
|
1644
|
+
.build(),
|
|
1645
|
+
|
|
1646
|
+
error: new StyleBuilder()
|
|
1647
|
+
.bg('linear-gradient(135deg, #e84393 0%, #d63031 100%)')
|
|
1648
|
+
.color('#ffffff')
|
|
1649
|
+
.padding('4px 8px')
|
|
1650
|
+
.rounded('4px')
|
|
1651
|
+
.bold()
|
|
1652
|
+
.build(),
|
|
1653
|
+
|
|
1654
|
+
warning: new StyleBuilder()
|
|
1655
|
+
.bg('linear-gradient(135deg, #fdcb6e 0%, #e17055 100%)')
|
|
1656
|
+
.color('#2d3436')
|
|
1657
|
+
.padding('4px 8px')
|
|
1658
|
+
.rounded('4px')
|
|
1659
|
+
.bold()
|
|
1660
|
+
.build(),
|
|
1661
|
+
|
|
1662
|
+
info: new StyleBuilder()
|
|
1663
|
+
.bg('linear-gradient(135deg, #74b9ff 0%, #0984e3 100%)')
|
|
1664
|
+
.color('#ffffff')
|
|
1665
|
+
.padding('4px 8px')
|
|
1666
|
+
.rounded('4px')
|
|
1667
|
+
.bold()
|
|
1668
|
+
.build(),
|
|
1669
|
+
},
|
|
1670
|
+
};
|
|
1671
|
+
|
|
1672
|
+
|
|
1673
|
+
/**
|
|
1674
|
+
* Example custom handler for demonstration
|
|
1675
|
+
*/
|
|
1676
|
+
export class AnalyticsLogHandler implements ILogHandler {
|
|
1677
|
+
handle(level: LogLevel, message: string, _args: any[], metadata: LogMetadata): void {
|
|
1678
|
+
// In a real implementation, this could send analytics data
|
|
1679
|
+
if (level === 'error' || level === 'critical') {
|
|
1680
|
+
// Track errors for analytics
|
|
1681
|
+
console.debug('Analytics: Error tracked', { level, message, timestamp: metadata.timestamp });
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
}
|