analogger 1.37.0 → 2.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.
@@ -158,6 +158,7 @@ const symbolNames = {
158
158
  lock : "🔒",
159
159
  male_sign : "♂",
160
160
  minus_sign : "➖",
161
+ money_bag : "💰",
161
162
  no_entry : "⛔",
162
163
  old_key : "🗝️",
163
164
  partly_sunny : "⛅",
@@ -411,6 +412,153 @@ function createTarGzArchiveSync(inputFile, archivePath, compressionLevel = 1) {
411
412
  }
412
413
  }
413
414
 
415
+ function getInvocationLine()
416
+ {
417
+ try
418
+ {
419
+ const error = new Error();
420
+ const stack = error.stack;
421
+ let anaLoggerPossibleFileName = "ana-logger";
422
+ let isMinified = false;
423
+ let result = null;
424
+ let strippedOutStackTrace;
425
+ let errorText = [];
426
+
427
+ if (stack) {
428
+ const lines = stack.split('\n');
429
+ let fileName = null;
430
+ if (lines.length >= 3) {
431
+ let index = 0;
432
+ // Look for the reference of this line
433
+ for (let i = 0, l = lines.length; i < l; i++) {
434
+ const line = lines[i];
435
+
436
+ // We can't use the name if it's minified
437
+ const parts = line.split(':');
438
+ if (parts.length < 3) {
439
+ errorText.push(line);
440
+ continue;
441
+ }
442
+
443
+ index = i;
444
+
445
+ fileName = parts[parts.length - 3];
446
+
447
+ if (line.indexOf(anaLoggerPossibleFileName)=== -1) {
448
+ // We're sure we can use this stacktrace
449
+ break;
450
+ }
451
+
452
+ if (line.indexOf("getInvocationLine")=== -1) {
453
+ // The file is minified
454
+ // We're only partially sure we can use this stacktrace
455
+ isMinified = true;
456
+ // We try to extract the file name
457
+ anaLoggerPossibleFileName = fileName.split(/[\\/]/).pop();
458
+ break;
459
+ }
460
+
461
+ // We have no idea if the stacktrace will help us, but we leave the search
462
+ break;
463
+ }
464
+
465
+ // Look for when the call was done exactly
466
+ for (let i = index + 1, l = lines.length; i < l; i++) {
467
+ const lineStr = lines[i];
468
+ if (lineStr.indexOf(fileName) > -1) {
469
+ continue;
470
+ }
471
+
472
+ const parts = lineStr.split(':');
473
+ if (parts.length < 3) {
474
+ continue;
475
+ }
476
+
477
+ strippedOutStackTrace = errorText.join("\n") + lines.slice(i).join('\n');
478
+
479
+ const col = parseInt(parts.pop());
480
+ const line = parseInt(parts.pop());
481
+ const file = parts.pop();
482
+ let infoStr = parts.pop();
483
+ let infoArr = infoStr.split(" ");
484
+
485
+ let method = null;
486
+ for (let j = 0; j < infoArr.length; j++) {
487
+ const element = infoArr[j];
488
+ if (!element) {
489
+ continue;
490
+ }
491
+
492
+ if (element.indexOf("at")===0) {
493
+ continue;
494
+ }
495
+
496
+ method = element;
497
+ break;
498
+ }
499
+
500
+ result = {
501
+ file,
502
+ line,
503
+ col,
504
+ method,
505
+ isMinified,
506
+ stack: strippedOutStackTrace
507
+ }
508
+
509
+ break;
510
+ }
511
+
512
+ return result;
513
+ }
514
+ }
515
+ }
516
+ catch(err)
517
+ {
518
+
519
+ }
520
+
521
+ return null;
522
+ }
523
+
524
+ function generateLid(maxChars = 8)
525
+ {
526
+ try
527
+ {
528
+ const line = getInvocationLine();
529
+ if (!line) {
530
+ return `LID${Date.now()}`;
531
+ }
532
+ const fun = line.method.split(".");
533
+ const id = fun[0].toUpperCase().substring(0, 3);
534
+ if (id.length >= maxChars) {
535
+ return id.substring(0, maxChars);
536
+ }
537
+
538
+ let combined1 = `${id}:${line.line}`;
539
+ if (combined1.length >= maxChars) {
540
+ combined1 = combined1.replaceAll(":", "");
541
+ return combined1.substring(0, maxChars);
542
+ }
543
+
544
+ let combined2 = `${id}:${line.line}:${line.col}`;
545
+ if (combined2.length > maxChars) {
546
+ const combined3 = combined2.substring(0, maxChars);
547
+ if (combined3.endsWith(":")) {
548
+ return `${id}${line.line}:${line.col}`.substring(0, maxChars);
549
+ }
550
+
551
+ return combined2.substring(0, maxChars);
552
+ }
553
+
554
+ return `${id}${line.line}:${line.col}`.substring(0, maxChars);
555
+ }
556
+ catch (e) {
557
+ return `ERR_LID${Date.now()}`;
558
+ }
559
+ }
560
+
561
+
414
562
  /**
415
563
  * https://stackoverflow.com/questions/17575790/environment-detection-node-js-or-browser
416
564
  * @returns {string}
@@ -528,6 +676,12 @@ class ____AnaLogger
528
676
 
529
677
  originalFormatFunction;
530
678
 
679
+ static lidTable = {};
680
+ static lidTableOn = false;
681
+
682
+ forceLidOn = false;
683
+ resolveLineCall = false;
684
+ resolveErrorLineCall = false;
531
685
 
532
686
  constructor({name = "default"} = {})
533
687
  {
@@ -581,6 +735,79 @@ class ____AnaLogger
581
735
  return this.instanceId;
582
736
  }
583
737
 
738
+ /**
739
+ * For the logger to generate a lid when none is specified
740
+ * @param lidOn
741
+ */
742
+ forceLid(lidOn = true)
743
+ {
744
+ this.forceLidOn = !!lidOn;
745
+ }
746
+
747
+ forceResolveLineCall(resolveLineCall = true)
748
+ {
749
+ this.resolveLineCall = !!resolveLineCall;
750
+ }
751
+
752
+ forceResolveErrorLineCall(resolveErrorLineCall = true)
753
+ {
754
+ this.resolveErrorLineCall = !!resolveErrorLineCall;
755
+ }
756
+
757
+ importLids(lids)
758
+ {
759
+ for (let lid in lids)
760
+ {
761
+ const lidObj = lids[lid] || {};
762
+ lidObj.lid = lid;
763
+ lidObj.callCount = 0;
764
+ lidObj.callTimes = [];
765
+ ____AnaLogger.lidTable[lid] = lidObj;
766
+ }
767
+ ____AnaLogger.lidTableOn = true;
768
+ }
769
+
770
+ loadLids(lids)
771
+ {
772
+ lids = lids || {};
773
+ this.importLids(lids);
774
+ }
775
+
776
+ convertTimestampToDate(timestamp)
777
+ {
778
+ const date = new Date(timestamp); // Create a Date object from the timestamp
779
+
780
+ const year = date.getFullYear();
781
+ const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed
782
+ const day = String(date.getDate()).padStart(2, '0');
783
+ const hours = String(date.getHours()).padStart(2, '0');
784
+ const minutes = String(date.getMinutes()).padStart(2, '0');
785
+ const seconds = String(date.getSeconds()).padStart(2, '0');
786
+ const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
787
+
788
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
789
+ }
790
+
791
+ getLids()
792
+ {
793
+ const clone = {...__AnaLogger.lidTable };
794
+ for (let lid in clone)
795
+ {
796
+ const lidObj = clone[lid] || {};
797
+ if (lidObj.callTimes.length)
798
+ {
799
+ lidObj.dates = [];
800
+ for (let j = 0; j < lidObj.callTimes.length; ++j)
801
+ {
802
+ const callTime = lidObj.callTimes[j];
803
+ const readableTime = this.convertTimestampToDate(callTime);
804
+ lidObj.dates.push(readableTime);
805
+ }
806
+ }
807
+ }
808
+ return clone;
809
+ }
810
+
584
811
  keepLogHistory()
