@abdokouta/react-logger 1.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/README.md +559 -0
- package/config/logger.config.ts +171 -0
- package/dist/index.d.cts +1231 -0
- package/dist/index.d.ts +1231 -0
- package/dist/index.js +906 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +886 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +76 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
21
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
22
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
23
|
+
if (decorator = decorators[i])
|
|
24
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
25
|
+
if (kind && result) __defProp(target, key, result);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
29
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var index_exports = {};
|
|
33
|
+
__export(index_exports, {
|
|
34
|
+
ConsoleTransporter: () => ConsoleTransporter,
|
|
35
|
+
JsonFormatter: () => JsonFormatter,
|
|
36
|
+
LogLevel: () => LogLevel,
|
|
37
|
+
Logger: () => LoggerService,
|
|
38
|
+
LoggerModule: () => LoggerModule,
|
|
39
|
+
LoggerService: () => LoggerService,
|
|
40
|
+
PrettyFormatter: () => PrettyFormatter,
|
|
41
|
+
SilentTransporter: () => SilentTransporter,
|
|
42
|
+
SimpleFormatter: () => SimpleFormatter,
|
|
43
|
+
StorageTransporter: () => StorageTransporter,
|
|
44
|
+
defineConfig: () => defineConfig,
|
|
45
|
+
useLogger: () => useLogger,
|
|
46
|
+
useLoggerContext: () => useLoggerContext
|
|
47
|
+
});
|
|
48
|
+
module.exports = __toCommonJS(index_exports);
|
|
49
|
+
|
|
50
|
+
// src/enums/log-level.enum.ts
|
|
51
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
52
|
+
LogLevel2[LogLevel2["Debug"] = 0] = "Debug";
|
|
53
|
+
LogLevel2[LogLevel2["Info"] = 1] = "Info";
|
|
54
|
+
LogLevel2[LogLevel2["Warn"] = 2] = "Warn";
|
|
55
|
+
LogLevel2[LogLevel2["Error"] = 3] = "Error";
|
|
56
|
+
LogLevel2[LogLevel2["Fatal"] = 4] = "Fatal";
|
|
57
|
+
return LogLevel2;
|
|
58
|
+
})(LogLevel || {});
|
|
59
|
+
|
|
60
|
+
// src/services/logger.service.ts
|
|
61
|
+
var import_react_di = require("@abdokouta/react-di");
|
|
62
|
+
|
|
63
|
+
// src/logger.ts
|
|
64
|
+
var Logger = class {
|
|
65
|
+
constructor(config) {
|
|
66
|
+
__publicField(this, "config");
|
|
67
|
+
__publicField(this, "sharedContext", {});
|
|
68
|
+
this.config = config;
|
|
69
|
+
if (config.context) {
|
|
70
|
+
this.sharedContext = { ...config.context };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
debug(message, context) {
|
|
74
|
+
this.log(LogLevel.DEBUG, message, context);
|
|
75
|
+
}
|
|
76
|
+
info(message, context) {
|
|
77
|
+
this.log(LogLevel.INFO, message, context);
|
|
78
|
+
}
|
|
79
|
+
warn(message, context) {
|
|
80
|
+
this.log(LogLevel.WARN, message, context);
|
|
81
|
+
}
|
|
82
|
+
error(message, context) {
|
|
83
|
+
this.log(LogLevel.ERROR, message, context);
|
|
84
|
+
}
|
|
85
|
+
fatal(message, context) {
|
|
86
|
+
this.log(LogLevel.FATAL, message, context);
|
|
87
|
+
}
|
|
88
|
+
withContext(context) {
|
|
89
|
+
this.sharedContext = { ...this.sharedContext, ...context };
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
withoutContext(keys) {
|
|
93
|
+
if (!keys) {
|
|
94
|
+
this.sharedContext = {};
|
|
95
|
+
} else {
|
|
96
|
+
for (const key of keys) {
|
|
97
|
+
delete this.sharedContext[key];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
getTransporters() {
|
|
103
|
+
return this.config.transporters;
|
|
104
|
+
}
|
|
105
|
+
log(level, message, context) {
|
|
106
|
+
const entry = {
|
|
107
|
+
level,
|
|
108
|
+
message,
|
|
109
|
+
context: { ...this.sharedContext, ...context },
|
|
110
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
111
|
+
};
|
|
112
|
+
for (const transporter of this.config.transporters) {
|
|
113
|
+
transporter.log(entry);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// src/constants/tokens.constant.ts
|
|
119
|
+
var LOGGER_CONFIG = /* @__PURE__ */ Symbol("LOGGER_CONFIG");
|
|
120
|
+
|
|
121
|
+
// src/services/logger.service.ts
|
|
122
|
+
var LoggerService = class {
|
|
123
|
+
/**
|
|
124
|
+
* Create a new logger service
|
|
125
|
+
*
|
|
126
|
+
* @param config - Logger configuration
|
|
127
|
+
*/
|
|
128
|
+
constructor(config) {
|
|
129
|
+
/** Logger configuration */
|
|
130
|
+
__publicField(this, "config");
|
|
131
|
+
/** Cached channel instances */
|
|
132
|
+
__publicField(this, "channels", /* @__PURE__ */ new Map());
|
|
133
|
+
/** Default channel instance */
|
|
134
|
+
__publicField(this, "defaultChannel");
|
|
135
|
+
this.config = config;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get a logger channel instance
|
|
139
|
+
*
|
|
140
|
+
* Returns the specified channel, or the default channel if no name is provided.
|
|
141
|
+
* Channels are lazily initialized and cached.
|
|
142
|
+
*
|
|
143
|
+
* @param name - Channel name (uses default if not specified)
|
|
144
|
+
* @returns Logger instance
|
|
145
|
+
* @throws Error if channel is not configured
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* // Use default channel
|
|
150
|
+
* const logger = loggerService.channel();
|
|
151
|
+
* logger.info('Default message');
|
|
152
|
+
*
|
|
153
|
+
* // Use specific channel
|
|
154
|
+
* const errorLogger = loggerService.channel('errors');
|
|
155
|
+
* errorLogger.error('Critical error');
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
channel(name) {
|
|
159
|
+
const channelName = name ?? this.config.default;
|
|
160
|
+
if (this.channels.has(channelName)) {
|
|
161
|
+
return this.channels.get(channelName);
|
|
162
|
+
}
|
|
163
|
+
const channel = this.resolve(channelName);
|
|
164
|
+
this.channels.set(channelName, channel);
|
|
165
|
+
return channel;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Log a message at the debug level (default channel)
|
|
169
|
+
*
|
|
170
|
+
* @param message - The log message
|
|
171
|
+
* @param context - Optional contextual data
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* loggerService.debug('Cache hit', { key: 'user:123' });
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
debug(message, context = {}) {
|
|
179
|
+
this.getDefaultChannel().debug(message, context);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Log a message at the info level (default channel)
|
|
183
|
+
*
|
|
184
|
+
* @param message - The log message
|
|
185
|
+
* @param context - Optional contextual data
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* loggerService.info('User logged in', { userId: '123' });
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
info(message, context = {}) {
|
|
193
|
+
this.getDefaultChannel().info(message, context);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Log a message at the warn level (default channel)
|
|
197
|
+
*
|
|
198
|
+
* @param message - The log message
|
|
199
|
+
* @param context - Optional contextual data
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```typescript
|
|
203
|
+
* loggerService.warn('API rate limit approaching', { remaining: 10 });
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
warn(message, context = {}) {
|
|
207
|
+
this.getDefaultChannel().warn(message, context);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Log a message at the error level (default channel)
|
|
211
|
+
*
|
|
212
|
+
* @param message - The log message
|
|
213
|
+
* @param context - Optional contextual data
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* loggerService.error('Database connection failed', { error });
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
error(message, context = {}) {
|
|
221
|
+
this.getDefaultChannel().error(message, context);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Log a message at the fatal level (default channel)
|
|
225
|
+
*
|
|
226
|
+
* @param message - The log message
|
|
227
|
+
* @param context - Optional contextual data
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* loggerService.fatal('Application crashed', { error, stack });
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
fatal(message, context = {}) {
|
|
235
|
+
this.getDefaultChannel().fatal(message, context);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Add persistent context to the default channel
|
|
239
|
+
*
|
|
240
|
+
* @param context - Key-value pairs to add to the shared context
|
|
241
|
+
* @returns The logger service instance for fluent chaining
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* loggerService
|
|
246
|
+
* .withContext({ requestId: 'abc-123' })
|
|
247
|
+
* .withContext({ userId: 42 })
|
|
248
|
+
* .info('Processing request');
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
withContext(context) {
|
|
252
|
+
this.getDefaultChannel().withContext(context);
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Remove keys from the default channel's shared context
|
|
257
|
+
*
|
|
258
|
+
* @param keys - Optional array of context keys to remove
|
|
259
|
+
* @returns The logger service instance for fluent chaining
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* // Remove specific keys
|
|
264
|
+
* loggerService.withoutContext(['userId', 'requestId']);
|
|
265
|
+
*
|
|
266
|
+
* // Clear all context
|
|
267
|
+
* loggerService.withoutContext();
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
withoutContext(keys) {
|
|
271
|
+
this.getDefaultChannel().withoutContext(keys);
|
|
272
|
+
return this;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get all transporters from the default channel
|
|
276
|
+
*
|
|
277
|
+
* @returns Array of transporter instances
|
|
278
|
+
*/
|
|
279
|
+
getTransporters() {
|
|
280
|
+
return this.getDefaultChannel().getTransporters();
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Get the default channel name
|
|
284
|
+
*
|
|
285
|
+
* @returns Default channel name
|
|
286
|
+
*/
|
|
287
|
+
getDefaultChannelName() {
|
|
288
|
+
return this.config.default;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Get all configured channel names
|
|
292
|
+
*
|
|
293
|
+
* @returns Array of channel names
|
|
294
|
+
*/
|
|
295
|
+
getChannelNames() {
|
|
296
|
+
return Object.keys(this.config.channels);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Check if a channel is configured
|
|
300
|
+
*
|
|
301
|
+
* @param name - Channel name
|
|
302
|
+
* @returns True if the channel is configured
|
|
303
|
+
*/
|
|
304
|
+
hasChannel(name) {
|
|
305
|
+
return name in this.config.channels;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Check if a channel is currently active (cached)
|
|
309
|
+
*
|
|
310
|
+
* @param name - Channel name (uses default if not specified)
|
|
311
|
+
* @returns True if the channel is active
|
|
312
|
+
*/
|
|
313
|
+
isChannelActive(name) {
|
|
314
|
+
const channelName = name ?? this.config.default;
|
|
315
|
+
return this.channels.has(channelName);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Get the default channel instance
|
|
319
|
+
*
|
|
320
|
+
* @returns Logger instance
|
|
321
|
+
* @throws Error if default channel cannot be resolved
|
|
322
|
+
* @private
|
|
323
|
+
*/
|
|
324
|
+
getDefaultChannel() {
|
|
325
|
+
if (this.defaultChannel) {
|
|
326
|
+
return this.defaultChannel;
|
|
327
|
+
}
|
|
328
|
+
const channelName = this.config.default;
|
|
329
|
+
if (this.channels.has(channelName)) {
|
|
330
|
+
this.defaultChannel = this.channels.get(channelName);
|
|
331
|
+
return this.defaultChannel;
|
|
332
|
+
}
|
|
333
|
+
this.defaultChannel = this.resolve(channelName);
|
|
334
|
+
this.channels.set(channelName, this.defaultChannel);
|
|
335
|
+
return this.defaultChannel;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Resolve a channel by name
|
|
339
|
+
*
|
|
340
|
+
* Creates a new logger instance based on channel configuration.
|
|
341
|
+
*
|
|
342
|
+
* @param name - Channel name
|
|
343
|
+
* @returns Logger instance
|
|
344
|
+
* @throws Error if channel is not configured
|
|
345
|
+
* @private
|
|
346
|
+
*/
|
|
347
|
+
resolve(name) {
|
|
348
|
+
const channelConfig = this.config.channels[name];
|
|
349
|
+
if (!channelConfig) {
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Logger channel [${name}] is not configured. Available channels: ${Object.keys(this.config.channels).join(", ")}`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
return new Logger(channelConfig);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
LoggerService = __decorateClass([
|
|
358
|
+
(0, import_react_di.Injectable)(),
|
|
359
|
+
__decorateParam(0, (0, import_react_di.Inject)(LOGGER_CONFIG))
|
|
360
|
+
], LoggerService);
|
|
361
|
+
|
|
362
|
+
// src/logger.module.ts
|
|
363
|
+
var import_react_di2 = require("@abdokouta/react-di");
|
|
364
|
+
|
|
365
|
+
// src/formatters/pretty.formatter.ts
|
|
366
|
+
var LEVEL_EMOJI = {
|
|
367
|
+
[0 /* Debug */]: "\u{1F41B}",
|
|
368
|
+
[1 /* Info */]: "\u2139\uFE0F",
|
|
369
|
+
[2 /* Warn */]: "\u26A0\uFE0F",
|
|
370
|
+
[3 /* Error */]: "\u274C",
|
|
371
|
+
[4 /* Fatal */]: "\u{1F480}"
|
|
372
|
+
};
|
|
373
|
+
var LEVEL_LABEL = {
|
|
374
|
+
[0 /* Debug */]: "DEBUG",
|
|
375
|
+
[1 /* Info */]: "INFO",
|
|
376
|
+
[2 /* Warn */]: "WARN",
|
|
377
|
+
[3 /* Error */]: "ERROR",
|
|
378
|
+
[4 /* Fatal */]: "FATAL"
|
|
379
|
+
};
|
|
380
|
+
var LEVEL_COLORS = {
|
|
381
|
+
[0 /* Debug */]: "color: #8B8B8B",
|
|
382
|
+
[1 /* Info */]: "color: #2196F3",
|
|
383
|
+
[2 /* Warn */]: "color: #FF9800",
|
|
384
|
+
[3 /* Error */]: "color: #F44336",
|
|
385
|
+
[4 /* Fatal */]: "color: #FFFFFF; background: #F44336; font-weight: bold; padding: 1px 4px; border-radius: 2px"
|
|
386
|
+
};
|
|
387
|
+
var PrettyFormatter = class {
|
|
388
|
+
/**
|
|
389
|
+
* Format a log entry into a pretty, human-readable string with
|
|
390
|
+
* emoji prefix, level badge, timestamp, message, and context.
|
|
391
|
+
*
|
|
392
|
+
* The returned string includes `%c` placeholders for CSS styling
|
|
393
|
+
* in the browser console. The ConsoleTransporter is responsible
|
|
394
|
+
* for passing the corresponding style strings.
|
|
395
|
+
*
|
|
396
|
+
* @param entry - The log entry to format.
|
|
397
|
+
* @returns The formatted string with CSS style placeholders.
|
|
398
|
+
*/
|
|
399
|
+
format(entry) {
|
|
400
|
+
const emoji = LEVEL_EMOJI[entry.level];
|
|
401
|
+
const label = LEVEL_LABEL[entry.level];
|
|
402
|
+
const time = this.formatTimestamp(entry.timestamp);
|
|
403
|
+
const contextStr = this.formatContext(entry.context);
|
|
404
|
+
return `${emoji} %c[${label}]%c [${time}] ${entry.message}${contextStr}`;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Extract a short time string (HH:mm:ss.SSS) from an ISO timestamp.
|
|
408
|
+
*
|
|
409
|
+
* @param timestamp - ISO 8601 timestamp string.
|
|
410
|
+
* @returns A short time-only representation.
|
|
411
|
+
*/
|
|
412
|
+
formatTimestamp(timestamp) {
|
|
413
|
+
try {
|
|
414
|
+
const date = new Date(timestamp);
|
|
415
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
416
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
417
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
418
|
+
const ms = String(date.getMilliseconds()).padStart(3, "0");
|
|
419
|
+
return `${hours}:${minutes}:${seconds}.${ms}`;
|
|
420
|
+
} catch {
|
|
421
|
+
return timestamp;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Serialize context data into a compact, readable string.
|
|
426
|
+
* Returns an empty string when context is empty to keep output clean.
|
|
427
|
+
*
|
|
428
|
+
* @param context - The context record to serialize.
|
|
429
|
+
* @returns A formatted context string or empty string.
|
|
430
|
+
*/
|
|
431
|
+
formatContext(context) {
|
|
432
|
+
const keys = Object.keys(context);
|
|
433
|
+
if (keys.length === 0) {
|
|
434
|
+
return "";
|
|
435
|
+
}
|
|
436
|
+
try {
|
|
437
|
+
return ` ${JSON.stringify(context)}`;
|
|
438
|
+
} catch {
|
|
439
|
+
return ` [context serialization failed]`;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// src/formatters/json.formatter.ts
|
|
445
|
+
var LEVEL_STRING = {
|
|
446
|
+
[0 /* Debug */]: "debug",
|
|
447
|
+
[1 /* Info */]: "info",
|
|
448
|
+
[2 /* Warn */]: "warn",
|
|
449
|
+
[3 /* Error */]: "error",
|
|
450
|
+
[4 /* Fatal */]: "fatal"
|
|
451
|
+
};
|
|
452
|
+
var JsonFormatter = class {
|
|
453
|
+
/**
|
|
454
|
+
* Format a log entry as a JSON string.
|
|
455
|
+
*
|
|
456
|
+
* Produces a flat JSON object with level (as string), message,
|
|
457
|
+
* timestamp, and context fields. Context is omitted when empty
|
|
458
|
+
* to keep output compact.
|
|
459
|
+
*
|
|
460
|
+
* @param entry - The log entry to format.
|
|
461
|
+
* @returns A JSON-serialized string representation of the entry.
|
|
462
|
+
*/
|
|
463
|
+
format(entry) {
|
|
464
|
+
const payload = {
|
|
465
|
+
level: LEVEL_STRING[entry.level],
|
|
466
|
+
message: entry.message,
|
|
467
|
+
timestamp: entry.timestamp
|
|
468
|
+
};
|
|
469
|
+
if (Object.keys(entry.context).length > 0) {
|
|
470
|
+
payload.context = entry.context;
|
|
471
|
+
}
|
|
472
|
+
try {
|
|
473
|
+
return JSON.stringify(payload);
|
|
474
|
+
} catch {
|
|
475
|
+
return JSON.stringify({
|
|
476
|
+
level: LEVEL_STRING[entry.level],
|
|
477
|
+
message: entry.message,
|
|
478
|
+
timestamp: entry.timestamp,
|
|
479
|
+
error: "context serialization failed"
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
// src/formatters/simple.formatter.ts
|
|
486
|
+
var LEVEL_LABEL2 = {
|
|
487
|
+
[0 /* Debug */]: "DEBUG",
|
|
488
|
+
[1 /* Info */]: "INFO",
|
|
489
|
+
[2 /* Warn */]: "WARN",
|
|
490
|
+
[3 /* Error */]: "ERROR",
|
|
491
|
+
[4 /* Fatal */]: "FATAL"
|
|
492
|
+
};
|
|
493
|
+
var SimpleFormatter = class {
|
|
494
|
+
/**
|
|
495
|
+
* Format a log entry into a plain text line.
|
|
496
|
+
*
|
|
497
|
+
* Output pattern: `[LEVEL] [timestamp] message {context}`
|
|
498
|
+
*
|
|
499
|
+
* @param entry - The log entry to format.
|
|
500
|
+
* @returns A plain text string with no styling.
|
|
501
|
+
*/
|
|
502
|
+
format(entry) {
|
|
503
|
+
const label = LEVEL_LABEL2[entry.level];
|
|
504
|
+
const contextStr = this.formatContext(entry.context);
|
|
505
|
+
return `[${label}] [${entry.timestamp}] ${entry.message}${contextStr}`;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Serialize context data into a compact string.
|
|
509
|
+
* Returns an empty string when context is empty.
|
|
510
|
+
*
|
|
511
|
+
* @param context - The context record to serialize.
|
|
512
|
+
* @returns A formatted context string or empty string.
|
|
513
|
+
*/
|
|
514
|
+
formatContext(context) {
|
|
515
|
+
const keys = Object.keys(context);
|
|
516
|
+
if (keys.length === 0) {
|
|
517
|
+
return "";
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
return ` ${JSON.stringify(context)}`;
|
|
521
|
+
} catch {
|
|
522
|
+
return " [context serialization failed]";
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
// src/transporters/silent.transporter.ts
|
|
528
|
+
var SilentTransporter = class {
|
|
529
|
+
/**
|
|
530
|
+
* Create a new SilentTransporter instance.
|
|
531
|
+
* All parameters are optional and exist only for interface compliance.
|
|
532
|
+
*/
|
|
533
|
+
constructor() {
|
|
534
|
+
/**
|
|
535
|
+
The formatter instance (maintained for interface compliance). */
|
|
536
|
+
__publicField(this, "_formatter");
|
|
537
|
+
/**
|
|
538
|
+
The minimum log level (maintained for interface compliance). */
|
|
539
|
+
__publicField(this, "_level");
|
|
540
|
+
this._formatter = new SimpleFormatter();
|
|
541
|
+
this._level = 0 /* Debug */;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* No-op transport method. Silently discards the entry.
|
|
545
|
+
*
|
|
546
|
+
* @param _entry - The log entry (ignored).
|
|
547
|
+
*/
|
|
548
|
+
transport(_entry) {
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Replace the current formatter.
|
|
552
|
+
*
|
|
553
|
+
* @param formatter - The new formatter instance.
|
|
554
|
+
*/
|
|
555
|
+
setFormatter(formatter) {
|
|
556
|
+
this._formatter = formatter;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Retrieve the currently assigned formatter.
|
|
560
|
+
*
|
|
561
|
+
* @returns The active formatter instance.
|
|
562
|
+
*/
|
|
563
|
+
getFormatter() {
|
|
564
|
+
return this._formatter;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Get the minimum log level.
|
|
568
|
+
*
|
|
569
|
+
* @returns The current minimum LogLevel threshold.
|
|
570
|
+
*/
|
|
571
|
+
getLevel() {
|
|
572
|
+
return this._level;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Set the minimum log level.
|
|
576
|
+
*
|
|
577
|
+
* @param level - The new minimum LogLevel threshold.
|
|
578
|
+
*/
|
|
579
|
+
setLevel(level) {
|
|
580
|
+
this._level = level;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
// src/transporters/console.transporter.ts
|
|
585
|
+
var ConsoleTransporter = class {
|
|
586
|
+
/**
|
|
587
|
+
* Create a new ConsoleTransporter instance.
|
|
588
|
+
*
|
|
589
|
+
* @param options - Optional configuration for formatter and minimum level.
|
|
590
|
+
*/
|
|
591
|
+
constructor(options = {}) {
|
|
592
|
+
/**
|
|
593
|
+
The formatter used to convert log entries into output strings. */
|
|
594
|
+
__publicField(this, "_formatter");
|
|
595
|
+
/**
|
|
596
|
+
The minimum log level threshold for this transporter. */
|
|
597
|
+
__publicField(this, "_level");
|
|
598
|
+
this._formatter = options.formatter ?? new PrettyFormatter();
|
|
599
|
+
this._level = options.level ?? 0 /* Debug */;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Deliver a log entry to the browser console.
|
|
603
|
+
*
|
|
604
|
+
* Routes the entry to the appropriate `console.*` method based on
|
|
605
|
+
* the log level. When using the PrettyFormatter, CSS styles are
|
|
606
|
+
* applied via `%c` placeholders for colored output.
|
|
607
|
+
*
|
|
608
|
+
* Entries below the configured minimum level are silently skipped.
|
|
609
|
+
*
|
|
610
|
+
* @param entry - The log entry to output.
|
|
611
|
+
*/
|
|
612
|
+
transport(entry) {
|
|
613
|
+
if (entry.level < this._level) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const formatted = this._formatter.format(entry);
|
|
617
|
+
const method = this.resolveConsoleMethod(entry.level);
|
|
618
|
+
if (this._formatter instanceof PrettyFormatter) {
|
|
619
|
+
const levelStyle = LEVEL_COLORS[entry.level];
|
|
620
|
+
const resetStyle = "color: inherit";
|
|
621
|
+
method(formatted, levelStyle, resetStyle);
|
|
622
|
+
} else {
|
|
623
|
+
method(formatted);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Replace the current formatter.
|
|
628
|
+
*
|
|
629
|
+
* @param formatter - The new formatter instance to use.
|
|
630
|
+
*/
|
|
631
|
+
setFormatter(formatter) {
|
|
632
|
+
this._formatter = formatter;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Retrieve the currently assigned formatter.
|
|
636
|
+
*
|
|
637
|
+
* @returns The active formatter instance.
|
|
638
|
+
*/
|
|
639
|
+
getFormatter() {
|
|
640
|
+
return this._formatter;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get the minimum log level this transporter handles.
|
|
644
|
+
*
|
|
645
|
+
* @returns The current minimum LogLevel threshold.
|
|
646
|
+
*/
|
|
647
|
+
getLevel() {
|
|
648
|
+
return this._level;
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Set the minimum log level this transporter handles.
|
|
652
|
+
*
|
|
653
|
+
* @param level - The new minimum LogLevel threshold.
|
|
654
|
+
*/
|
|
655
|
+
setLevel(level) {
|
|
656
|
+
this._level = level;
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Resolve the appropriate `console.*` method for a given log level.
|
|
660
|
+
*
|
|
661
|
+
* Maps logger severity levels to the closest browser console method
|
|
662
|
+
* to ensure proper DevTools filtering and visual distinction.
|
|
663
|
+
*
|
|
664
|
+
* @param level - The log level to resolve.
|
|
665
|
+
* @returns The bound console method function.
|
|
666
|
+
*/
|
|
667
|
+
resolveConsoleMethod(level) {
|
|
668
|
+
switch (level) {
|
|
669
|
+
case 0 /* Debug */:
|
|
670
|
+
return console.debug.bind(console);
|
|
671
|
+
case 1 /* Info */:
|
|
672
|
+
return console.info.bind(console);
|
|
673
|
+
case 2 /* Warn */:
|
|
674
|
+
return console.warn.bind(console);
|
|
675
|
+
case 3 /* Error */:
|
|
676
|
+
return console.error.bind(console);
|
|
677
|
+
case 4 /* Fatal */:
|
|
678
|
+
return console.error.bind(console);
|
|
679
|
+
default:
|
|
680
|
+
return console.log.bind(console);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// src/logger.module.ts
|
|
686
|
+
var LoggerModule = class {
|
|
687
|
+
/**
|
|
688
|
+
* Configure the logger module
|
|
689
|
+
*
|
|
690
|
+
* @param config - Logger configuration (can be passed directly without defineConfig)
|
|
691
|
+
* @returns Dynamic module
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* ```typescript
|
|
695
|
+
* LoggerModule.forRoot({
|
|
696
|
+
* default: 'console',
|
|
697
|
+
* channels: {
|
|
698
|
+
* console: {
|
|
699
|
+
* transporters: [new ConsoleTransporter()],
|
|
700
|
+
* },
|
|
701
|
+
* },
|
|
702
|
+
* })
|
|
703
|
+
* ```
|
|
704
|
+
*/
|
|
705
|
+
static forRoot(config) {
|
|
706
|
+
const processedConfig = LoggerModule.processConfig(config);
|
|
707
|
+
return (0, import_react_di2.forRoot)(LoggerModule, {
|
|
708
|
+
providers: [
|
|
709
|
+
{
|
|
710
|
+
provide: LOGGER_CONFIG,
|
|
711
|
+
useValue: processedConfig
|
|
712
|
+
},
|
|
713
|
+
LoggerService
|
|
714
|
+
],
|
|
715
|
+
exports: [LoggerService, LOGGER_CONFIG]
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Process configuration to add default transporters if needed
|
|
720
|
+
*
|
|
721
|
+
* @param config - Raw configuration
|
|
722
|
+
* @returns Processed configuration with defaults
|
|
723
|
+
* @private
|
|
724
|
+
*/
|
|
725
|
+
static processConfig(config) {
|
|
726
|
+
const processedChannels = {};
|
|
727
|
+
for (const name in config.channels) {
|
|
728
|
+
if (Object.prototype.hasOwnProperty.call(config.channels, name)) {
|
|
729
|
+
const channelConfig = config.channels[name];
|
|
730
|
+
if (!channelConfig) continue;
|
|
731
|
+
if (!channelConfig.transporters || channelConfig.transporters.length === 0) {
|
|
732
|
+
if (name === "silent") {
|
|
733
|
+
processedChannels[name] = {
|
|
734
|
+
...channelConfig,
|
|
735
|
+
transporters: [new SilentTransporter()]
|
|
736
|
+
};
|
|
737
|
+
} else {
|
|
738
|
+
processedChannels[name] = {
|
|
739
|
+
...channelConfig,
|
|
740
|
+
transporters: [new ConsoleTransporter()]
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
} else {
|
|
744
|
+
processedChannels[name] = channelConfig;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return {
|
|
749
|
+
...config,
|
|
750
|
+
channels: processedChannels
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
LoggerModule = __decorateClass([
|
|
755
|
+
(0, import_react_di2.Module)({})
|
|
756
|
+
], LoggerModule);
|
|
757
|
+
|
|
758
|
+
// src/config/logger.config.ts
|
|
759
|
+
function defineConfig(config) {
|
|
760
|
+
return config;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/transporters/storage.transporter.ts
|
|
764
|
+
var DEFAULT_STORAGE_KEY = "logger:entries";
|
|
765
|
+
var DEFAULT_MAX_ENTRIES = 100;
|
|
766
|
+
var StorageTransporter = class {
|
|
767
|
+
/**
|
|
768
|
+
* Create a new StorageTransporter instance.
|
|
769
|
+
*
|
|
770
|
+
* @param options - Optional configuration for storage key, limits, and formatter.
|
|
771
|
+
*/
|
|
772
|
+
constructor(options = {}) {
|
|
773
|
+
/**
|
|
774
|
+
The formatter used to convert log entries into storable strings. */
|
|
775
|
+
__publicField(this, "_formatter");
|
|
776
|
+
/**
|
|
777
|
+
The minimum log level threshold for this transporter. */
|
|
778
|
+
__publicField(this, "_level");
|
|
779
|
+
/**
|
|
780
|
+
The localStorage key for persisting entries. */
|
|
781
|
+
__publicField(this, "_key");
|
|
782
|
+
/**
|
|
783
|
+
Maximum number of entries to retain. */
|
|
784
|
+
__publicField(this, "_maxEntries");
|
|
785
|
+
this._formatter = options.formatter ?? new JsonFormatter();
|
|
786
|
+
this._level = options.level ?? 0 /* Debug */;
|
|
787
|
+
this._key = options.key ?? DEFAULT_STORAGE_KEY;
|
|
788
|
+
this._maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Persist a log entry to localStorage.
|
|
792
|
+
*
|
|
793
|
+
* The entry is formatted, appended to the existing entries array,
|
|
794
|
+
* and trimmed to the maximum entry limit. If localStorage is
|
|
795
|
+
* unavailable or full, the error is silently swallowed to avoid
|
|
796
|
+
* crashing the application.
|
|
797
|
+
*
|
|
798
|
+
* @param entry - The log entry to persist.
|
|
799
|
+
*/
|
|
800
|
+
transport(entry) {
|
|
801
|
+
if (entry.level < this._level) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
try {
|
|
805
|
+
const formatted = this._formatter.format(entry);
|
|
806
|
+
const entries = this.readEntries();
|
|
807
|
+
entries.push(formatted);
|
|
808
|
+
while (entries.length > this._maxEntries) {
|
|
809
|
+
entries.shift();
|
|
810
|
+
}
|
|
811
|
+
localStorage.setItem(this._key, JSON.stringify(entries));
|
|
812
|
+
} catch {
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Replace the current formatter.
|
|
817
|
+
*
|
|
818
|
+
* @param formatter - The new formatter instance.
|
|
819
|
+
*/
|
|
820
|
+
setFormatter(formatter) {
|
|
821
|
+
this._formatter = formatter;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Retrieve the currently assigned formatter.
|
|
825
|
+
*
|
|
826
|
+
* @returns The active formatter instance.
|
|
827
|
+
*/
|
|
828
|
+
getFormatter() {
|
|
829
|
+
return this._formatter;
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Get the minimum log level this transporter handles.
|
|
833
|
+
*
|
|
834
|
+
* @returns The current minimum LogLevel threshold.
|
|
835
|
+
*/
|
|
836
|
+
getLevel() {
|
|
837
|
+
return this._level;
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Set the minimum log level this transporter handles.
|
|
841
|
+
*
|
|
842
|
+
* @param level - The new minimum LogLevel threshold.
|
|
843
|
+
*/
|
|
844
|
+
setLevel(level) {
|
|
845
|
+
this._level = level;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Clear all stored log entries from localStorage.
|
|
849
|
+
* Useful for manual cleanup or when resetting application state.
|
|
850
|
+
*/
|
|
851
|
+
clear() {
|
|
852
|
+
try {
|
|
853
|
+
localStorage.removeItem(this._key);
|
|
854
|
+
} catch {
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Retrieve all stored log entries from localStorage.
|
|
859
|
+
*
|
|
860
|
+
* @returns An array of formatted log entry strings.
|
|
861
|
+
*/
|
|
862
|
+
getEntries() {
|
|
863
|
+
return this.readEntries();
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Read the current entries array from localStorage.
|
|
867
|
+
* Returns an empty array if the key does not exist or parsing fails.
|
|
868
|
+
*
|
|
869
|
+
* @returns The parsed entries array.
|
|
870
|
+
*/
|
|
871
|
+
readEntries() {
|
|
872
|
+
try {
|
|
873
|
+
const raw = localStorage.getItem(this._key);
|
|
874
|
+
if (!raw) {
|
|
875
|
+
return [];
|
|
876
|
+
}
|
|
877
|
+
const parsed = JSON.parse(raw);
|
|
878
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
879
|
+
} catch {
|
|
880
|
+
return [];
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
// src/hooks/use-logger/use-logger.hook.ts
|
|
886
|
+
var import_react_di3 = require("@abdokouta/react-di");
|
|
887
|
+
function useLogger(channelName) {
|
|
888
|
+
const loggerService = (0, import_react_di3.useInject)(LoggerService);
|
|
889
|
+
if (channelName) {
|
|
890
|
+
return loggerService.channel(channelName);
|
|
891
|
+
}
|
|
892
|
+
return loggerService;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/hooks/use-logger-context/use-logger-context.hook.ts
|
|
896
|
+
var import_react = require("react");
|
|
897
|
+
function useLoggerContext(context, channelName) {
|
|
898
|
+
const logger = useLogger(channelName);
|
|
899
|
+
(0, import_react.useEffect)(() => {
|
|
900
|
+
logger.withContext(context);
|
|
901
|
+
return () => {
|
|
902
|
+
logger.withoutContext(Object.keys(context));
|
|
903
|
+
};
|
|
904
|
+
}, [logger, context]);
|
|
905
|
+
}
|
|
906
|
+
//# sourceMappingURL=index.js.map
|