@feizk/logger 1.7.0 → 2.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/dist/index.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,223 +15,357 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
31
21
  var index_exports = {};
32
22
  __export(index_exports, {
23
+ LEVEL_LABELS: () => LEVEL_LABELS,
24
+ LOG_LEVEL_PRIORITIES: () => LOG_LEVEL_PRIORITIES,
33
25
  Logger: () => Logger,
34
- TIMESTAMP_TYPES: () => TIMESTAMP_TYPES
26
+ buildMessage: () => buildMessage,
27
+ formatJson: () => formatJson,
28
+ formatTimestamp: () => formatTimestamp,
29
+ getColoredLabel: () => getColoredLabel
35
30
  });
36
31
  module.exports = __toCommonJS(index_exports);
37
32
 
38
- // src/utils.ts
39
- var import_chalk = __toESM(require("chalk"));
40
- var TIMESTAMP_TYPES = {
41
- ISO: "iso",
42
- Locale: "locale",
43
- Custom: "custom"
33
+ // src/constants.ts
34
+ var LOG_LEVEL_PRIORITIES = {
35
+ trace: 0,
36
+ debug: 1,
37
+ info: 2,
38
+ warn: 3,
39
+ error: 4,
40
+ fatal: 5
41
+ };
42
+ var ANSI = {
43
+ reset: "\x1B[0m",
44
+ bold: "\x1B[1m",
45
+ dim: "\x1B[2m",
46
+ red: "\x1B[31m",
47
+ green: "\x1B[32m",
48
+ yellow: "\x1B[33m",
49
+ blue: "\x1B[34m",
50
+ magenta: "\x1B[35m",
51
+ cyan: "\x1B[36m",
52
+ gray: "\x1B[90m",
53
+ white: "\x1B[37m",
54
+ bgRed: "\x1B[41m"
55
+ };
56
+ var LEVEL_COLORS = {
57
+ trace: ANSI.gray,
58
+ debug: ANSI.cyan,
59
+ info: ANSI.blue,
60
+ warn: ANSI.yellow,
61
+ error: ANSI.red,
62
+ fatal: `${ANSI.bgRed}${ANSI.white}${ANSI.bold}`
63
+ };
64
+ var LEVEL_LABELS = {
65
+ trace: "[TRACE]",
66
+ debug: "[DEBUG]",
67
+ info: "[INFO]",
68
+ warn: "[WARN]",
69
+ error: "[ERROR]",
70
+ fatal: "[FATAL]"
44
71
  };
45
- function formatTimestamp(formatTimestampFn, types, date = /* @__PURE__ */ new Date()) {
46
- const [, timestamp] = formatTimestampFn(types, date);
47
- return timestamp;
72
+ var CONSOLE_METHODS = {
73
+ trace: "trace",
74
+ debug: "debug",
75
+ info: "log",
76
+ warn: "warn",
77
+ error: "error",
78
+ fatal: "error"
79
+ };
80
+ var TIMESTAMP_PRESETS = {
81
+ iso: (date) => date.toISOString(),
82
+ locale: (date) => date.toLocaleString()
83
+ };
84
+
85
+ // src/utils.ts
86
+ var coloredLabelCache = /* @__PURE__ */ new Map();
87
+ function getColoredLabel(level, enableColors) {
88
+ const cacheKey = `${level}:${enableColors}`;
89
+ const cached = coloredLabelCache.get(cacheKey);
90
+ if (cached !== void 0) return cached;
91
+ const label = LEVEL_LABELS[level];
92
+ if (!enableColors) {
93
+ coloredLabelCache.set(cacheKey, label);
94
+ return label;
95
+ }
96
+ const color = LEVEL_COLORS[level];
97
+ const result = `${color}${label}${ANSI.reset}`;
98
+ coloredLabelCache.set(cacheKey, result);
99
+ return result;
48
100
  }
49
- function getColor(level, enableColors) {
50
- if (!enableColors) return level;
51
- const colors = {
52
- "[INFO]": import_chalk.default.blue(level),
53
- "[WARN]": import_chalk.default.yellow(level),
54
- "[ERROR]": import_chalk.default.red(level),
55
- "[DEBUG]": import_chalk.default.gray(level)
56
- };
57
- return colors[level] || level;
101
+ function formatTimestamp(option, date = /* @__PURE__ */ new Date()) {
102
+ if (typeof option === "function") {
103
+ return option(date);
104
+ }
105
+ const preset = TIMESTAMP_PRESETS[option];
106
+ return preset(date);
58
107
  }
59
- function getDiscordColor(level) {
60
- const colors = {
61
- debug: 9807270,
62
- info: 3447003,
63
- warn: 15965202,
64
- error: 15158332
108
+ function formatJson(entry) {
109
+ const message = entry.args.map(formatArg).join(" ");
110
+ const output = {
111
+ level: entry.level,
112
+ timestamp: entry.timestamp,
113
+ message
65
114
  };
66
- return colors[level];
67
- }
68
- function generateId() {
69
- return Math.random().toString(36).substr(2, 8).toUpperCase();
115
+ if (entry.prefix) {
116
+ output.prefix = entry.prefix;
117
+ }
118
+ if (Object.keys(entry.context).length > 0) {
119
+ output.context = entry.context;
120
+ }
121
+ return JSON.stringify(output);
70
122
  }
71
- function formatLog(level, timestamp, args, options) {
72
- const { formatLog: formatLog2, enableColors = true } = options;
73
- const coloredLevel = getColor(level, enableColors);
74
- if (formatLog2) {
75
- return [formatLog2(coloredLevel, timestamp, args)];
123
+ function formatArg(arg) {
124
+ if (arg === null) return "null";
125
+ if (arg === void 0) return "undefined";
126
+ if (typeof arg === "string") return arg;
127
+ if (typeof arg === "number") return String(arg);
128
+ if (typeof arg === "boolean") return String(arg);
129
+ if (typeof arg === "bigint") return `${arg}n`;
130
+ if (typeof arg === "symbol") return arg.toString();
131
+ if (arg instanceof Error) {
132
+ const parts = [arg.message];
133
+ if (arg.name && arg.name !== "Error") parts.unshift(arg.name);
134
+ if (arg.stack) parts.push(arg.stack);
135
+ return parts.join(": ");
136
+ }
137
+ if (Array.isArray(arg)) {
138
+ try {
139
+ return JSON.stringify(arg);
140
+ } catch {
141
+ return String(arg);
142
+ }
143
+ }
144
+ try {
145
+ return JSON.stringify(arg, getCircularReplacer());
146
+ } catch {
147
+ return String(arg);
76
148
  }
77
- return [`${coloredLevel} ${timestamp}`, ...args];
149
+ }
150
+ function getCircularReplacer() {
151
+ const seen = /* @__PURE__ */ new WeakSet();
152
+ return (_key, value) => {
153
+ if (typeof value === "object" && value !== null) {
154
+ if (seen.has(value)) {
155
+ return "[Circular]";
156
+ }
157
+ seen.add(value);
158
+ }
159
+ return value;
160
+ };
161
+ }
162
+ function buildMessage(args) {
163
+ return args.map(formatArg).join(" ");
78
164
  }
79
165
 
80
166
  // src/logger.ts
81
- var defaultFormatTimestamp = (types, date = /* @__PURE__ */ new Date()) => [types.ISO, date.toISOString()];
82
- var LOG_LEVEL_PRIORITIES = {
83
- debug: 0,
84
- info: 1,
85
- warn: 2,
86
- error: 3
87
- };
88
- var Logger = class {
167
+ var Logger = class _Logger {
168
+ /**
169
+ * Create a new Logger instance.
170
+ * @param options - Configuration options
171
+ */
89
172
  constructor(options = {}) {
90
- this.discordQueue = [];
91
- this.isProcessing = false;
92
173
  this.options = {
174
+ level: options.level ?? "debug",
175
+ silent: options.silent ?? false,
93
176
  enableColors: options.enableColors ?? true,
94
- formatTimestamp: options.formatTimestamp ?? defaultFormatTimestamp,
95
- formatLog: options.formatLog,
96
- discord: options.discord
177
+ timestamp: options.timestamp ?? "iso",
178
+ formatter: options.formatter,
179
+ json: options.json ?? false,
180
+ transports: [...options.transports ?? []],
181
+ prefix: options.prefix,
182
+ context: { ...options.context ?? {} }
97
183
  };
98
- this.level = options.level ?? "debug";
184
+ this.transports = this.options.transports;
185
+ this.prefix = this.options.prefix;
186
+ this.context = this.options.context;
187
+ }
188
+ /**
189
+ * Log a trace message (most verbose).
190
+ * @param args - Arguments to log
191
+ */
192
+ trace(...args) {
193
+ this.log("trace", args);
194
+ }
195
+ /**
196
+ * Log a debug message.
197
+ * @param args - Arguments to log
198
+ */
199
+ debug(...args) {
200
+ this.log("debug", args);
99
201
  }
100
202
  /**
101
- * Sets the minimum log level for filtering messages.
102
- * @param level - The log level to set.
203
+ * Log an info message.
204
+ * @param args - Arguments to log
205
+ */
206
+ info(...args) {
207
+ this.log("info", args);
208
+ }
209
+ /**
210
+ * Log a warning message.
211
+ * @param args - Arguments to log
212
+ */
213
+ warn(...args) {
214
+ this.log("warn", args);
215
+ }
216
+ /**
217
+ * Log an error message.
218
+ * @param args - Arguments to log
219
+ */
220
+ error(...args) {
221
+ this.log("error", args);
222
+ }
223
+ /**
224
+ * Log a fatal message (most severe).
225
+ * @param args - Arguments to log
226
+ */
227
+ fatal(...args) {
228
+ this.log("fatal", args);
229
+ }
230
+ /**
231
+ * Set the minimum log level.
232
+ * @param level - The log level to set
103
233
  */
104
234
  setLevel(level) {
105
- this.level = level;
235
+ this.options.level = level;
106
236
  }
107
237
  /**
108
- * Sends a log message to Discord via webhook if configured.
109
- * @param level - The log level.
110
- * @param timestamp - The formatted timestamp.
111
- * @param args - The log arguments.
238
+ * Get the current log level.
239
+ * @returns The current log level
112
240
  */
113
- sendToDiscord(level, timestamp, args) {
114
- const discord = this.options.discord;
115
- if (!discord?.enable) return;
116
- try {
117
- new URL(discord.webhookURL);
118
- } catch {
119
- return;
120
- }
121
- const message = args.map((arg) => typeof arg === "string" ? arg : JSON.stringify(arg)).join(" ");
122
- const title = `${level.toUpperCase()}-${generateId()}`;
123
- const embed = discord.formatEmbed ? discord.formatEmbed(level, timestamp, message) : {
124
- title,
125
- description: message,
126
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
127
- color: getDiscordColor(level)
128
- };
129
- this.discordQueue.push({ embed, retryCount: 0 });
130
- if (!this.isProcessing) {
131
- this.isProcessing = true;
132
- setTimeout(() => this.processQueue(), 0);
133
- }
241
+ getLevel() {
242
+ return this.options.level;
134
243
  }
135
244
  /**
136
- * Processes the Discord queue by sending batches of embeds.
245
+ * Add a transport to the logger.
246
+ * @param transport - The transport to add
137
247
  */
138
- processQueue() {
139
- if (this.discordQueue.length === 0) {
140
- this.isProcessing = false;
141
- return;
248
+ addTransport(transport) {
249
+ this.transports.push(transport);
250
+ }
251
+ /**
252
+ * Remove a transport from the logger.
253
+ * @param transport - The transport to remove
254
+ */
255
+ removeTransport(transport) {
256
+ const index = this.transports.indexOf(transport);
257
+ if (index !== -1) {
258
+ this.transports.splice(index, 1);
142
259
  }
143
- this.isProcessing = true;
144
- const discord = this.options.discord;
145
- const batchSize = discord.batchSize ?? 10;
146
- const batch = this.discordQueue.splice(0, batchSize);
147
- this.sendBatch(batch.map((item) => item.embed)).then(() => {
148
- const delay = discord.batchDelay ?? 2e3;
149
- this.processTimeout = setTimeout(() => this.processQueue(), delay);
150
- }).catch(() => {
151
- const maxRetries = discord.maxRetries ?? 3;
152
- const retryItems = batch.filter((item) => item.retryCount < maxRetries).map((item) => ({
153
- ...item,
154
- retryCount: item.retryCount + 1
155
- }));
156
- this.discordQueue.unshift(...retryItems);
157
- const retryDelayBase = discord.retryDelayBase ?? 1e3;
158
- const delay = retryDelayBase * Math.pow(2, batch[0]?.retryCount ?? 0);
159
- this.processTimeout = setTimeout(() => this.processQueue(), delay);
160
- });
161
260
  }
162
261
  /**
163
- * Sends a batch of embeds to Discord.
164
- * @param embeds - The embeds to send.
262
+ * Create a child logger with additional prefix and context.
263
+ * @param options - Child logger options
264
+ * @returns A new Logger instance
165
265
  */
166
- async sendBatch(embeds) {
167
- const discord = this.options.discord;
168
- await fetch(discord.webhookURL, {
169
- method: "POST",
170
- headers: { "Content-Type": "application/json" },
171
- body: JSON.stringify({ embeds })
266
+ child(options = {}) {
267
+ const combinedPrefix = options.prefix ? this.prefix ? `${this.prefix}:${options.prefix}` : options.prefix : this.prefix;
268
+ const combinedContext = {
269
+ ...this.context,
270
+ ...options.context ?? {}
271
+ };
272
+ return new _Logger({
273
+ level: options.level ?? this.options.level,
274
+ silent: options.silent ?? this.options.silent,
275
+ enableColors: this.options.enableColors,
276
+ timestamp: this.options.timestamp,
277
+ formatter: this.options.formatter,
278
+ json: this.options.json,
279
+ transports: this.transports.slice(),
280
+ prefix: combinedPrefix,
281
+ context: combinedContext
172
282
  });
173
283
  }
174
284
  /**
175
- * Checks if a log level should be output based on the current log level.
176
- * @param level - The log level to check.
177
- * @returns True if the message should be logged.
285
+ * Destroy the logger and all its transports.
286
+ * Calls destroy() on all registered transports.
178
287
  */
179
- shouldLog(level) {
180
- return LOG_LEVEL_PRIORITIES[level] >= LOG_LEVEL_PRIORITIES[this.level];
288
+ async destroy() {
289
+ const destroyPromises = this.transports.map(async (transport) => {
290
+ if (typeof transport.destroy === "function") {
291
+ await transport.destroy();
292
+ }
293
+ });
294
+ await Promise.all(destroyPromises);
295
+ this.transports.length = 0;
181
296
  }
182
297
  /**
183
- * Logs an info message.
184
- * @param args - The arguments to log.
298
+ * Core logging method - all public methods delegate here.
299
+ * @param level - The log level
300
+ * @param args - The arguments to log
185
301
  */
186
- info(...args) {
187
- if (!this.shouldLog("info")) return;
188
- const timestamp = formatTimestamp(
189
- this.options.formatTimestamp,
190
- TIMESTAMP_TYPES
191
- );
192
- console.log(...formatLog("[INFO]", timestamp, args, this.options));
193
- this.sendToDiscord("info", timestamp, args);
302
+ log(level, args) {
303
+ if (!this.shouldLog(level)) return;
304
+ const entry = {
305
+ level,
306
+ timestamp: formatTimestamp(this.options.timestamp),
307
+ args,
308
+ prefix: this.prefix,
309
+ context: this.context
310
+ };
311
+ if (!this.options.silent) {
312
+ this.writeToConsole(level, entry);
313
+ }
314
+ for (const transport of this.transports) {
315
+ this.dispatchToTransport(transport, entry);
316
+ }
194
317
  }
195
318
  /**
196
- * Logs a warning message.
197
- * @param args - The arguments to log.
319
+ * Write a log entry to the console.
320
+ * @param level - The log level
321
+ * @param entry - The log entry
198
322
  */
199
- warn(...args) {
200
- if (!this.shouldLog("warn")) return;
201
- const timestamp = formatTimestamp(
202
- this.options.formatTimestamp,
203
- TIMESTAMP_TYPES
204
- );
205
- console.log(...formatLog("[WARN]", timestamp, args, this.options));
206
- this.sendToDiscord("warn", timestamp, args);
323
+ writeToConsole(level, entry) {
324
+ const method = CONSOLE_METHODS[level];
325
+ if (this.options.formatter) {
326
+ console[method](this.options.formatter(entry));
327
+ return;
328
+ }
329
+ if (this.options.json) {
330
+ console[method](formatJson(entry));
331
+ return;
332
+ }
333
+ const label = getColoredLabel(entry.level, this.options.enableColors);
334
+ const prefixStr = entry.prefix ? ` [${entry.prefix}]` : "";
335
+ const message = buildMessage(entry.args);
336
+ console[method](`${label} ${entry.timestamp}${prefixStr}`, message);
207
337
  }
208
338
  /**
209
- * Logs an error message.
210
- * @param args - The arguments to log.
339
+ * Dispatch a log entry to a transport.
340
+ * @param transport - The transport
341
+ * @param entry - The log entry
211
342
  */
212
- error(...args) {
213
- if (!this.shouldLog("error")) return;
214
- const timestamp = formatTimestamp(
215
- this.options.formatTimestamp,
216
- TIMESTAMP_TYPES
217
- );
218
- console.log(...formatLog("[ERROR]", timestamp, args, this.options));
219
- this.sendToDiscord("error", timestamp, args);
343
+ dispatchToTransport(transport, entry) {
344
+ try {
345
+ const result = transport.log(entry);
346
+ if (result instanceof Promise) {
347
+ result.catch(() => {
348
+ });
349
+ }
350
+ } catch {
351
+ }
220
352
  }
221
353
  /**
222
- * Logs a debug message.
223
- * @param args - The arguments to log.
354
+ * Check if a log level should be output.
355
+ * @param level - The log level to check
356
+ * @returns True if the message should be logged
224
357
  */
225
- debug(...args) {
226
- if (!this.shouldLog("debug")) return;
227
- const timestamp = formatTimestamp(
228
- this.options.formatTimestamp,
229
- TIMESTAMP_TYPES
230
- );
231
- console.log(...formatLog("[DEBUG]", timestamp, args, this.options));
232
- this.sendToDiscord("debug", timestamp, args);
358
+ shouldLog(level) {
359
+ return LOG_LEVEL_PRIORITIES[level] >= LOG_LEVEL_PRIORITIES[this.options.level];
233
360
  }
234
361
  };
235
362
  // Annotate the CommonJS export names for ESM import in node:
236
363
  0 && (module.exports = {
364
+ LEVEL_LABELS,
365
+ LOG_LEVEL_PRIORITIES,
237
366
  Logger,
238
- TIMESTAMP_TYPES
367
+ buildMessage,
368
+ formatJson,
369
+ formatTimestamp,
370
+ getColoredLabel
239
371
  });