585
812
  {
586
813
  this.keepLog = true;
@@ -2018,18 +2245,74 @@ class ____AnaLogger
2018
2245
  }
2019
2246
  }
2020
2247
 
2248
+ isContextMessagePattern(str) {
2249
+ return /\{\{[^}]+}}/.test(str);
2250
+ }
2251
+
2252
+ transformContextMessage(template, data)
2253
+ {
2254
+ let result = template;
2255
+ for (const key in data)
2256
+ {
2257
+ const placeholder = `{{${key}}}`;
2258
+ const value = data[key];
2259
+ // Replace all occurrences of the placeholder with the value
2260
+ result = result.replaceAll(placeholder, value);
2261
+ }
2262
+ return result;
2263
+ }
2264
+
2021
2265
  /**
2022
2266
  * Display log following template
2023
2267
  * @param context
2268
+ * @param argsWithoutContext
2024
2269
  */
2025
- processOutput(context = {})
2270
+ processOutput(context = {}, ...argsWithoutContext)
2026
2271
  {
2027
2272
  try
2028
2273
  {
2029
2274
  let message = "";
2275
+
2276
+ if (____AnaLogger.lidTableOn && context.lid) {
2277
+ const lidObj = ____AnaLogger.lidTable[context.lid];
2278
+
2279
+ // If the lid is already in the table, we set the message if not existing
2280
+ if (lidObj)
2281
+ {
2282
+ context.message = context.message || lidObj.message;
2283
+ lidObj.callCount = lidObj.callCount || 0;
2284
+ ++lidObj.callCount;
2285
+ lidObj.callTimes.push(Date.now());
2286
+ }
2287
+ // If the lid is not already in the table, we register it
2288
+ else {
2289
+ ____AnaLogger.lidTable[context.lid] = {
2290
+ ...context,
2291
+ message: argsWithoutContext[0],
2292
+ lid: context.lid,
2293
+ callCount: 1,
2294
+ callTimes: [Date.now()]
2295
+ }
2296
+ }
2297
+ }
2298
+
2299
+ if (context.message) {
2300
+ // If the context message is a template
2301
+ if (this.isContextMessagePattern(context.message)) {
2302
+ // The context message second parameter should be an object that we will use to perform the replacement
2303
+ if (argsWithoutContext.length >= 1 && typeof argsWithoutContext[0] === "object") {
2304
+ context.message = this.transformContextMessage(context.message, argsWithoutContext[0]);
2305
+ // We remove the second parameter from the arguments
2306
+ argsWithoutContext.shift();
2307
+ }
2308
+ }
2309
+
2310
+ // We add the context message as an arguments
2311
+ argsWithoutContext.unshift(context.message);
2312
+ }
2030
2313
  this.applySymbolByName(context);
2031
2314
 
2032
- this.checkOnLogging(context, context, arguments, "onContext");
2315
+ this.checkOnLogging(context, context, argsWithoutContext, "onContext");
2033
2316
  if (!this.isTargetAllowed(context.target))
2034
2317
  {
2035
2318
  return;
@@ -2045,12 +2328,12 @@ class ____AnaLogger
2045
2328
  return;
2046
2329
  }
2047
2330
 
2048
- // Clone arguments without the context (= the first argument passed) to generate the message
2049
- const newMessages = this.checkOnLogging(context, arguments && arguments.length > 1? arguments[1] : arguments, arguments,"onMessage");
2331
+ const newMessages = this.checkOnLogging(context, argsWithoutContext[0], arguments,"onMessage");
2050
2332
  if (newMessages !== undefined) {
2051
2333
  arguments[1] = newMessages;
2052
2334
  }
2053
- let args = Array.prototype.slice.call(arguments, 1 /* => Ignore arguments[0] = context */);
2335
+
2336
+ let args = argsWithoutContext;
2054
2337
 
2055
2338
  message = this.convertArgumentsToText(args);
2056
2339
 
@@ -2157,6 +2440,12 @@ class ____AnaLogger
2157
2440
  options.hasOwnProperty("lid");
2158
2441
  }
2159
2442
 
2443
+ /**
2444
+ * Convert a string to an object by parsing the string
2445
+ * and identifying key-value pairs
2446
+ * @param str
2447
+ * @returns {{}|null}
2448
+ */
2160
2449
  stringToObject(str) {
2161
2450
  try {
2162
2451
  str = str.trim();
@@ -2221,39 +2510,39 @@ class ____AnaLogger
2221
2510
  /**
2222
2511
  * Convert a string into a Context object if possible
2223
2512
  * TODO: To implement in next version
2224
- * @param str
2225
2513
  * @returns {string}
2514
+ * @param input
2226
2515
  */
2227
- extractContextFromInput(str)
2516
+ extractContextFromInput(input)
2228
2517
  {
2229
- if (typeof str === "string" || str instanceof String)
2518
+ if (typeof input === "string" || input instanceof String)
2230
2519
  {
2231
- if (str.toLowerCase().indexOf("lid:") !== 0)
2520
+ if (input.toLowerCase().indexOf("lid:") !== 0)
2232
2521
  {
2233
- return str;
2522
+ return input;
2234
2523
  }
2235
2524
 
2236
- const obj = this.stringToObject(str);
2525
+ const obj = this.stringToObject(input);
2237
2526
  if (obj) {
2238
- str = obj;
2527
+ input = obj;
2239
2528
  }
2240
2529
  }
2241
2530
 
2242
- if (typeof str==="object" && !Array.isArray(str) && str!==null) {
2243
- if (this.isExtendedOptionsPassed(str)) {
2244
- if (str.contextName) {
2245
- const obj = this.#contexts[str.contextName];
2531
+ if (typeof input==="object" && !Array.isArray(input) && input!==null) {
2532
+ if (this.isExtendedOptionsPassed(input)) {
2533
+ if (input.contextName) {
2534
+ const obj = this.#contexts[input.contextName];
2246
2535
  if (obj) {
2247
- str = Object.assign({}, obj, str);
2536
+ input = Object.assign({}, obj, input);
2248
2537
  }
2249
2538
  }
2250
- if (!str.target) {
2251
- str.target = this.getActiveTarget();
2539
+ if (!input.target) {
2540
+ input.target = this.getActiveTarget();
2252
2541
  }
2253
2542
  }
2254
2543
  }
2255
2544
 
2256
- return str;
2545
+ return input;
2257
2546
  }
2258
2547
 
2259
2548
  listSymbols()
@@ -2315,13 +2604,25 @@ class ____AnaLogger
2315
2604
  log(options, ...args)
2316
2605
  {
2317
2606
  options = this.extractContextFromInput(options);
2607
+
2318
2608
  // If the first parameter is not of context format,
2319
2609
  // We use the default context
2320
2610
  if (!this.isExtendedOptionsPassed(options))
2321
2611
  {
2322
- const defaultContext = this.generateDefaultContext();
2323
- this.processOutput.apply(this, [defaultContext, options, ...args]);
2324
- return;
2612
+ if (!this.forceLidOn)
2613
+ {
2614
+ const defaultContext = this.generateDefaultContext();
2615
+ this.processOutput.apply(this, [defaultContext, options, ...args]);
2616
+ return;
2617
+ }
2618
+
2619
+ if (!args || !args.length)
2620
+ {
2621
+ args = [options];
2622
+ }
2623
+
2624
+ const newLid = generateLid(this.options.lidLenMax);
2625
+ options = {lid: newLid};
2325
2626
  }
2326
2627
 
2327
2628
  const someContext = this.generateDefaultContext();
@@ -2333,6 +2634,10 @@ class ____AnaLogger
2333
2634
  return;
2334
2635
  }
2335
2636
 
2637
+ if (this.resolveLineCall)
2638
+ {
2639
+ context.stack = getInvocationLine();
2640
+ }
2336
2641
  this.processOutput.apply(this, [context, ...args]);
2337
2642
  }
2338
2643
 
@@ -2349,16 +2654,25 @@ class ____AnaLogger
2349
2654
  // We use the error context and display
2350
2655
  if (!this.isExtendedOptionsPassed(options))
2351
2656
  {
2352
- const defaultContext = this.generateErrorContext();
2353
- this.processOutput.apply(this, [defaultContext, options, ...args]);
2354
- return;
2657
+ if (!this.forceLidOn)
2658
+ {
2659
+ const defaultContext = this.generateErrorContext();
2660
+ this.processOutput.apply(this, [defaultContext, options, ...args]);
2661
+ return;
2662
+ }
2663
+
2664
+ const newLid = generateLid(this.options.lidLenMax);
2665
+ options = {lid: newLid};
2355
2666
  }
2356
2667
 
2357
2668
  const errorContext = this.generateErrorContext();
2358
2669
  let context = this.convertToContext(options, errorContext);
2359
2670
 
2360
- let args0 = Array.prototype.slice.call(arguments, 1);
2361
- this.log(context, ...args0);
2671
+ if (this.resolveErrorLineCall)
2672
+ {
2673
+ context.stack = getInvocationLine();
2674
+ }
2675
+ this.log(context, ...args);
2362
2676
  }
2363
2677
 
2364
2678
  overrideError()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "analogger",
3
- "version": "1.37.0",
3
+ "version": "2.0.0",
4
4
  "description": "Js Logger",
5
5
  "main": "./src/ana-logger.cjs",
6
6
  "module": "./esm/src/ana-logger.mjs",