@mks2508/better-logger 2.1.0 → 3.0.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 +6 -1
- package/CHANGELOG.json +93 -1
- package/CHANGELOG.md +127 -0
- package/CLAUDE.md +14 -1
- package/README.md +111 -20
- package/dist/Logger.d.ts +205 -58
- 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-BCfx_yCK.js +2 -0
- package/dist/chunks/Logger-BCfx_yCK.js.map +1 -0
- package/dist/chunks/{Logger-B7L-ujY4.js → Logger-C126NTrD.js} +862 -96
- package/dist/chunks/Logger-C126NTrD.js.map +1 -0
- 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 +1 -1
- 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 +23 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +240 -9
- 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 +2 -2
- 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 +38 -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/docs/API.md +227 -0
- package/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/Logger.ts +391 -102
- package/src/ScopedLogger.ts +78 -318
- package/src/hooks/HookManager.ts +177 -0
- package/src/hooks/index.ts +1 -0
- package/src/index.ts +77 -2
- package/src/serializers/SerializerRegistry.ts +173 -0
- package/src/serializers/index.ts +1 -0
- package/src/styling/StyleCache.ts +131 -0
- 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 +46 -0
- package/src/types/hooks.ts +45 -0
- package/src/types/index.ts +39 -1
- package/src/types/serializers.ts +30 -0
- package/src/types/transports.ts +52 -0
- 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/src/ScopedLogger.ts
CHANGED
|
@@ -1,344 +1,159 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview ScopedLogger con badges y logging contextual
|
|
3
|
-
* @version 0.3.0
|
|
4
|
-
* @since 2024
|
|
5
|
-
*
|
|
6
|
-
* Proporciona loggers especializados con contexto, badges automáticos
|
|
7
|
-
* y características avanzadas para componentes y APIs.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
import { Logger } from './Logger.js';
|
|
11
|
-
import type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* @class ScopedLogger
|
|
17
|
-
* @extends Logger
|
|
18
|
-
* @description Logger contextual que añade prefijos y badges automáticamente
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* const dbLogger = logger.scope('Database');
|
|
22
|
-
* dbLogger.info('Conectando...'); // [Database] Conectando...
|
|
23
|
-
*
|
|
24
|
-
* // Con badges personalizados
|
|
25
|
-
* dbLogger.badge('SLOW').warn('Query lenta detectada');
|
|
26
|
-
* // [SLOW] [Database] Query lenta detectada
|
|
27
|
-
*
|
|
28
|
-
* @since 0.3.0
|
|
29
|
-
*/
|
|
30
|
-
export class ScopedLogger extends Logger {
|
|
31
|
-
private parentLogger: Logger;
|
|
32
|
-
private scopeName: string;
|
|
2
|
+
import type { TimerEntry, Bindings } from './types/index.js';
|
|
3
|
+
|
|
4
|
+
export class ScopedLogger {
|
|
5
|
+
private readonly parent: Logger;
|
|
6
|
+
private readonly scopeName: string;
|
|
33
7
|
private badgeList: string[] = [];
|
|
34
8
|
private contextStack: string[] = [];
|
|
9
|
+
private _timers?: Map<string, TimerEntry>;
|
|
35
10
|
|
|
36
|
-
constructor(
|
|
37
|
-
|
|
38
|
-
super({ ...parentLogger.getConfig(), ...config });
|
|
39
|
-
this.parentLogger = parentLogger;
|
|
11
|
+
constructor(parent: Logger, scopeName: string) {
|
|
12
|
+
this.parent = parent;
|
|
40
13
|
this.scopeName = scopeName;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
badges(badges: string[]): ScopedLogger {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private get timers(): Map<string, TimerEntry> {
|
|
17
|
+
if (!this._timers) this._timers = new Map();
|
|
18
|
+
return this._timers;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private getBindings(): Bindings {
|
|
22
|
+
return {
|
|
23
|
+
scope: this.getScopePrefix(),
|
|
24
|
+
badges: this.badgeList.length > 0 ? [...this.badgeList] : undefined,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private getScopePrefix(): string {
|
|
29
|
+
if (this.contextStack.length === 0) return this.scopeName;
|
|
30
|
+
return [this.scopeName, ...this.contextStack].join(':');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
badges(badges: string[]): this {
|
|
62
34
|
this.badgeList = [...badges];
|
|
63
35
|
return this;
|
|
64
36
|
}
|
|
65
37
|
|
|
66
|
-
|
|
67
|
-
* Añade un badge individual
|
|
68
|
-
*
|
|
69
|
-
* @param {string} badge - Badge a añadir
|
|
70
|
-
* @returns {ScopedLogger} Esta instancia para encadenamiento
|
|
71
|
-
*
|
|
72
|
-
* @example
|
|
73
|
-
* dbLogger.badge('SLOW').warn('Query tardó 3s');
|
|
74
|
-
* // [SLOW] [Database] Query tardó 3s
|
|
75
|
-
*
|
|
76
|
-
* @since 0.3.0
|
|
77
|
-
*/
|
|
78
|
-
badge(badge: string): ScopedLogger {
|
|
38
|
+
badge(badge: string): this {
|
|
79
39
|
if (!this.badgeList.includes(badge)) {
|
|
80
40
|
this.badgeList.push(badge);
|
|
81
41
|
}
|
|
82
42
|
return this;
|
|
83
43
|
}
|
|
84
44
|
|
|
85
|
-
|
|
86
|
-
* Elimina todos los badges
|
|
87
|
-
*
|
|
88
|
-
* @returns {ScopedLogger} Esta instancia para encadenamiento
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* api.clearBadges().info('Sin badges');
|
|
92
|
-
* // [API] Sin badges
|
|
93
|
-
*
|
|
94
|
-
* @since 0.3.0
|
|
95
|
-
*/
|
|
96
|
-
clearBadges(): ScopedLogger {
|
|
45
|
+
clearBadges(): this {
|
|
97
46
|
this.badgeList = [];
|
|
98
47
|
return this;
|
|
99
48
|
}
|
|
100
49
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
*
|
|
104
|
-
* @param {string} presetName - Nombre del preset a aplicar
|
|
105
|
-
* @returns {ScopedLogger} Esta instancia para encadenamiento
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* const errorLogger = logger.scope('ErrorHandler')
|
|
109
|
-
* .style('cyberpunk');
|
|
110
|
-
*
|
|
111
|
-
* @since 0.3.0
|
|
112
|
-
*/
|
|
113
|
-
style(presetName: string): ScopedLogger {
|
|
114
|
-
this.setTheme(presetName as any); // Will be updated when we implement presets
|
|
50
|
+
style(presetName: string): this {
|
|
51
|
+
this.parent.setTheme(presetName as any);
|
|
115
52
|
return this;
|
|
116
53
|
}
|
|
117
54
|
|
|
118
|
-
/**
|
|
119
|
-
* Crea un contexto temporal que se limpia automáticamente
|
|
120
|
-
*
|
|
121
|
-
* @param {string} contextName - Nombre del contexto
|
|
122
|
-
* @returns {ContextLogger} Logger con contexto temporal
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* const db = logger.scope('Database');
|
|
126
|
-
*
|
|
127
|
-
* // Contexto temporal para una operación
|
|
128
|
-
* db.context('Migration').run(() => {
|
|
129
|
-
* db.info('Iniciando migración');
|
|
130
|
-
* db.success('Tablas creadas');
|
|
131
|
-
* });
|
|
132
|
-
* // Contexto automáticamente limpiado
|
|
133
|
-
*
|
|
134
|
-
* @since 0.3.0
|
|
135
|
-
*/
|
|
136
55
|
context(contextName: string): ContextLogger {
|
|
137
56
|
return new ContextLogger(this, contextName);
|
|
138
57
|
}
|
|
139
58
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (badges) enhancedMessage += badges + ' ';
|
|
153
|
-
if (scopePrefix) enhancedMessage += `[${scopePrefix}] `;
|
|
154
|
-
|
|
155
|
-
if (args.length > 0) {
|
|
156
|
-
enhancedMessage += String(args[0]);
|
|
157
|
-
// Replace the first argument with our enhanced message
|
|
158
|
-
args[0] = enhancedMessage;
|
|
59
|
+
time(label: string): void {
|
|
60
|
+
this.timers.set(label, {
|
|
61
|
+
label: `${this.scopeName}:${label}`,
|
|
62
|
+
startTime: performance.now(),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
timeEnd(label: string): number | undefined {
|
|
67
|
+
const timer = this.timers.get(label);
|
|
68
|
+
if (!timer) {
|
|
69
|
+
this.warn(`Timer '${label}' not found`);
|
|
70
|
+
return undefined;
|
|
159
71
|
}
|
|
72
|
+
const elapsed = performance.now() - timer.startTime;
|
|
73
|
+
this.timers.delete(label);
|
|
74
|
+
this.success(`Timer: ${label} - ${elapsed.toFixed(2)}ms`);
|
|
75
|
+
return elapsed;
|
|
76
|
+
}
|
|
160
77
|
|
|
161
|
-
|
|
162
|
-
|
|
78
|
+
debug(...args: any[]): void {
|
|
79
|
+
this.parent.logWithBindings(this.getBindings(), 'debug', ...args);
|
|
163
80
|
}
|
|
164
81
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
* @private
|
|
168
|
-
* @returns {string} Badges formateados como [BADGE1][BADGE2]
|
|
169
|
-
*/
|
|
170
|
-
private getBadgeString(): string {
|
|
171
|
-
if (this.badgeList.length === 0) return '';
|
|
172
|
-
return this.badgeList.map(badge => `[${badge}]`).join('');
|
|
82
|
+
info(...args: any[]): void {
|
|
83
|
+
this.parent.logWithBindings(this.getBindings(), 'info', ...args);
|
|
173
84
|
}
|
|
174
85
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
86
|
+
warn(...args: any[]): void {
|
|
87
|
+
this.parent.logWithBindings(this.getBindings(), 'warn', ...args);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
error(...args: any[]): void {
|
|
91
|
+
this.parent.logWithBindings(this.getBindings(), 'error', ...args);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
success(...args: any[]): void {
|
|
95
|
+
const bindings = this.getBindings();
|
|
96
|
+
this.parent.logWithBindings(bindings, 'info', ...args);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
critical(...args: any[]): void {
|
|
100
|
+
this.parent.logWithBindings(this.getBindings(), 'critical', ...args);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
trace(...args: any[]): void {
|
|
104
|
+
this.parent.logWithBindings(this.getBindings(), 'debug', ...args);
|
|
105
|
+
console.trace(`[${this.scopeName}]`);
|
|
183
106
|
}
|
|
184
107
|
|
|
185
|
-
/**
|
|
186
|
-
* Método interno para añadir contexto
|
|
187
|
-
* @internal
|
|
188
|
-
*/
|
|
189
108
|
_pushContext(context: string): void {
|
|
190
109
|
this.contextStack.push(context);
|
|
191
110
|
}
|
|
192
111
|
|
|
193
|
-
/**
|
|
194
|
-
* Método interno para eliminar contexto
|
|
195
|
-
* @internal
|
|
196
|
-
*/
|
|
197
112
|
_popContext(): void {
|
|
198
113
|
this.contextStack.pop();
|
|
199
114
|
}
|
|
200
115
|
}
|
|
201
116
|
|
|
202
|
-
/**
|
|
203
|
-
* Logger específico para APIs con badges comunes de API
|
|
204
|
-
*
|
|
205
|
-
* @class APILogger
|
|
206
|
-
* @extends ScopedLogger
|
|
207
|
-
* @description Logger especializado para endpoints y servicios API
|
|
208
|
-
*
|
|
209
|
-
* @example
|
|
210
|
-
* const api = logger.api('REST');
|
|
211
|
-
* api.info('GET /users');
|
|
212
|
-
* // [API] [REST] GET /users
|
|
213
|
-
*
|
|
214
|
-
* api.slow('Query lenta', 3500);
|
|
215
|
-
* // [API] [SLOW] [REST] Query lenta (3500ms)
|
|
216
|
-
*
|
|
217
|
-
* @since 0.3.0
|
|
218
|
-
*/
|
|
219
117
|
export class APILogger extends ScopedLogger {
|
|
220
|
-
constructor(
|
|
221
|
-
super(
|
|
118
|
+
constructor(parent: Logger, apiName: string) {
|
|
119
|
+
super(parent, `API:${apiName}`);
|
|
222
120
|
this.badge('API');
|
|
223
121
|
}
|
|
224
122
|
|
|
225
|
-
/**
|
|
226
|
-
* Registra operaciones lentas
|
|
227
|
-
*
|
|
228
|
-
* @param {string} message - Mensaje sobre la operación lenta
|
|
229
|
-
* @param {number} duration - Duración en milisegundos (opcional)
|
|
230
|
-
*
|
|
231
|
-
* @example
|
|
232
|
-
* api.slow('Query compleja', 5000);
|
|
233
|
-
* api.slow('Procesamiento pesado');
|
|
234
|
-
*
|
|
235
|
-
* @since 0.3.0
|
|
236
|
-
*/
|
|
237
123
|
slow(message: string, duration?: number): void {
|
|
238
124
|
this.badge('SLOW');
|
|
239
125
|
const msg = duration ? `${message} (${duration}ms)` : message;
|
|
240
126
|
this.warn(msg);
|
|
241
127
|
}
|
|
242
128
|
|
|
243
|
-
/**
|
|
244
|
-
* Registra límites de tasa alcanzados
|
|
245
|
-
*
|
|
246
|
-
* @param {string} message - Mensaje sobre el límite
|
|
247
|
-
*
|
|
248
|
-
* @example
|
|
249
|
-
* api.rateLimit('Límite excedido: 100 req/min');
|
|
250
|
-
*
|
|
251
|
-
* @since 0.3.0
|
|
252
|
-
*/
|
|
253
129
|
rateLimit(message: string): void {
|
|
254
130
|
this.badge('RATE_LIMIT');
|
|
255
131
|
this.warn(message);
|
|
256
132
|
}
|
|
257
133
|
|
|
258
|
-
/**
|
|
259
|
-
* Registra problemas de autenticación
|
|
260
|
-
*
|
|
261
|
-
* @param {string} message - Mensaje de autenticación
|
|
262
|
-
*
|
|
263
|
-
* @example
|
|
264
|
-
* api.auth('Token inválido o expirado');
|
|
265
|
-
* api.auth('Credenciales incorrectas');
|
|
266
|
-
*
|
|
267
|
-
* @since 0.3.0
|
|
268
|
-
*/
|
|
269
134
|
auth(message: string): void {
|
|
270
135
|
this.badge('AUTH');
|
|
271
136
|
this.error(message);
|
|
272
137
|
}
|
|
273
138
|
|
|
274
|
-
/**
|
|
275
|
-
* Registra advertencias de deprecación
|
|
276
|
-
*
|
|
277
|
-
* @param {string} message - Mensaje de deprecación
|
|
278
|
-
*
|
|
279
|
-
* @example
|
|
280
|
-
* api.deprecated('Este endpoint será eliminado en v2.0');
|
|
281
|
-
* api.deprecated('Usar /v2/users en lugar de /users');
|
|
282
|
-
*
|
|
283
|
-
* @since 0.3.0
|
|
284
|
-
*/
|
|
285
139
|
deprecated(message: string): void {
|
|
286
140
|
this.badge('DEPRECATED');
|
|
287
141
|
this.warn(message);
|
|
288
142
|
}
|
|
289
143
|
}
|
|
290
144
|
|
|
291
|
-
/**
|
|
292
|
-
* Logger específico para componentes de UI
|
|
293
|
-
*
|
|
294
|
-
* @class ComponentLogger
|
|
295
|
-
* @extends ScopedLogger
|
|
296
|
-
* @description Logger especializado para componentes con eventos de ciclo de vida
|
|
297
|
-
*
|
|
298
|
-
* @example
|
|
299
|
-
* const authForm = logger.component('AuthForm');
|
|
300
|
-
* authForm.lifecycle('mounted');
|
|
301
|
-
* authForm.stateChange('idle', 'loading');
|
|
302
|
-
*
|
|
303
|
-
* @since 0.3.0
|
|
304
|
-
*/
|
|
305
145
|
export class ComponentLogger extends ScopedLogger {
|
|
306
|
-
constructor(
|
|
307
|
-
super(
|
|
146
|
+
constructor(parent: Logger, componentName: string) {
|
|
147
|
+
super(parent, componentName);
|
|
308
148
|
this.badge('COMPONENT');
|
|
309
149
|
}
|
|
310
150
|
|
|
311
|
-
/**
|
|
312
|
-
* Registra eventos del ciclo de vida del componente
|
|
313
|
-
*
|
|
314
|
-
* @param {string} event - Nombre del evento (mounted, unmounted, updated, etc)
|
|
315
|
-
* @param {string} message - Mensaje adicional (opcional)
|
|
316
|
-
*
|
|
317
|
-
* @example
|
|
318
|
-
* component.lifecycle('mounted', 'Componente listo');
|
|
319
|
-
* component.lifecycle('beforeDestroy');
|
|
320
|
-
*
|
|
321
|
-
* @since 0.3.0
|
|
322
|
-
*/
|
|
323
151
|
lifecycle(event: string, message?: string): void {
|
|
324
152
|
this.badge('LIFECYCLE');
|
|
325
153
|
const msg = message ? `${event}: ${message}` : event;
|
|
326
154
|
this.info(msg);
|
|
327
155
|
}
|
|
328
156
|
|
|
329
|
-
/**
|
|
330
|
-
* Registra cambios de estado
|
|
331
|
-
*
|
|
332
|
-
* @param {string} from - Estado anterior
|
|
333
|
-
* @param {string} to - Estado nuevo
|
|
334
|
-
* @param {any} data - Datos adicionales (opcional)
|
|
335
|
-
*
|
|
336
|
-
* @example
|
|
337
|
-
* component.stateChange('idle', 'loading');
|
|
338
|
-
* component.stateChange('loading', 'error', { code: 404 });
|
|
339
|
-
*
|
|
340
|
-
* @since 0.3.0
|
|
341
|
-
*/
|
|
342
157
|
stateChange(from: string, to: string, data?: any): void {
|
|
343
158
|
this.badge('STATE');
|
|
344
159
|
const msg = `${from} → ${to}`;
|
|
@@ -349,42 +164,12 @@ export class ComponentLogger extends ScopedLogger {
|
|
|
349
164
|
}
|
|
350
165
|
}
|
|
351
166
|
|
|
352
|
-
/**
|
|
353
|
-
* Registra cambios en las propiedades
|
|
354
|
-
*
|
|
355
|
-
* @param {Record<string, any>} changes - Objeto con los cambios
|
|
356
|
-
*
|
|
357
|
-
* @example
|
|
358
|
-
* component.propsChange({
|
|
359
|
-
* user: newUser,
|
|
360
|
-
* isActive: true
|
|
361
|
-
* });
|
|
362
|
-
*
|
|
363
|
-
* @since 0.3.0
|
|
364
|
-
*/
|
|
365
167
|
propsChange(changes: Record<string, any>): void {
|
|
366
168
|
this.badge('PROPS');
|
|
367
169
|
this.debug('Props changed:', changes);
|
|
368
170
|
}
|
|
369
171
|
}
|
|
370
172
|
|
|
371
|
-
/**
|
|
372
|
-
* Logger de contexto temporal que se limpia automáticamente
|
|
373
|
-
*
|
|
374
|
-
* @class ContextLogger
|
|
375
|
-
* @description Proporciona un contexto temporal para operaciones agrupadas
|
|
376
|
-
*
|
|
377
|
-
* @example
|
|
378
|
-
* const db = logger.scope('Database');
|
|
379
|
-
*
|
|
380
|
-
* // El contexto se limpia automáticamente al finalizar
|
|
381
|
-
* db.context('Transaction').run(() => {
|
|
382
|
-
* db.info('Iniciando transacción');
|
|
383
|
-
* db.success('Transacción completada');
|
|
384
|
-
* });
|
|
385
|
-
*
|
|
386
|
-
* @since 0.3.0
|
|
387
|
-
*/
|
|
388
173
|
export class ContextLogger {
|
|
389
174
|
private parentLogger: ScopedLogger;
|
|
390
175
|
private contextName: string;
|
|
@@ -394,21 +179,6 @@ export class ContextLogger {
|
|
|
394
179
|
this.contextName = contextName;
|
|
395
180
|
}
|
|
396
181
|
|
|
397
|
-
/**
|
|
398
|
-
* Ejecuta una función con este contexto activo
|
|
399
|
-
*
|
|
400
|
-
* @template T - Tipo de retorno de la función
|
|
401
|
-
* @param {Function} fn - Función a ejecutar
|
|
402
|
-
* @returns {T} Resultado de la función
|
|
403
|
-
*
|
|
404
|
-
* @example
|
|
405
|
-
* const result = logger.context('Process').run(() => {
|
|
406
|
-
* logger.info('Procesando...');
|
|
407
|
-
* return processData();
|
|
408
|
-
* });
|
|
409
|
-
*
|
|
410
|
-
* @since 0.3.0
|
|
411
|
-
*/
|
|
412
182
|
run<T>(fn: () => T): T {
|
|
413
183
|
this.parentLogger._pushContext(this.contextName);
|
|
414
184
|
try {
|
|
@@ -418,9 +188,6 @@ export class ContextLogger {
|
|
|
418
188
|
}
|
|
419
189
|
}
|
|
420
190
|
|
|
421
|
-
/**
|
|
422
|
-
* Run an async function with this context active
|
|
423
|
-
*/
|
|
424
191
|
async runAsync<T>(fn: () => Promise<T>): Promise<T> {
|
|
425
192
|
this.parentLogger._pushContext(this.contextName);
|
|
426
193
|
try {
|
|
@@ -430,25 +197,18 @@ export class ContextLogger {
|
|
|
430
197
|
}
|
|
431
198
|
}
|
|
432
199
|
|
|
433
|
-
/**
|
|
434
|
-
* Start the context (manual mode)
|
|
435
|
-
*/
|
|
436
200
|
start(): void {
|
|
437
201
|
this.parentLogger._pushContext(this.contextName);
|
|
438
202
|
}
|
|
439
203
|
|
|
440
|
-
/**
|
|
441
|
-
* End the context (manual mode)
|
|
442
|
-
*/
|
|
443
204
|
end(): void {
|
|
444
205
|
this.parentLogger._popContext();
|
|
445
206
|
}
|
|
446
207
|
|
|
447
|
-
// Delegate logging methods to parent with context
|
|
448
208
|
debug(...args: any[]): void { this.parentLogger.debug(...args); }
|
|
449
209
|
info(...args: any[]): void { this.parentLogger.info(...args); }
|
|
450
210
|
warn(...args: any[]): void { this.parentLogger.warn(...args); }
|
|
451
211
|
error(...args: any[]): void { this.parentLogger.error(...args); }
|
|
452
212
|
success(...args: any[]): void { this.parentLogger.success(...args); }
|
|
453
213
|
critical(...args: any[]): void { this.parentLogger.critical(...args); }
|
|
454
|
-
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HookLogEntry,
|
|
3
|
+
HookEvent,
|
|
4
|
+
HookCallback,
|
|
5
|
+
MiddlewareFn,
|
|
6
|
+
HookRegistration,
|
|
7
|
+
MiddlewareRegistration,
|
|
8
|
+
IHookManager
|
|
9
|
+
} from '../types/index.js';
|
|
10
|
+
|
|
11
|
+
function generateId(): string {
|
|
12
|
+
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class HookManager implements IHookManager {
|
|
16
|
+
private hooks: Map<HookEvent, HookRegistration[]> = new Map();
|
|
17
|
+
private middlewares: MiddlewareRegistration[] = [];
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
this.hooks.set('beforeLog', []);
|
|
21
|
+
this.hooks.set('afterLog', []);
|
|
22
|
+
this.hooks.set('onError', []);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
on(event: HookEvent, callback: HookCallback, priority: number = 50): () => void {
|
|
26
|
+
const registration: HookRegistration = {
|
|
27
|
+
id: generateId(),
|
|
28
|
+
event,
|
|
29
|
+
callback,
|
|
30
|
+
priority,
|
|
31
|
+
once: false
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const hooks = this.hooks.get(event)!;
|
|
35
|
+
hooks.push(registration);
|
|
36
|
+
hooks.sort((a, b) => b.priority - a.priority);
|
|
37
|
+
|
|
38
|
+
return () => this.removeHook(event, registration.id);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
once(event: HookEvent, callback: HookCallback, priority: number = 50): () => void {
|
|
42
|
+
const registration: HookRegistration = {
|
|
43
|
+
id: generateId(),
|
|
44
|
+
event,
|
|
45
|
+
callback,
|
|
46
|
+
priority,
|
|
47
|
+
once: true
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const hooks = this.hooks.get(event)!;
|
|
51
|
+
hooks.push(registration);
|
|
52
|
+
hooks.sort((a, b) => b.priority - a.priority);
|
|
53
|
+
|
|
54
|
+
return () => this.removeHook(event, registration.id);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
off(event: HookEvent, callback: HookCallback): boolean {
|
|
58
|
+
const hooks = this.hooks.get(event);
|
|
59
|
+
if (!hooks) return false;
|
|
60
|
+
|
|
61
|
+
const index = hooks.findIndex(h => h.callback === callback);
|
|
62
|
+
if (index >= 0) {
|
|
63
|
+
hooks.splice(index, 1);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private removeHook(event: HookEvent, id: string): void {
|
|
70
|
+
const hooks = this.hooks.get(event);
|
|
71
|
+
if (hooks) {
|
|
72
|
+
const index = hooks.findIndex(h => h.id === id);
|
|
73
|
+
if (index >= 0) {
|
|
74
|
+
hooks.splice(index, 1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
use(middleware: MiddlewareFn, priority: number = 50): () => void {
|
|
80
|
+
const registration: MiddlewareRegistration = {
|
|
81
|
+
id: generateId(),
|
|
82
|
+
fn: middleware,
|
|
83
|
+
priority
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
this.middlewares.push(registration);
|
|
87
|
+
this.middlewares.sort((a, b) => b.priority - a.priority);
|
|
88
|
+
|
|
89
|
+
return () => {
|
|
90
|
+
const index = this.middlewares.findIndex(m => m.id === registration.id);
|
|
91
|
+
if (index >= 0) {
|
|
92
|
+
this.middlewares.splice(index, 1);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async emit(event: HookEvent, entry: HookLogEntry): Promise<HookLogEntry> {
|
|
98
|
+
const hooks = this.hooks.get(event) || [];
|
|
99
|
+
let currentEntry = { ...entry };
|
|
100
|
+
const toRemove: string[] = [];
|
|
101
|
+
|
|
102
|
+
for (const hook of hooks) {
|
|
103
|
+
try {
|
|
104
|
+
const result = await hook.callback(currentEntry);
|
|
105
|
+
if (result) {
|
|
106
|
+
currentEntry = { ...currentEntry, ...result };
|
|
107
|
+
}
|
|
108
|
+
if (hook.once) {
|
|
109
|
+
toRemove.push(hook.id);
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (event !== 'onError') {
|
|
113
|
+
await this.emit('onError', {
|
|
114
|
+
...currentEntry,
|
|
115
|
+
error,
|
|
116
|
+
hookEvent: event
|
|
117
|
+
} as HookLogEntry);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
toRemove.forEach(id => this.removeHook(event, id));
|
|
123
|
+
return currentEntry;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async process(entry: HookLogEntry): Promise<HookLogEntry> {
|
|
127
|
+
let currentEntry = { ...entry };
|
|
128
|
+
|
|
129
|
+
currentEntry = await this.emit('beforeLog', currentEntry);
|
|
130
|
+
|
|
131
|
+
if (this.middlewares.length > 0) {
|
|
132
|
+
let index = 0;
|
|
133
|
+
|
|
134
|
+
const executeNext = async (): Promise<void> => {
|
|
135
|
+
if (index < this.middlewares.length) {
|
|
136
|
+
const middleware = this.middlewares[index++];
|
|
137
|
+
if (middleware) {
|
|
138
|
+
await middleware.fn(currentEntry, executeNext);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
await executeNext();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return currentEntry;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async afterProcess(entry: HookLogEntry): Promise<void> {
|
|
150
|
+
await this.emit('afterLog', entry);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
clear(): void {
|
|
154
|
+
this.hooks.forEach(hooks => hooks.length = 0);
|
|
155
|
+
this.middlewares.length = 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
getStats(): { hooks: Record<HookEvent, number>; middlewares: number } {
|
|
159
|
+
return {
|
|
160
|
+
hooks: {
|
|
161
|
+
beforeLog: this.hooks.get('beforeLog')?.length || 0,
|
|
162
|
+
afterLog: this.hooks.get('afterLog')?.length || 0,
|
|
163
|
+
onError: this.hooks.get('onError')?.length || 0
|
|
164
|
+
},
|
|
165
|
+
middlewares: this.middlewares.length
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let _defaultHookManager: HookManager | null = null;
|
|
171
|
+
|
|
172
|
+
export function getDefaultHookManager(): HookManager {
|
|
173
|
+
if (!_defaultHookManager) {
|
|
174
|
+
_defaultHookManager = new HookManager();
|
|
175
|
+
}
|
|
176
|
+
return _defaultHookManager;
|
|
177
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { HookManager, getDefaultHookManager } from './HookManager.js';
|