@ncoderz/log-m8 1.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/LICENSE +18 -0
- package/README.md +296 -0
- package/dist/browser/log-m8.global.js +1 -0
- package/dist/browser/log-m8.global.js.map +1 -0
- package/dist/index.cjs +1412 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1276 -0
- package/dist/index.d.ts +1276 -0
- package/dist/index.js +1384 -0
- package/dist/index.js.map +1 -0
- package/package.json +88 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1384 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// src/LogLevel.ts
|
|
6
|
+
var LogLevel = {
|
|
7
|
+
off: "off",
|
|
8
|
+
// No logging
|
|
9
|
+
fatal: "fatal",
|
|
10
|
+
error: "error",
|
|
11
|
+
warn: "warn",
|
|
12
|
+
info: "info",
|
|
13
|
+
debug: "debug",
|
|
14
|
+
track: "track",
|
|
15
|
+
// Special log level for analytics
|
|
16
|
+
trace: "trace"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/PluginKind.ts
|
|
20
|
+
var PluginKind = {
|
|
21
|
+
appender: "appender",
|
|
22
|
+
// Log output destinations (console, file, network, etc.)
|
|
23
|
+
filter: "filter",
|
|
24
|
+
// Event filtering logic (level, content, rate limiting, etc.)
|
|
25
|
+
formatter: "formatter"
|
|
26
|
+
// Event formatting (text, JSON, custom templates, etc.)
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/appenders/ConsoleAppender.ts
|
|
30
|
+
var NAME = "console";
|
|
31
|
+
var VERSION = "1.0.0";
|
|
32
|
+
var KIND = PluginKind.appender;
|
|
33
|
+
var SUPPORTED_LEVELS = /* @__PURE__ */ new Set([
|
|
34
|
+
LogLevel.fatal,
|
|
35
|
+
LogLevel.error,
|
|
36
|
+
LogLevel.warn,
|
|
37
|
+
LogLevel.info,
|
|
38
|
+
LogLevel.debug,
|
|
39
|
+
LogLevel.track,
|
|
40
|
+
LogLevel.trace
|
|
41
|
+
]);
|
|
42
|
+
var ConsoleAppender = class {
|
|
43
|
+
constructor() {
|
|
44
|
+
__publicField(this, "name", NAME);
|
|
45
|
+
__publicField(this, "version", VERSION);
|
|
46
|
+
__publicField(this, "kind", KIND);
|
|
47
|
+
__publicField(this, "supportedLevels", SUPPORTED_LEVELS);
|
|
48
|
+
__publicField(this, "enabled", true);
|
|
49
|
+
__publicField(this, "priority");
|
|
50
|
+
__publicField(this, "_config");
|
|
51
|
+
__publicField(this, "_formatter");
|
|
52
|
+
__publicField(this, "_filters", []);
|
|
53
|
+
__publicField(this, "_available", true);
|
|
54
|
+
// Console method mapping with fallbacks for missing methods
|
|
55
|
+
__publicField(this, "off", () => {
|
|
56
|
+
});
|
|
57
|
+
__publicField(this, "fatal", console.error ? console.error.bind(console) : console.log.bind(console));
|
|
58
|
+
__publicField(this, "error", console.error ? console.error.bind(console) : console.log.bind(console));
|
|
59
|
+
__publicField(this, "warn", console.warn ? console.warn.bind(console) : console.log.bind(console));
|
|
60
|
+
__publicField(this, "info", console.info ? console.info.bind(console) : console.log.bind(console));
|
|
61
|
+
__publicField(this, "debug", console.debug ? console.debug.bind(console) : console.log.bind(console));
|
|
62
|
+
// Avoid console.trace as it captures stack traces and is significantly slower; prefer debug/log
|
|
63
|
+
__publicField(this, "trace", console.debug ? console.debug.bind(console) : console.log.bind(console));
|
|
64
|
+
__publicField(this, "track", console.log.bind(console));
|
|
65
|
+
}
|
|
66
|
+
init(config, formatter, filters) {
|
|
67
|
+
this._config = config;
|
|
68
|
+
this._formatter = formatter;
|
|
69
|
+
this._filters = filters || [];
|
|
70
|
+
this._available = typeof console !== "undefined" && !!console.log;
|
|
71
|
+
this.enabled = this._config?.enabled !== false;
|
|
72
|
+
this.priority = this._config?.priority;
|
|
73
|
+
}
|
|
74
|
+
dispose() {
|
|
75
|
+
}
|
|
76
|
+
write(event) {
|
|
77
|
+
if (!this._available) return;
|
|
78
|
+
for (const filter of this._filters) {
|
|
79
|
+
if (filter.enabled && !filter.filter(event)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const data = this._formatter ? this._formatter.format(event) : [event];
|
|
84
|
+
this[event.level](...data);
|
|
85
|
+
}
|
|
86
|
+
flush() {
|
|
87
|
+
}
|
|
88
|
+
enableFilter(name) {
|
|
89
|
+
const filter = this._getFilter(name);
|
|
90
|
+
if (!filter) return;
|
|
91
|
+
filter.enabled = true;
|
|
92
|
+
}
|
|
93
|
+
disableFilter(name) {
|
|
94
|
+
const filter = this._getFilter(name);
|
|
95
|
+
if (!filter) return;
|
|
96
|
+
filter.enabled = false;
|
|
97
|
+
}
|
|
98
|
+
_getFilter(name) {
|
|
99
|
+
return this._filters.find((f) => f.name === name);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var ConsoleAppenderFactory = class {
|
|
103
|
+
constructor() {
|
|
104
|
+
__publicField(this, "name", NAME);
|
|
105
|
+
__publicField(this, "version", VERSION);
|
|
106
|
+
__publicField(this, "kind", KIND);
|
|
107
|
+
}
|
|
108
|
+
create(config) {
|
|
109
|
+
const appender = new ConsoleAppender();
|
|
110
|
+
appender.init(config);
|
|
111
|
+
return appender;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// src/appenders/FileAppender.ts
|
|
116
|
+
import { createWriteStream } from "fs";
|
|
117
|
+
|
|
118
|
+
// src/LogM8Utils.ts
|
|
119
|
+
var TIMESTAMP_TOKEN_REGEX = /(yyyy|SSS|hh|mm|ss|SS|zz|z|yy|MM|dd|A|a|h|S)/g;
|
|
120
|
+
var EXCLUDED_ERROR_KEYS = /* @__PURE__ */ new Set(["name", "message", "stack", "cause"]);
|
|
121
|
+
var COMMON_NON_ENUMERABLE_PROPS = ["code", "errno", "syscall", "path"];
|
|
122
|
+
var LogM8Utils = class _LogM8Utils {
|
|
123
|
+
/**
|
|
124
|
+
* Detects browser environment for feature compatibility.
|
|
125
|
+
*
|
|
126
|
+
* @returns True when both window and document global objects are available
|
|
127
|
+
*/
|
|
128
|
+
static isBrowser() {
|
|
129
|
+
return typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if an object is a string.
|
|
133
|
+
*
|
|
134
|
+
* @param obj - The object to check.
|
|
135
|
+
* @returns true if the object is a string, otherwise false.
|
|
136
|
+
*/
|
|
137
|
+
static isString(obj) {
|
|
138
|
+
return typeof obj === "string" || obj instanceof String;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Traverses nested object properties using dot-separated path notation.
|
|
142
|
+
*
|
|
143
|
+
* Supports both object property access and array indexing with numeric keys.
|
|
144
|
+
* Also supports bracket notation for array indices which is normalized internally
|
|
145
|
+
* (e.g., `data[0].items[2]` becomes `data.0.items.2`).
|
|
146
|
+
* Safe navigation that returns undefined for invalid paths rather than throwing.
|
|
147
|
+
*
|
|
148
|
+
* @param obj - Source object to traverse
|
|
149
|
+
* @param path - Dot-separated property path (e.g., 'user.profile.name', 'items.0.id')
|
|
150
|
+
* or a path with bracket indices (e.g., 'items[0].id')
|
|
151
|
+
* @returns Property value at the specified path, or undefined if not found
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const data = { user: { profile: { name: 'John' } }, items: [{ id: 1 }, { id: 2 }] };
|
|
156
|
+
* getPropertyByPath(data, 'user.profile.name'); // 'John'
|
|
157
|
+
* getPropertyByPath(data, 'items.0.id'); // 1
|
|
158
|
+
* getPropertyByPath(data, 'items[1].id'); // 2 (bracket notation)
|
|
159
|
+
* getPropertyByPath(data, 'missing.path'); // undefined
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
static getPropertyByPath(obj, path) {
|
|
163
|
+
let value = obj;
|
|
164
|
+
const normalized = path.replace(/\[(\d+)\]/g, ".$1");
|
|
165
|
+
const segments = normalized.split(".");
|
|
166
|
+
for (const key of segments) {
|
|
167
|
+
if (typeof value === "object" && value !== null) {
|
|
168
|
+
if (Array.isArray(value)) {
|
|
169
|
+
const idx = Number(key);
|
|
170
|
+
if (Number.isInteger(idx) && idx >= 0) {
|
|
171
|
+
value = value[idx];
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
value = value[key];
|
|
176
|
+
} else {
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Formats Date objects using preset formats or custom token patterns.
|
|
184
|
+
*
|
|
185
|
+
* Supports common presets ('iso', 'locale') and flexible token-based formatting
|
|
186
|
+
* for complete control over timestamp appearance. Tokens are replaced with
|
|
187
|
+
* corresponding date/time components, while non-token text is preserved literally.
|
|
188
|
+
*
|
|
189
|
+
* Supported format tokens:
|
|
190
|
+
* - yyyy: 4-digit year (2025)
|
|
191
|
+
* - yy: 2-digit year (25)
|
|
192
|
+
* - MM: month with leading zero (01-12)
|
|
193
|
+
* - dd: day with leading zero (01-31)
|
|
194
|
+
* - hh: 24-hour format hour with leading zero (00-23)
|
|
195
|
+
* - h: 12-hour format hour (1-12)
|
|
196
|
+
* - mm: minutes with leading zero (00-59)
|
|
197
|
+
* - ss: seconds with leading zero (00-59)
|
|
198
|
+
* - SSS: milliseconds with leading zeros (000-999)
|
|
199
|
+
* - SS: centiseconds with leading zero (00-99)
|
|
200
|
+
* - S: deciseconds (0-9)
|
|
201
|
+
* - A: uppercase AM/PM
|
|
202
|
+
* - a: lowercase am/pm
|
|
203
|
+
* - z: timezone offset with colon (±HH:MM)
|
|
204
|
+
* - zz: timezone offset without colon (±HHMM)
|
|
205
|
+
*
|
|
206
|
+
* @param date - Date instance to format
|
|
207
|
+
* @param fmt - Format preset ('iso'|'locale') or custom token pattern
|
|
208
|
+
* @returns Formatted timestamp string
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* const date = new Date('2025-08-04T14:23:45.123Z');
|
|
213
|
+
*
|
|
214
|
+
* // Presets
|
|
215
|
+
* formatTimestamp(date, 'iso'); // '2025-08-04T14:23:45.123Z'
|
|
216
|
+
* formatTimestamp(date, 'locale'); // '8/4/2025, 2:23:45 PM' (locale-dependent)
|
|
217
|
+
*
|
|
218
|
+
* // Custom patterns
|
|
219
|
+
* formatTimestamp(date, 'yyyy-MM-dd hh:mm:ss'); // '2025-08-04 14:23:45'
|
|
220
|
+
* formatTimestamp(date, 'MM/dd/yyyy h:mm A'); // '08/04/2025 2:23 PM'
|
|
221
|
+
* formatTimestamp(date, 'hh:mm:ss.SSS'); // '14:23:45.123'
|
|
222
|
+
* formatTimestamp(date, 'yyyy-MM-dd hh:mm:ss z'); // '2025-08-04 14:23:45 +00:00'
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
static formatTimestamp(date, fmt) {
|
|
226
|
+
const fmtLower = fmt?.toLowerCase();
|
|
227
|
+
if (!fmt || fmtLower === "iso" || fmtLower === "toisostring") {
|
|
228
|
+
return date.toISOString();
|
|
229
|
+
}
|
|
230
|
+
if (fmtLower === "locale" || fmtLower === "tolocalestring") {
|
|
231
|
+
return date.toLocaleString();
|
|
232
|
+
}
|
|
233
|
+
const pad = (n, z = 2) => String(n).padStart(z, "0");
|
|
234
|
+
const hours24 = date.getHours();
|
|
235
|
+
const hours12 = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
|
236
|
+
return fmt.replace(TIMESTAMP_TOKEN_REGEX, (m) => {
|
|
237
|
+
switch (m) {
|
|
238
|
+
case "yyyy":
|
|
239
|
+
return pad(date.getFullYear(), 4);
|
|
240
|
+
case "yy":
|
|
241
|
+
return pad(date.getFullYear() % 100);
|
|
242
|
+
case "MM":
|
|
243
|
+
return pad(date.getMonth() + 1);
|
|
244
|
+
case "dd":
|
|
245
|
+
return pad(date.getDate());
|
|
246
|
+
case "hh":
|
|
247
|
+
return pad(hours24);
|
|
248
|
+
case "h":
|
|
249
|
+
return pad(hours12);
|
|
250
|
+
case "mm":
|
|
251
|
+
return pad(date.getMinutes());
|
|
252
|
+
case "ss":
|
|
253
|
+
return pad(date.getSeconds());
|
|
254
|
+
case "SSS":
|
|
255
|
+
return pad(date.getMilliseconds(), 3);
|
|
256
|
+
case "SS":
|
|
257
|
+
return pad(Math.floor(date.getMilliseconds() / 10), 2);
|
|
258
|
+
case "S":
|
|
259
|
+
return pad(Math.floor(date.getMilliseconds() / 100), 1);
|
|
260
|
+
case "A":
|
|
261
|
+
return hours24 < 12 ? "AM" : "PM";
|
|
262
|
+
case "a":
|
|
263
|
+
return hours24 < 12 ? "am" : "pm";
|
|
264
|
+
case "z":
|
|
265
|
+
case "zz": {
|
|
266
|
+
const tzOffset = -date.getTimezoneOffset();
|
|
267
|
+
const tzSign = tzOffset >= 0 ? "+" : "-";
|
|
268
|
+
const tzHours = Math.floor(Math.abs(tzOffset) / 60);
|
|
269
|
+
const tzMinutes = Math.abs(tzOffset) % 60;
|
|
270
|
+
if (m === "z") {
|
|
271
|
+
return `${tzSign}${pad(tzHours)}:${pad(tzMinutes)}`;
|
|
272
|
+
}
|
|
273
|
+
return `${tzSign}${pad(tzHours)}${pad(tzMinutes)}`;
|
|
274
|
+
}
|
|
275
|
+
default:
|
|
276
|
+
return m;
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Converts arbitrary values into JSON strings optimized for logging systems.
|
|
282
|
+
*
|
|
283
|
+
* This utility ensures that any JavaScript value can be safely logged without causing
|
|
284
|
+
* serialization errors or producing excessively large output. It's designed specifically
|
|
285
|
+
* for logging contexts where reliability and readability are more important than
|
|
286
|
+
* perfect fidelity.
|
|
287
|
+
*
|
|
288
|
+
* ## Key Features
|
|
289
|
+
*
|
|
290
|
+
* **Depth Protection**: Prevents stack overflows and excessive output by limiting
|
|
291
|
+
* object traversal depth. Objects/arrays beyond the limit are replaced with
|
|
292
|
+
* "[Object]" or "[Array]" placeholders.
|
|
293
|
+
*
|
|
294
|
+
* **Array Length Limiting**: Automatically truncates arrays that exceed the maximum
|
|
295
|
+
* length threshold. Truncated arrays include a message indicating how many additional
|
|
296
|
+
* items were omitted.
|
|
297
|
+
*
|
|
298
|
+
* **String Truncation**: Automatically truncates long strings to prevent log flooding.
|
|
299
|
+
* Truncated strings end with an ellipsis (…) character.
|
|
300
|
+
*
|
|
301
|
+
* **Type Safety**: Handles problematic JavaScript types that would normally cause
|
|
302
|
+
* JSON.stringify to throw:
|
|
303
|
+
* - BigInt values are converted to strings
|
|
304
|
+
* - Date objects are normalized to ISO 8601 format
|
|
305
|
+
* - Error instances are serialized to structured objects via {@link LogM8Utils.serializeError}
|
|
306
|
+
*
|
|
307
|
+
* ## Implementation Details
|
|
308
|
+
*
|
|
309
|
+
* - Uses a WeakMap to track traversal depth per object, preventing revisits to the
|
|
310
|
+
* same instance at different depths
|
|
311
|
+
* - Respects existing `toJSON()` methods on objects
|
|
312
|
+
* - Not designed for full cycle detection - cyclic references are handled by the
|
|
313
|
+
* depth limit rather than explicit cycle breaking
|
|
314
|
+
* - The replacer function executes in the context of the parent object, enabling
|
|
315
|
+
* depth tracking through the traversal
|
|
316
|
+
*
|
|
317
|
+
* @param value - Any JavaScript value to stringify for logging (events, contexts, errors, etc.)
|
|
318
|
+
* @param options - Configuration for controlling output size and complexity
|
|
319
|
+
* @param options.maxDepth - Maximum nesting depth for objects/arrays (default: 3).
|
|
320
|
+
* Level 0 = primitive values only,
|
|
321
|
+
* Level 1 = top-level properties,
|
|
322
|
+
* Level 2 = nested properties, etc.
|
|
323
|
+
* @param options.maxArrayLength - Maximum number of array elements to include before truncation (default: 100).
|
|
324
|
+
* Arrays exceeding this limit will be truncated with a message
|
|
325
|
+
* indicating the number of omitted items.
|
|
326
|
+
* @param options.maxStringLength - Maximum character length for strings before truncation (default: 200)
|
|
327
|
+
* @param space - Indentation for pretty-printing. Can be a number (spaces) or string (e.g., '\t').
|
|
328
|
+
* Pass undefined for compact output (recommended for production logs).
|
|
329
|
+
*
|
|
330
|
+
* @returns A JSON string that is guaranteed to be safe for logging systems
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* // Basic usage with default options
|
|
334
|
+
* const json = LogM8Utils.stringifyLog({ message: 'User logged in', userId: 12345 });
|
|
335
|
+
* // => '{"message":"User logged in","userId":12345}'
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* // Handling problematic types
|
|
339
|
+
* const data = {
|
|
340
|
+
* bigNumber: 123456789012345678901234567890n,
|
|
341
|
+
* timestamp: new Date('2024-01-15T10:30:00Z'),
|
|
342
|
+
* error: new Error('Connection failed'),
|
|
343
|
+
* longText: 'x'.repeat(500)
|
|
344
|
+
* };
|
|
345
|
+
* const json = LogM8Utils.stringifyLog(data);
|
|
346
|
+
* // => '{"bigNumber":"123456789012345678901234567890","timestamp":"2024-01-15T10:30:00.000Z","error":{...},"longText":"xxx...xxx…"}'
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* // Array length limiting for large arrays
|
|
350
|
+
* const largeData = {
|
|
351
|
+
* items: new Array(1000).fill({ id: 1, name: 'item' }),
|
|
352
|
+
* values: Array.from({ length: 500 }, (_, i) => i)
|
|
353
|
+
* };
|
|
354
|
+
* const json = LogM8Utils.stringifyLog(largeData, { maxArrayLength: 50 });
|
|
355
|
+
* // => '{"items":[{...},{...},...,"... 950 more items"],"values":[0,1,2,...,"... 450 more items"]}'
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* // Depth limiting for deeply nested objects
|
|
359
|
+
* const deepObj = {
|
|
360
|
+
* level1: {
|
|
361
|
+
* level2: {
|
|
362
|
+
* level3: {
|
|
363
|
+
* level4: {
|
|
364
|
+
* level5: 'too deep'
|
|
365
|
+
* }
|
|
366
|
+
* }
|
|
367
|
+
* }
|
|
368
|
+
* }
|
|
369
|
+
* };
|
|
370
|
+
* const json = LogM8Utils.stringifyLog(deepObj, { maxDepth: 3 });
|
|
371
|
+
* // => '{"level1":{"level2":{"level3":{"level4":"[Object]"}}}}'
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* // Custom options for verbose debugging
|
|
375
|
+
* const debugJson = LogM8Utils.stringifyLog(
|
|
376
|
+
* complexObject,
|
|
377
|
+
* { maxDepth: 5, maxArrayLength: 500, maxStringLength: 1000 },
|
|
378
|
+
* 2 // Pretty print with 2 spaces
|
|
379
|
+
* );
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* // Production logging with minimal output
|
|
383
|
+
* const prodJson = LogM8Utils.stringifyLog(
|
|
384
|
+
* userEvent,
|
|
385
|
+
* { maxDepth: 2, maxArrayLength: 20, maxStringLength: 100 } // Aggressive truncation for high-volume logs
|
|
386
|
+
* );
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* // Handling arrays with mixed content
|
|
390
|
+
* const mixedArray = {
|
|
391
|
+
* results: [
|
|
392
|
+
* { id: 1, data: 'first' },
|
|
393
|
+
* { id: 2, data: 'second' },
|
|
394
|
+
* ...Array(200).fill({ id: 999, data: 'bulk' })
|
|
395
|
+
* ]
|
|
396
|
+
* };
|
|
397
|
+
* const json = LogM8Utils.stringifyLog(mixedArray, { maxArrayLength: 10 });
|
|
398
|
+
* // First 10 items preserved, then truncation message
|
|
399
|
+
*
|
|
400
|
+
* @see {@link LogM8Utils.serializeError} - For Error serialization details
|
|
401
|
+
*/
|
|
402
|
+
static stringifyLog(value, { maxDepth = 3, maxStringLength = 200, maxArrayLength = 100 } = {}, space) {
|
|
403
|
+
const levels = /* @__PURE__ */ new WeakMap();
|
|
404
|
+
function replacer(key, v) {
|
|
405
|
+
if (v && typeof v === "object") {
|
|
406
|
+
const parentLevel = levels.get(this) ?? 0;
|
|
407
|
+
if (parentLevel >= maxDepth) {
|
|
408
|
+
return Array.isArray(v) ? "[Array]" : "[Object]";
|
|
409
|
+
}
|
|
410
|
+
levels.set(v, parentLevel + 1);
|
|
411
|
+
if (Array.isArray(v) && v.length > maxArrayLength) {
|
|
412
|
+
const truncated = v.slice(0, maxArrayLength);
|
|
413
|
+
truncated.push(`... ${v.length - maxArrayLength} more items`);
|
|
414
|
+
return truncated;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (_LogM8Utils.isString(v) && v.length > maxStringLength) {
|
|
418
|
+
return v.slice(0, maxStringLength) + "\u2026";
|
|
419
|
+
}
|
|
420
|
+
if (typeof v === "bigint") {
|
|
421
|
+
return v.toString();
|
|
422
|
+
}
|
|
423
|
+
if (v instanceof Date) {
|
|
424
|
+
return v.toISOString();
|
|
425
|
+
}
|
|
426
|
+
if (v instanceof Error) {
|
|
427
|
+
return _LogM8Utils.serializeError(v);
|
|
428
|
+
}
|
|
429
|
+
return v;
|
|
430
|
+
}
|
|
431
|
+
return JSON.stringify(value, replacer, space);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Serializes an Error (or Error-like) into a plain, JSON-safe object.
|
|
435
|
+
*
|
|
436
|
+
* Behavior:
|
|
437
|
+
* - Returns null for falsy inputs.
|
|
438
|
+
* - If the object provides a custom toJSON(), that result is used verbatim to
|
|
439
|
+
* honor caller-defined serialization.
|
|
440
|
+
* - Otherwise includes standard fields: name, message, stack.
|
|
441
|
+
* - Recursively serializes the optional error.cause chain using the same rules.
|
|
442
|
+
* - Handles circular references in the cause chain safely.
|
|
443
|
+
* - Copies other own enumerable properties (excluding name, message, stack, cause),
|
|
444
|
+
* skipping properties that throw on access or are not JSON-serializable.
|
|
445
|
+
* - Optionally includes common non-enumerable properties like 'code' if present.
|
|
446
|
+
*
|
|
447
|
+
* Important:
|
|
448
|
+
* - This function does not attempt to preserve all non-enumerable properties.
|
|
449
|
+
* - Circular references in the cause chain are detected and handled gracefully.
|
|
450
|
+
*
|
|
451
|
+
* @param error - Unknown error input (Error instance or compatible object).
|
|
452
|
+
* @returns Structured error data suitable for logging, or null when input is falsy.
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* try {
|
|
456
|
+
* throw new Error('Boom');
|
|
457
|
+
* } catch (e) {
|
|
458
|
+
* const payload = LogM8Utils.serializeError(e);
|
|
459
|
+
* // { name: 'Error', message: 'Boom', stack: '...', ... }
|
|
460
|
+
* }
|
|
461
|
+
*/
|
|
462
|
+
static serializeError(error) {
|
|
463
|
+
const serializeErrorInternal = (error2, seen) => {
|
|
464
|
+
if (!error2) return null;
|
|
465
|
+
const errorObj = error2;
|
|
466
|
+
if (typeof errorObj === "object" && errorObj !== null) {
|
|
467
|
+
if (seen.has(errorObj)) {
|
|
468
|
+
return {
|
|
469
|
+
name: "CircularReference",
|
|
470
|
+
message: "Circular reference detected in error cause chain"
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
seen.add(errorObj);
|
|
474
|
+
}
|
|
475
|
+
if (typeof errorObj.toJSON === "function") {
|
|
476
|
+
try {
|
|
477
|
+
const result = errorObj.toJSON();
|
|
478
|
+
JSON.stringify(result);
|
|
479
|
+
return result;
|
|
480
|
+
} catch (_e) {
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const serialized = {
|
|
484
|
+
name: errorObj.name || "Error",
|
|
485
|
+
message: errorObj.message || "",
|
|
486
|
+
stack: errorObj.stack
|
|
487
|
+
};
|
|
488
|
+
if ("cause" in errorObj && errorObj.cause !== void 0) {
|
|
489
|
+
serialized.cause = serializeErrorInternal(errorObj.cause, seen);
|
|
490
|
+
}
|
|
491
|
+
for (const key in errorObj) {
|
|
492
|
+
if (Object.prototype.hasOwnProperty.call(errorObj, key) && !EXCLUDED_ERROR_KEYS.has(key)) {
|
|
493
|
+
try {
|
|
494
|
+
const value = errorObj[key];
|
|
495
|
+
JSON.stringify(value);
|
|
496
|
+
serialized[key] = value;
|
|
497
|
+
} catch (_e) {
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
for (const prop of COMMON_NON_ENUMERABLE_PROPS) {
|
|
502
|
+
try {
|
|
503
|
+
const descriptor = Object.getOwnPropertyDescriptor(errorObj, prop);
|
|
504
|
+
if (descriptor && descriptor.value !== void 0) {
|
|
505
|
+
JSON.stringify(descriptor.value);
|
|
506
|
+
serialized[prop] = descriptor.value;
|
|
507
|
+
}
|
|
508
|
+
} catch (_e) {
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return serialized;
|
|
512
|
+
};
|
|
513
|
+
return serializeErrorInternal(error, /* @__PURE__ */ new WeakSet());
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// src/appenders/FileAppender.ts
|
|
518
|
+
var NAME2 = "file";
|
|
519
|
+
var VERSION2 = "1.0.0";
|
|
520
|
+
var KIND2 = PluginKind.appender;
|
|
521
|
+
var SUPPORTED_LEVELS2 = /* @__PURE__ */ new Set([
|
|
522
|
+
LogLevel.fatal,
|
|
523
|
+
LogLevel.error,
|
|
524
|
+
LogLevel.warn,
|
|
525
|
+
LogLevel.info,
|
|
526
|
+
LogLevel.debug,
|
|
527
|
+
LogLevel.track,
|
|
528
|
+
LogLevel.trace
|
|
529
|
+
]);
|
|
530
|
+
var FileAppender = class {
|
|
531
|
+
constructor() {
|
|
532
|
+
__publicField(this, "name", NAME2);
|
|
533
|
+
__publicField(this, "version", VERSION2);
|
|
534
|
+
__publicField(this, "kind", KIND2);
|
|
535
|
+
__publicField(this, "supportedLevels", SUPPORTED_LEVELS2);
|
|
536
|
+
__publicField(this, "enabled", true);
|
|
537
|
+
__publicField(this, "priority");
|
|
538
|
+
__publicField(this, "_config");
|
|
539
|
+
__publicField(this, "_formatter");
|
|
540
|
+
__publicField(this, "_filters", []);
|
|
541
|
+
__publicField(this, "_stream");
|
|
542
|
+
}
|
|
543
|
+
init(config, formatter, filters) {
|
|
544
|
+
this._config = config;
|
|
545
|
+
this._formatter = formatter;
|
|
546
|
+
this._filters = filters || [];
|
|
547
|
+
const flags = this._config.append ? "a" : "w";
|
|
548
|
+
this._stream = createWriteStream(this._config.filename, { flags });
|
|
549
|
+
this.enabled = this._config?.enabled !== false;
|
|
550
|
+
this.priority = this._config?.priority;
|
|
551
|
+
}
|
|
552
|
+
dispose() {
|
|
553
|
+
this._stream?.end();
|
|
554
|
+
}
|
|
555
|
+
write(event) {
|
|
556
|
+
if (!this._stream) return;
|
|
557
|
+
for (const filter of this._filters) {
|
|
558
|
+
if (filter.enabled && !filter.filter(event)) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const data = this._formatter ? this._formatter.format(event) : [event];
|
|
563
|
+
const message = data.map((d) => {
|
|
564
|
+
if (LogM8Utils.isString(d)) return d;
|
|
565
|
+
return String(d);
|
|
566
|
+
}).join(" ");
|
|
567
|
+
this._stream.write(message + "\n");
|
|
568
|
+
}
|
|
569
|
+
flush() {
|
|
570
|
+
}
|
|
571
|
+
enableFilter(name) {
|
|
572
|
+
const filter = this._getFilter(name);
|
|
573
|
+
if (!filter) return;
|
|
574
|
+
filter.enabled = true;
|
|
575
|
+
}
|
|
576
|
+
disableFilter(name) {
|
|
577
|
+
const filter = this._getFilter(name);
|
|
578
|
+
if (!filter) return;
|
|
579
|
+
filter.enabled = false;
|
|
580
|
+
}
|
|
581
|
+
_getFilter(name) {
|
|
582
|
+
return this._filters.find((f) => f.name === name);
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
var FileAppenderFactory = class {
|
|
586
|
+
constructor() {
|
|
587
|
+
__publicField(this, "name", NAME2);
|
|
588
|
+
__publicField(this, "version", VERSION2);
|
|
589
|
+
__publicField(this, "kind", KIND2);
|
|
590
|
+
}
|
|
591
|
+
create(config) {
|
|
592
|
+
const appender = new FileAppender();
|
|
593
|
+
appender.init(config);
|
|
594
|
+
return appender;
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
// src/filters/MatchFilter.ts
|
|
599
|
+
var MatchFilter = class {
|
|
600
|
+
constructor() {
|
|
601
|
+
__publicField(this, "name", "match-filter");
|
|
602
|
+
__publicField(this, "version", "1.0.0");
|
|
603
|
+
__publicField(this, "kind", PluginKind.filter);
|
|
604
|
+
__publicField(this, "enabled", true);
|
|
605
|
+
__publicField(this, "_allow");
|
|
606
|
+
__publicField(this, "_deny");
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Initializes allow/deny rule maps. Values are compared using deep equality for
|
|
610
|
+
* arrays/objects and strict equality for primitives. Missing maps are treated as empty.
|
|
611
|
+
* @param config - Filter configuration with optional allow/deny maps
|
|
612
|
+
*/
|
|
613
|
+
init(config) {
|
|
614
|
+
const cfg = config ?? {};
|
|
615
|
+
this._allow = cfg.allow ?? void 0;
|
|
616
|
+
this._deny = cfg.deny ?? void 0;
|
|
617
|
+
this.enabled = cfg.enabled !== false;
|
|
618
|
+
}
|
|
619
|
+
dispose() {
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Evaluates the given event against configured rules.
|
|
623
|
+
* - allow: if provided and non-empty, ALL rules must match (AND)
|
|
624
|
+
* - deny: if provided, ANY match denies (OR); deny takes precedence over allow
|
|
625
|
+
* Returns false on unexpected errors to fail-safe.
|
|
626
|
+
* @param logEvent - Event to evaluate
|
|
627
|
+
* @returns true when the event should be logged; false to drop
|
|
628
|
+
*/
|
|
629
|
+
filter(logEvent) {
|
|
630
|
+
try {
|
|
631
|
+
if (this._allow && Object.keys(this._allow).length > 0) {
|
|
632
|
+
for (const [path, expected] of Object.entries(this._allow)) {
|
|
633
|
+
const actual = LogM8Utils.getPropertyByPath(logEvent, path);
|
|
634
|
+
if (!this._isEqual(actual, expected)) return false;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (this._deny && Object.keys(this._deny).length > 0) {
|
|
638
|
+
for (const [path, expected] of Object.entries(this._deny)) {
|
|
639
|
+
const actual = LogM8Utils.getPropertyByPath(logEvent, path);
|
|
640
|
+
if (this._isEqual(actual, expected)) return false;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return true;
|
|
644
|
+
} catch (_err) {
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
// Simple deep equality for primitives, arrays, plain objects, dates
|
|
649
|
+
_isEqual(a, b) {
|
|
650
|
+
if (a === b) return true;
|
|
651
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
652
|
+
return Number.isNaN(a) && Number.isNaN(b);
|
|
653
|
+
}
|
|
654
|
+
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
|
|
655
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
656
|
+
if (a.length !== b.length) return false;
|
|
657
|
+
for (let i = 0; i < a.length; i++) {
|
|
658
|
+
if (!this._isEqual(a[i], b[i])) return false;
|
|
659
|
+
}
|
|
660
|
+
return true;
|
|
661
|
+
}
|
|
662
|
+
if (this._isPlainObject(a) && this._isPlainObject(b)) {
|
|
663
|
+
const aKeys = Object.keys(a);
|
|
664
|
+
const bKeys = Object.keys(b);
|
|
665
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
666
|
+
for (const key of aKeys) {
|
|
667
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
|
|
668
|
+
if (!this._isEqual(a[key], b[key]))
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
_isPlainObject(val) {
|
|
676
|
+
return typeof val === "object" && val !== null && !Array.isArray(val) && Object.getPrototypeOf(val) === Object.prototype;
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
var MatchFilterFactory = class {
|
|
680
|
+
constructor() {
|
|
681
|
+
__publicField(this, "name", "match-filter");
|
|
682
|
+
__publicField(this, "version", "1.0.0");
|
|
683
|
+
__publicField(this, "kind", PluginKind.filter);
|
|
684
|
+
}
|
|
685
|
+
create(config) {
|
|
686
|
+
const filter = new MatchFilter();
|
|
687
|
+
filter.init(config);
|
|
688
|
+
return filter;
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
// src/formatters/DefaultFormatter.ts
|
|
693
|
+
var NAME3 = "default-formatter";
|
|
694
|
+
var VERSION3 = "1.0.0";
|
|
695
|
+
var KIND3 = PluginKind.formatter;
|
|
696
|
+
var DEFAULT_FORMAT = ["{timestamp} {LEVEL} [{logger}]", "{message}", "{data}"];
|
|
697
|
+
var DEFAULT_TIMESTAMP_FORMAT = "hh:mm:ss.SSS";
|
|
698
|
+
var DefaultFormatter = class {
|
|
699
|
+
constructor() {
|
|
700
|
+
__publicField(this, "name", NAME3);
|
|
701
|
+
__publicField(this, "version", VERSION3);
|
|
702
|
+
__publicField(this, "kind", KIND3);
|
|
703
|
+
__publicField(this, "_config");
|
|
704
|
+
__publicField(this, "_format");
|
|
705
|
+
__publicField(this, "_timestampFormat", DEFAULT_TIMESTAMP_FORMAT);
|
|
706
|
+
__publicField(this, "_levelMap");
|
|
707
|
+
__publicField(this, "_colorEnabled", false);
|
|
708
|
+
// ANSI color codes for Node.js terminal output
|
|
709
|
+
__publicField(this, "_levelColorMap", {
|
|
710
|
+
trace: "\x1B[37m",
|
|
711
|
+
// White
|
|
712
|
+
track: "\x1B[38;5;208m",
|
|
713
|
+
// Orange
|
|
714
|
+
debug: "\x1B[90m",
|
|
715
|
+
// Grey
|
|
716
|
+
info: "\x1B[34m",
|
|
717
|
+
// Blue
|
|
718
|
+
warn: "\x1B[33m",
|
|
719
|
+
// Yellow
|
|
720
|
+
error: "\x1B[31m",
|
|
721
|
+
// Red
|
|
722
|
+
fatal: "\x1B[41m"
|
|
723
|
+
// Red background
|
|
724
|
+
});
|
|
725
|
+
// CSS color styles for browser console output
|
|
726
|
+
__publicField(this, "_levelCssColorMap", {
|
|
727
|
+
trace: "color: #bbb;",
|
|
728
|
+
// Light gray
|
|
729
|
+
track: "color: orange;",
|
|
730
|
+
debug: "color: grey;",
|
|
731
|
+
info: "color: blue;",
|
|
732
|
+
warn: "color: gold;",
|
|
733
|
+
error: "color: red;",
|
|
734
|
+
fatal: "background: red; color: white;"
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
init(config) {
|
|
738
|
+
const isBrowser = LogM8Utils.isBrowser();
|
|
739
|
+
this._config = Object.assign({}, config);
|
|
740
|
+
this._colorEnabled = !!this._config.color;
|
|
741
|
+
this._format = [];
|
|
742
|
+
let formatConfig = this._config.format ?? DEFAULT_FORMAT;
|
|
743
|
+
if (typeof this._config.format === "string") formatConfig = [this._config.format];
|
|
744
|
+
if (formatConfig) {
|
|
745
|
+
for (const f of formatConfig) {
|
|
746
|
+
const regex = /(\{[^}]+\})|([^{}]+)/g;
|
|
747
|
+
const parts = [];
|
|
748
|
+
let match;
|
|
749
|
+
while ((match = regex.exec(f)) !== null) {
|
|
750
|
+
if (match[1]) {
|
|
751
|
+
parts.push(match[1]);
|
|
752
|
+
} else if (match[2] !== void 0) {
|
|
753
|
+
parts.push(match[2]);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
this._format.push(parts);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
this._timestampFormat = this._config.timestampFormat ?? DEFAULT_TIMESTAMP_FORMAT;
|
|
760
|
+
const levelValues = Object.values(LogLevel);
|
|
761
|
+
const maxLevelLength = Math.max(...levelValues.map((l) => l.length));
|
|
762
|
+
this._levelMap = levelValues.reduce(
|
|
763
|
+
(acc, level) => {
|
|
764
|
+
let levelStr = level.toUpperCase().padEnd(maxLevelLength, " ");
|
|
765
|
+
if (this._colorEnabled) {
|
|
766
|
+
if (isBrowser) {
|
|
767
|
+
const css = this._levelCssColorMap[level] || "";
|
|
768
|
+
acc[level] = [`%c${levelStr}`, css];
|
|
769
|
+
return acc;
|
|
770
|
+
} else {
|
|
771
|
+
const color = this._levelColorMap[level] || "";
|
|
772
|
+
const reset = "\x1B[0m";
|
|
773
|
+
levelStr = color + levelStr + reset;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
acc[level] = levelStr;
|
|
777
|
+
return acc;
|
|
778
|
+
},
|
|
779
|
+
{}
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
dispose() {
|
|
783
|
+
}
|
|
784
|
+
format(logEvent) {
|
|
785
|
+
let output;
|
|
786
|
+
const formatArr = this._format;
|
|
787
|
+
if (formatArr.length > 0) {
|
|
788
|
+
output = formatArr.map((item) => {
|
|
789
|
+
if (item.length === 1) {
|
|
790
|
+
return this.resolveToken(item[0], logEvent);
|
|
791
|
+
}
|
|
792
|
+
return item.reduce((str, part) => {
|
|
793
|
+
const val = this.resolveToken(part, logEvent);
|
|
794
|
+
return str + String(val);
|
|
795
|
+
}, "");
|
|
796
|
+
});
|
|
797
|
+
const dataIndex = output.findIndex((v) => Array.isArray(v));
|
|
798
|
+
if (dataIndex >= 0) {
|
|
799
|
+
const data = output[dataIndex];
|
|
800
|
+
if (data.length > 0) output.splice(dataIndex, 1, ...data);
|
|
801
|
+
else output.splice(dataIndex, 1);
|
|
802
|
+
}
|
|
803
|
+
} else {
|
|
804
|
+
output = [
|
|
805
|
+
LogM8Utils.formatTimestamp(logEvent.timestamp, this._timestampFormat),
|
|
806
|
+
logEvent.level,
|
|
807
|
+
logEvent.logger,
|
|
808
|
+
logEvent.message,
|
|
809
|
+
...logEvent.data,
|
|
810
|
+
logEvent.context
|
|
811
|
+
];
|
|
812
|
+
}
|
|
813
|
+
return output;
|
|
814
|
+
}
|
|
815
|
+
resolveToken(part, logEvent) {
|
|
816
|
+
if (part.startsWith("{") && part.endsWith("}")) {
|
|
817
|
+
const key = part.slice(1, -1);
|
|
818
|
+
if (key === "message" && typeof logEvent.message !== "string") {
|
|
819
|
+
return logEvent.message;
|
|
820
|
+
} else if (key === "LEVEL") {
|
|
821
|
+
return this._levelMap[logEvent.level] ?? logEvent.level;
|
|
822
|
+
} else if (key === "timestamp") {
|
|
823
|
+
const raw = LogM8Utils.getPropertyByPath(logEvent, key);
|
|
824
|
+
return LogM8Utils.formatTimestamp(raw, this._timestampFormat);
|
|
825
|
+
}
|
|
826
|
+
return LogM8Utils.getPropertyByPath(logEvent, key);
|
|
827
|
+
}
|
|
828
|
+
return part;
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
var DefaultFormatterFactory = class {
|
|
832
|
+
constructor() {
|
|
833
|
+
__publicField(this, "name", NAME3);
|
|
834
|
+
__publicField(this, "version", VERSION3);
|
|
835
|
+
__publicField(this, "kind", KIND3);
|
|
836
|
+
}
|
|
837
|
+
create(config) {
|
|
838
|
+
const appender = new DefaultFormatter();
|
|
839
|
+
appender.init(config);
|
|
840
|
+
return appender;
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
// src/formatters/JsonFormatter.ts
|
|
845
|
+
var NAME4 = "json-formatter";
|
|
846
|
+
var VERSION4 = "1.0.0";
|
|
847
|
+
var KIND4 = PluginKind.formatter;
|
|
848
|
+
var DEFAULT_FORMAT2 = ["timestamp", "level", "logger", "message", "data"];
|
|
849
|
+
var DEFAULT_TIMESTAMP_FORMAT2 = "iso";
|
|
850
|
+
var DEFAULT_PRETTY = 2;
|
|
851
|
+
var DEFAULT_MAX_DEPTH = 3;
|
|
852
|
+
var DEFAULT_MAX_STRING_LEN = 1e3;
|
|
853
|
+
var DEFAULT_MAX_ARRAY_LEN = 100;
|
|
854
|
+
var JsonFormatter = class {
|
|
855
|
+
constructor() {
|
|
856
|
+
__publicField(this, "name", NAME4);
|
|
857
|
+
__publicField(this, "version", VERSION4);
|
|
858
|
+
__publicField(this, "kind", KIND4);
|
|
859
|
+
__publicField(this, "_config");
|
|
860
|
+
__publicField(this, "_format");
|
|
861
|
+
__publicField(this, "_pretty");
|
|
862
|
+
__publicField(this, "_maxDepth", DEFAULT_MAX_DEPTH);
|
|
863
|
+
__publicField(this, "_maxStringLen", DEFAULT_MAX_STRING_LEN);
|
|
864
|
+
__publicField(this, "_maxArrayLen", DEFAULT_MAX_ARRAY_LEN);
|
|
865
|
+
__publicField(this, "_timestampFormat", DEFAULT_TIMESTAMP_FORMAT2);
|
|
866
|
+
}
|
|
867
|
+
init(config) {
|
|
868
|
+
this._config = Object.assign({}, config);
|
|
869
|
+
this._pretty = this._config.pretty === true ? DEFAULT_PRETTY : this._config.pretty ? this._config.pretty : void 0;
|
|
870
|
+
this._maxDepth = this._config.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
871
|
+
this._maxStringLen = this._config.maxStringLen ?? DEFAULT_MAX_STRING_LEN;
|
|
872
|
+
this._maxArrayLen = this._config.maxArrayLen ?? DEFAULT_MAX_ARRAY_LEN;
|
|
873
|
+
let formatConfig = this._config.format ?? DEFAULT_FORMAT2;
|
|
874
|
+
if (typeof this._config.format === "string") formatConfig = [this._config.format];
|
|
875
|
+
this._format = formatConfig;
|
|
876
|
+
this._timestampFormat = this._config.timestampFormat ?? DEFAULT_TIMESTAMP_FORMAT2;
|
|
877
|
+
}
|
|
878
|
+
dispose() {
|
|
879
|
+
}
|
|
880
|
+
format(logEvent) {
|
|
881
|
+
let outputObj = {};
|
|
882
|
+
const formatArr = this._format;
|
|
883
|
+
if (formatArr.length > 0) {
|
|
884
|
+
formatArr.forEach((item) => {
|
|
885
|
+
const t = this.resolveToken(item, logEvent);
|
|
886
|
+
if (t != void 0) {
|
|
887
|
+
outputObj[t.key] = t.value;
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
} else {
|
|
891
|
+
outputObj = logEvent;
|
|
892
|
+
}
|
|
893
|
+
return [
|
|
894
|
+
LogM8Utils.stringifyLog(
|
|
895
|
+
outputObj,
|
|
896
|
+
{
|
|
897
|
+
maxDepth: this._maxDepth,
|
|
898
|
+
maxStringLength: this._maxStringLen,
|
|
899
|
+
maxArrayLength: this._maxArrayLen
|
|
900
|
+
},
|
|
901
|
+
this._pretty
|
|
902
|
+
)
|
|
903
|
+
];
|
|
904
|
+
}
|
|
905
|
+
resolveToken(part, logEvent) {
|
|
906
|
+
const key = part;
|
|
907
|
+
if (key === "LEVEL") {
|
|
908
|
+
return { key, value: logEvent.level };
|
|
909
|
+
} else if (key === "timestamp") {
|
|
910
|
+
const raw = LogM8Utils.getPropertyByPath(logEvent, key);
|
|
911
|
+
return { key, value: LogM8Utils.formatTimestamp(raw, this._timestampFormat) };
|
|
912
|
+
}
|
|
913
|
+
return { key, value: LogM8Utils.getPropertyByPath(logEvent, key) };
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
var JsonFormatterFactory = class {
|
|
917
|
+
constructor() {
|
|
918
|
+
__publicField(this, "name", NAME4);
|
|
919
|
+
__publicField(this, "version", VERSION4);
|
|
920
|
+
__publicField(this, "kind", KIND4);
|
|
921
|
+
}
|
|
922
|
+
create(config) {
|
|
923
|
+
const appender = new JsonFormatter();
|
|
924
|
+
appender.init(config);
|
|
925
|
+
return appender;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
// src/PluginManager.ts
|
|
930
|
+
var PluginManager = class {
|
|
931
|
+
constructor() {
|
|
932
|
+
__publicField(this, "_pluginFactories", /* @__PURE__ */ new Map());
|
|
933
|
+
__publicField(this, "_plugins", []);
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Registers a plugin factory.
|
|
937
|
+
* @param pluginFactory - Factory to register for creating plugins.
|
|
938
|
+
* @throws Error if a factory with the same name is already registered.
|
|
939
|
+
*/
|
|
940
|
+
registerPluginFactory(pluginFactory) {
|
|
941
|
+
if (this._pluginFactories.has(pluginFactory.name)) {
|
|
942
|
+
throw new Error(`LogM8: Plugin with name ${pluginFactory.name} is already registered.`);
|
|
943
|
+
}
|
|
944
|
+
this._pluginFactories.set(pluginFactory.name, pluginFactory);
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Creates and registers a plugin instance using the matching factory.
|
|
948
|
+
* @param kind - The kind of plugin to create (appender, filter, formatter).
|
|
949
|
+
* @param nameOrConfig - Plugin name string or configuration object with plugin name.
|
|
950
|
+
* @returns The created plugin instance.
|
|
951
|
+
* @throws Error if no matching factory is found.
|
|
952
|
+
*/
|
|
953
|
+
createPlugin(kind, nameOrConfig) {
|
|
954
|
+
const name = typeof nameOrConfig === "string" ? nameOrConfig : nameOrConfig.name;
|
|
955
|
+
const config = typeof nameOrConfig === "string" ? { name } : nameOrConfig;
|
|
956
|
+
const pluginFactory = this.getPluginFactory(name, kind);
|
|
957
|
+
if (!pluginFactory) {
|
|
958
|
+
throw new Error(`LogM8: Plugin factory kind '${kind}' with name '${name}' not found.`);
|
|
959
|
+
}
|
|
960
|
+
const plugin = pluginFactory.create(config);
|
|
961
|
+
this._plugins.push(plugin);
|
|
962
|
+
return plugin;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Retrieves a registered plugin factory by name and kind.
|
|
966
|
+
* @param name - The factory name.
|
|
967
|
+
* @param kind - The expected plugin kind.
|
|
968
|
+
* @returns The factory if found and matching kind, otherwise undefined.
|
|
969
|
+
*/
|
|
970
|
+
getPluginFactory(name, kind) {
|
|
971
|
+
const pluginFactory = this._pluginFactories.get(name);
|
|
972
|
+
if (!pluginFactory || kind !== pluginFactory.kind) return;
|
|
973
|
+
return pluginFactory;
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Disposes all created plugin instances by invoking their dispose methods.
|
|
977
|
+
* Clears the internal plugin list.
|
|
978
|
+
*/
|
|
979
|
+
disposePlugins() {
|
|
980
|
+
this._plugins.forEach((plugin) => {
|
|
981
|
+
plugin.dispose();
|
|
982
|
+
});
|
|
983
|
+
this._plugins = [];
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Clears all registered plugin factories without disposing instances.
|
|
987
|
+
*/
|
|
988
|
+
clearFactories() {
|
|
989
|
+
this._pluginFactories.clear();
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
// src/LogM8.ts
|
|
994
|
+
var MAX_LOG_BUFFER_SIZE = 100;
|
|
995
|
+
var DEFAULT_APPENDERS = [
|
|
996
|
+
{
|
|
997
|
+
name: "console",
|
|
998
|
+
formatter: "default-formatter"
|
|
999
|
+
}
|
|
1000
|
+
];
|
|
1001
|
+
var LogM8 = class {
|
|
1002
|
+
constructor() {
|
|
1003
|
+
__publicField(this, "_initialized", false);
|
|
1004
|
+
__publicField(this, "_pluginManager", new PluginManager());
|
|
1005
|
+
__publicField(this, "_loggers", /* @__PURE__ */ new Map());
|
|
1006
|
+
__publicField(this, "_appenders", []);
|
|
1007
|
+
__publicField(this, "_filters", []);
|
|
1008
|
+
__publicField(this, "_defaultLevel", LogLevel.info);
|
|
1009
|
+
__publicField(this, "_logLevelValues", Object.values(LogLevel));
|
|
1010
|
+
__publicField(this, "_logLevelSet", new Set(this._logLevelValues));
|
|
1011
|
+
// Buffer for log events before the system is initialized
|
|
1012
|
+
__publicField(this, "_logBuffer", []);
|
|
1013
|
+
this._pluginManager.registerPluginFactory(new ConsoleAppenderFactory());
|
|
1014
|
+
this._pluginManager.registerPluginFactory(new FileAppenderFactory());
|
|
1015
|
+
this._pluginManager.registerPluginFactory(new DefaultFormatterFactory());
|
|
1016
|
+
this._pluginManager.registerPluginFactory(new JsonFormatterFactory());
|
|
1017
|
+
this._pluginManager.registerPluginFactory(new MatchFilterFactory());
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Initializes the logging system with configuration and flushes any buffered events.
|
|
1021
|
+
*
|
|
1022
|
+
* Sets up default and per-logger levels, creates configured appenders with their
|
|
1023
|
+
* formatters and filters, and processes any events buffered before initialization.
|
|
1024
|
+
* Appenders are sorted by priority (descending) for deterministic execution order.
|
|
1025
|
+
*
|
|
1026
|
+
* @param config - Logging configuration object
|
|
1027
|
+
* @param config.level - Default log level for all loggers ('info' if not specified)
|
|
1028
|
+
* @param config.loggers - Per-logger level overrides by name
|
|
1029
|
+
* @param config.appenders - Appender configurations (defaults to console if not specified)
|
|
1030
|
+
*
|
|
1031
|
+
* @throws {Error} When referenced plugin factories are not registered
|
|
1032
|
+
*
|
|
1033
|
+
* @example
|
|
1034
|
+
* ```typescript
|
|
1035
|
+
* Logging.init({
|
|
1036
|
+
* level: 'warn',
|
|
1037
|
+
* loggers: { 'app.database': 'debug' },
|
|
1038
|
+
* appenders: [{
|
|
1039
|
+
* name: 'console',
|
|
1040
|
+
* formatter: 'default-formatter',
|
|
1041
|
+
* filters: ['sensitive-data']
|
|
1042
|
+
* }]
|
|
1043
|
+
* });
|
|
1044
|
+
* ```
|
|
1045
|
+
*/
|
|
1046
|
+
init(config) {
|
|
1047
|
+
config = Object.assign({}, config);
|
|
1048
|
+
this._reset();
|
|
1049
|
+
this._defaultLevel = this._logLevelSet.has(config.level) ? config.level : LogLevel.info;
|
|
1050
|
+
for (const [name, level] of Object.entries(config.loggers ?? {})) {
|
|
1051
|
+
const logger = this.getLogger(name);
|
|
1052
|
+
const l = this._logLevelSet.has(level) ? level : this._defaultLevel;
|
|
1053
|
+
logger.setLevel(l);
|
|
1054
|
+
}
|
|
1055
|
+
const appenderConfigs = config.appenders ?? DEFAULT_APPENDERS;
|
|
1056
|
+
for (const appenderConfig of appenderConfigs) {
|
|
1057
|
+
const appender = this._pluginManager.createPlugin(
|
|
1058
|
+
PluginKind.appender,
|
|
1059
|
+
appenderConfig
|
|
1060
|
+
);
|
|
1061
|
+
const formatter = appenderConfig.formatter ? this._pluginManager.createPlugin(
|
|
1062
|
+
PluginKind.formatter,
|
|
1063
|
+
appenderConfig.formatter
|
|
1064
|
+
) : void 0;
|
|
1065
|
+
const filters = [];
|
|
1066
|
+
const ac = appenderConfig;
|
|
1067
|
+
for (const filterConfig of ac.filters ?? []) {
|
|
1068
|
+
const filter = this._pluginManager.createPlugin(PluginKind.filter, filterConfig);
|
|
1069
|
+
if (filter) {
|
|
1070
|
+
filters.push(filter);
|
|
1071
|
+
} else {
|
|
1072
|
+
if (console && console.log) {
|
|
1073
|
+
console.log(
|
|
1074
|
+
`LogM8: Filter '${filterConfig}' not found for appender ${appenderConfig.name}.`
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
appender.init(appenderConfig, formatter, filters);
|
|
1080
|
+
this._appenders.push(appender);
|
|
1081
|
+
}
|
|
1082
|
+
this._sortAppenders();
|
|
1083
|
+
for (const filterConfig of config.filters ?? []) {
|
|
1084
|
+
const filter = this._pluginManager.createPlugin(PluginKind.filter, filterConfig);
|
|
1085
|
+
if (filter) {
|
|
1086
|
+
this._filters.push(filter);
|
|
1087
|
+
} else {
|
|
1088
|
+
if (console && console.log) {
|
|
1089
|
+
console.log(`LogM8: Filter '${filterConfig}' not found (global).`);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
this._initialized = true;
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Shuts down the logging system and releases all resources.
|
|
1097
|
+
*
|
|
1098
|
+
* Flushes all appenders, disposes plugin instances, clears logger registry,
|
|
1099
|
+
* discards buffered events, and deregisters plugin factories. The system
|
|
1100
|
+
* can be reinitialized after disposal.
|
|
1101
|
+
*
|
|
1102
|
+
* @example
|
|
1103
|
+
* ```typescript
|
|
1104
|
+
* // Graceful shutdown
|
|
1105
|
+
* await new Promise(resolve => {
|
|
1106
|
+
* Logging.flushAppenders();
|
|
1107
|
+
* setTimeout(() => {
|
|
1108
|
+
* Logging.dispose();
|
|
1109
|
+
* resolve();
|
|
1110
|
+
* }, 100);
|
|
1111
|
+
* });
|
|
1112
|
+
* ```
|
|
1113
|
+
*/
|
|
1114
|
+
dispose() {
|
|
1115
|
+
this._reset();
|
|
1116
|
+
this._logBuffer = [];
|
|
1117
|
+
this._pluginManager.clearFactories();
|
|
1118
|
+
this._initialized = false;
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Retrieves or creates a logger instance with hierarchical naming.
|
|
1122
|
+
*
|
|
1123
|
+
* Logger instances are cached and reused for the same name. Names can be
|
|
1124
|
+
* provided as strings with dot-separation or as array segments that get
|
|
1125
|
+
* joined. Each logger maintains independent level and context settings.
|
|
1126
|
+
*
|
|
1127
|
+
* @param name - Logger name as string ('app.service') or segments (['app', 'service'])
|
|
1128
|
+
* @returns Logger instance for the specified name
|
|
1129
|
+
*
|
|
1130
|
+
* @example
|
|
1131
|
+
* ```typescript
|
|
1132
|
+
* const logger1 = Logging.getLogger('app.database');
|
|
1133
|
+
* const logger2 = Logging.getLogger(['app', 'database']);
|
|
1134
|
+
* // logger1 === logger2 (same instance)
|
|
1135
|
+
*
|
|
1136
|
+
* logger1.setLevel('debug');
|
|
1137
|
+
* logger1.setContext({ service: 'postgres' });
|
|
1138
|
+
* ```
|
|
1139
|
+
*/
|
|
1140
|
+
getLogger(name) {
|
|
1141
|
+
let nameStr = name;
|
|
1142
|
+
if (Array.isArray(name)) {
|
|
1143
|
+
nameStr = name.join(".");
|
|
1144
|
+
}
|
|
1145
|
+
const existingLogger = this._loggers.get(nameStr);
|
|
1146
|
+
if (existingLogger) return existingLogger;
|
|
1147
|
+
const logger = {
|
|
1148
|
+
name: nameStr,
|
|
1149
|
+
level: this._defaultLevel,
|
|
1150
|
+
context: {}
|
|
1151
|
+
};
|
|
1152
|
+
logger.fatal = this._log.bind(this, logger, LogLevel.fatal);
|
|
1153
|
+
logger.error = this._log.bind(this, logger, LogLevel.error);
|
|
1154
|
+
logger.warn = this._log.bind(this, logger, LogLevel.warn);
|
|
1155
|
+
logger.info = this._log.bind(this, logger, LogLevel.info);
|
|
1156
|
+
logger.debug = this._log.bind(this, logger, LogLevel.debug);
|
|
1157
|
+
logger.trace = this._log.bind(this, logger, LogLevel.trace);
|
|
1158
|
+
logger.track = this._log.bind(this, logger, LogLevel.track);
|
|
1159
|
+
logger.setLevel = this._setLevel.bind(this, logger);
|
|
1160
|
+
logger.setContext = this._setContext.bind(this, logger);
|
|
1161
|
+
logger.getLogger = (name2) => this.getLogger([logger.name, name2]);
|
|
1162
|
+
this._setLevel(logger, this._defaultLevel);
|
|
1163
|
+
this._loggers.set(logger.name, logger);
|
|
1164
|
+
return logger;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Enables an appender to resume processing log events.
|
|
1168
|
+
*
|
|
1169
|
+
* @param name - Name of the appender to enable
|
|
1170
|
+
*/
|
|
1171
|
+
enableAppender(name) {
|
|
1172
|
+
const appender = this._getAppender(name);
|
|
1173
|
+
if (!appender) return;
|
|
1174
|
+
appender.enabled = true;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Disables an appender to stop processing log events.
|
|
1178
|
+
*
|
|
1179
|
+
* @param name - Name of the appender to disable
|
|
1180
|
+
*/
|
|
1181
|
+
disableAppender(name) {
|
|
1182
|
+
const appender = this._getAppender(name);
|
|
1183
|
+
if (!appender) return;
|
|
1184
|
+
appender.enabled = false;
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Forces an appender to flush any buffered output.
|
|
1188
|
+
*
|
|
1189
|
+
* Catches and logs flush errors to console without interrupting operation.
|
|
1190
|
+
* Useful for ensuring data persistence before shutdown or at intervals.
|
|
1191
|
+
*
|
|
1192
|
+
* @param name - Name of the appender to flush
|
|
1193
|
+
*/
|
|
1194
|
+
flushAppender(name) {
|
|
1195
|
+
const appender = this._getAppender(name);
|
|
1196
|
+
if (!appender) return;
|
|
1197
|
+
try {
|
|
1198
|
+
appender.flush();
|
|
1199
|
+
} catch (err) {
|
|
1200
|
+
if (console && console.error) {
|
|
1201
|
+
console.error(`LogM8: Failed to flush appender: ${appender.name}:`, err);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Flushes all configured appenders.
|
|
1207
|
+
*
|
|
1208
|
+
* Iterates through all appenders calling flush on each, with individual
|
|
1209
|
+
* error handling per appender.
|
|
1210
|
+
*/
|
|
1211
|
+
flushAppenders() {
|
|
1212
|
+
for (const appender of this._appenders) {
|
|
1213
|
+
this.flushAppender(appender.name);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Enables a filter to resume processing log events.
|
|
1218
|
+
*
|
|
1219
|
+
* When an appender name is provided, enables the filter only for that specific
|
|
1220
|
+
* appender. When no appender is specified, enables the filter globally.
|
|
1221
|
+
* Silently ignores requests for non-existent filters or appenders.
|
|
1222
|
+
*
|
|
1223
|
+
* @param name - Name of the filter to enable
|
|
1224
|
+
* @param appenderName - Optional appender name to enable filter for specific appender only
|
|
1225
|
+
*
|
|
1226
|
+
* @example
|
|
1227
|
+
* ```typescript
|
|
1228
|
+
* // Enable filter globally
|
|
1229
|
+
* Logging.enableFilter('sensitive-data');
|
|
1230
|
+
*
|
|
1231
|
+
* // Enable filter only for console appender
|
|
1232
|
+
* Logging.enableFilter('debug-filter', 'console');
|
|
1233
|
+
* ```
|
|
1234
|
+
*/
|
|
1235
|
+
enableFilter(name, appenderName) {
|
|
1236
|
+
if (appenderName) {
|
|
1237
|
+
this._getAppender(appenderName)?.enableFilter(name);
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
const filter = this._getFilter(name);
|
|
1241
|
+
if (!filter) return;
|
|
1242
|
+
filter.enabled = true;
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Disables a filter to stop processing log events.
|
|
1246
|
+
*
|
|
1247
|
+
* When an appender name is provided, disables the filter only for that specific
|
|
1248
|
+
* appender. When no appender is specified, disables the filter globally.
|
|
1249
|
+
* Silently ignores requests for non-existent filters or appenders.
|
|
1250
|
+
*
|
|
1251
|
+
* @param name - Name of the filter to disable
|
|
1252
|
+
* @param appenderName - Optional appender name to disable filter for specific appender only
|
|
1253
|
+
*
|
|
1254
|
+
* @example
|
|
1255
|
+
* ```typescript
|
|
1256
|
+
* // Disable filter globally
|
|
1257
|
+
* Logging.disableFilter('sensitive-data');
|
|
1258
|
+
*
|
|
1259
|
+
* // Disable filter only for file appender
|
|
1260
|
+
* Logging.disableFilter('debug-filter', 'file');
|
|
1261
|
+
* ```
|
|
1262
|
+
*/
|
|
1263
|
+
disableFilter(name, appenderName) {
|
|
1264
|
+
if (appenderName) {
|
|
1265
|
+
this._getAppender(appenderName)?.disableFilter(name);
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
const filter = this._getFilter(name);
|
|
1269
|
+
if (!filter) return;
|
|
1270
|
+
filter.enabled = false;
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Registers a custom plugin factory for appenders, formatters, or filters.
|
|
1274
|
+
*
|
|
1275
|
+
* Allows extending the logging system with custom implementations.
|
|
1276
|
+
* Must be called before init() to be available during configuration.
|
|
1277
|
+
*
|
|
1278
|
+
* @param pluginFactory - Factory instance implementing the PluginFactory interface
|
|
1279
|
+
*
|
|
1280
|
+
* @example
|
|
1281
|
+
* ```typescript
|
|
1282
|
+
* class SlackAppenderFactory implements PluginFactory {
|
|
1283
|
+
* name = 'slack';
|
|
1284
|
+
* kind = PluginKind.appender;
|
|
1285
|
+
* create(config) { return new SlackAppender(config); }
|
|
1286
|
+
* }
|
|
1287
|
+
*
|
|
1288
|
+
* Logging.registerPluginFactory(new SlackAppenderFactory());
|
|
1289
|
+
* ```
|
|
1290
|
+
*/
|
|
1291
|
+
registerPluginFactory(pluginFactory) {
|
|
1292
|
+
this._pluginManager.registerPluginFactory(pluginFactory);
|
|
1293
|
+
}
|
|
1294
|
+
_log(logger, level, message, ...data) {
|
|
1295
|
+
const levelNumber = this._logLevelValues.indexOf(level);
|
|
1296
|
+
if (levelNumber > logger._levelNumber) return;
|
|
1297
|
+
const logEvent = {
|
|
1298
|
+
logger: logger.name,
|
|
1299
|
+
level,
|
|
1300
|
+
message,
|
|
1301
|
+
data,
|
|
1302
|
+
context: logger.context,
|
|
1303
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1304
|
+
};
|
|
1305
|
+
if (this._initialized) {
|
|
1306
|
+
if (this._logBuffer.length > 0) {
|
|
1307
|
+
for (const bufferedEvent of this._logBuffer) {
|
|
1308
|
+
this._processLogEvent(bufferedEvent);
|
|
1309
|
+
}
|
|
1310
|
+
this._logBuffer = [];
|
|
1311
|
+
}
|
|
1312
|
+
this._processLogEvent(logEvent);
|
|
1313
|
+
} else {
|
|
1314
|
+
if (this._logBuffer.length >= MAX_LOG_BUFFER_SIZE) {
|
|
1315
|
+
this._logBuffer.shift();
|
|
1316
|
+
}
|
|
1317
|
+
this._logBuffer.push(logEvent);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
_setLevel(logger, level) {
|
|
1321
|
+
logger.level = level;
|
|
1322
|
+
logger._levelNumber = this._logLevelValues.indexOf(level);
|
|
1323
|
+
logger.isEnabled = level !== LogLevel.off;
|
|
1324
|
+
const levelNumber = logger._levelNumber;
|
|
1325
|
+
logger.isFatal = this._logLevelValues.indexOf(LogLevel.fatal) <= levelNumber;
|
|
1326
|
+
logger.isError = this._logLevelValues.indexOf(LogLevel.error) <= levelNumber;
|
|
1327
|
+
logger.isWarn = this._logLevelValues.indexOf(LogLevel.warn) <= levelNumber;
|
|
1328
|
+
logger.isInfo = this._logLevelValues.indexOf(LogLevel.info) <= levelNumber;
|
|
1329
|
+
logger.isDebug = this._logLevelValues.indexOf(LogLevel.debug) <= levelNumber;
|
|
1330
|
+
logger.isTrack = this._logLevelValues.indexOf(LogLevel.track) <= levelNumber;
|
|
1331
|
+
logger.isTrace = this._logLevelValues.indexOf(LogLevel.trace) <= levelNumber;
|
|
1332
|
+
}
|
|
1333
|
+
_setContext(logger, context) {
|
|
1334
|
+
logger.context = context ?? {};
|
|
1335
|
+
}
|
|
1336
|
+
_processLogEvent(event) {
|
|
1337
|
+
for (const filter of this._filters) {
|
|
1338
|
+
if (filter.enabled && !filter.filter(event)) {
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
for (const appender of this._appenders) {
|
|
1343
|
+
try {
|
|
1344
|
+
if (!appender.enabled) continue;
|
|
1345
|
+
if (!appender.supportedLevels.has(event.level)) continue;
|
|
1346
|
+
appender.write(event);
|
|
1347
|
+
} catch (err) {
|
|
1348
|
+
if (console && console.log) {
|
|
1349
|
+
console.log(`LogM8: Failed to append log with '${appender.name}':`, err);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
_getAppender(name) {
|
|
1355
|
+
return this._appenders.find((a) => a.name === name);
|
|
1356
|
+
}
|
|
1357
|
+
_sortAppenders() {
|
|
1358
|
+
this._appenders.sort((a, b) => {
|
|
1359
|
+
const aPriority = a?.priority ?? 0;
|
|
1360
|
+
const bPriority = b?.priority ?? 0;
|
|
1361
|
+
return bPriority - aPriority;
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
_getFilter(name) {
|
|
1365
|
+
return this._filters.find((f) => f.name === name);
|
|
1366
|
+
}
|
|
1367
|
+
_reset() {
|
|
1368
|
+
this.flushAppenders();
|
|
1369
|
+
this._appenders = [];
|
|
1370
|
+
this._loggers.clear();
|
|
1371
|
+
this._defaultLevel = LogLevel.info;
|
|
1372
|
+
this._pluginManager.disposePlugins();
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
// src/index.ts
|
|
1377
|
+
var Logging = new LogM8();
|
|
1378
|
+
export {
|
|
1379
|
+
LogLevel,
|
|
1380
|
+
LogM8Utils,
|
|
1381
|
+
Logging,
|
|
1382
|
+
PluginKind
|
|
1383
|
+
};
|
|
1384
|
+
//# sourceMappingURL=index.js.map
|