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.
@@ -398,6 +398,153 @@ function createTarGzArchiveSync(inputFile, archivePath, compressionLevel = 1) {
398
398
  }
399
399
  }
400
400
 
401
+ function getInvocationLine()
402
+ {
403
+ try
404
+ {
405
+ const error = new Error();
406
+ const stack = error.stack;
407
+ let anaLoggerPossibleFileName = "ana-logger";
408
+ let isMinified = false;
409
+ let result = null;
410
+ let strippedOutStackTrace;
411
+ let errorText = [];
412
+
413
+ if (stack) {
414
+ const lines = stack.split('\n');
415
+ let fileName = null;
416
+ if (lines.length >= 3) {
417
+ let index = 0;
418
+ // Look for the reference of this line
419
+ for (let i = 0, l = lines.length; i < l; i++) {
420
+ const line = lines[i];
421
+
422
+ // We can't use the name if it's minified
423
+ const parts = line.split(':');
424
+ if (parts.length < 3) {
425
+ errorText.push(line);
426
+ continue;
427
+ }
428
+
429
+ index = i;
430
+
431
+ fileName = parts[parts.length - 3];
432
+
433
+ if (line.indexOf(anaLoggerPossibleFileName)=== -1) {
434
+ // We're sure we can use this stacktrace
435
+ break;
436
+ }
437
+
438
+ if (line.indexOf("getInvocationLine")=== -1) {
439
+ // The file is minified
440
+ // We're only partially sure we can use this stacktrace
441
+ isMinified = true;
442
+ // We try to extract the file name
443
+ anaLoggerPossibleFileName = fileName.split(/[\\/]/).pop();
444
+ break;
445
+ }
446
+
447
+ // We have no idea if the stacktrace will help us, but we leave the search
448
+ break;
449
+ }
450
+
451
+ // Look for when the call was done exactly
452
+ for (let i = index + 1, l = lines.length; i < l; i++) {
453
+ const lineStr = lines[i];
454
+ if (lineStr.indexOf(fileName) > -1) {
455
+ continue;
456
+ }
457
+
458
+ const parts = lineStr.split(':');
459
+ if (parts.length < 3) {
460
+ continue;
461
+ }
462
+
463
+ strippedOutStackTrace = errorText.join("\n") + lines.slice(i).join('\n');
464
+
465
+ const col = parseInt(parts.pop());
466
+ const line = parseInt(parts.pop());
467
+ const file = parts.pop();
468
+ let infoStr = parts.pop();
469
+ let infoArr = infoStr.split(" ");
470
+
471
+ let method = null;
472
+ for (let j = 0; j < infoArr.length; j++) {
473
+ const element = infoArr[j];
474
+ if (!element) {
475
+ continue;
476
+ }
477
+
478
+ if (element.indexOf("at")===0) {
479
+ continue;
480
+ }
481
+
482
+ method = element;
483
+ break;
484
+ }
485
+
486
+ result = {
487
+ file,
488
+ line,
489
+ col,
490
+ method,
491
+ isMinified,
492
+ stack: strippedOutStackTrace
493
+ }
494
+
495
+ break;
496
+ }
497
+
498
+ return result;
499
+ }
500
+ }
501
+ }
502
+ catch(err)
503
+ {
504
+
505
+ }
506
+
507
+ return null;
508
+ }
509
+
510
+ function generateLid(maxChars = 8)
511
+ {
512
+ try
513
+ {
514
+ const line = getInvocationLine();
515
+ if (!line) {
516
+ return `LID${Date.now()}`;
517
+ }
518
+ const fun = line.method.split(".");
519
+ const id = fun[0].toUpperCase().substring(0, 3);
520
+ if (id.length >= maxChars) {
521
+ return id.substring(0, maxChars);
522
+ }
523
+
524
+ let combined1 = `${id}:${line.line}`;
525
+ if (combined1.length >= maxChars) {
526
+ combined1 = combined1.replaceAll(":", "");
527
+ return combined1.substring(0, maxChars);
528
+ }
529
+
530
+ let combined2 = `${id}:${line.line}:${line.col}`;
531
+ if (combined2.length > maxChars) {
532
+ const combined3 = combined2.substring(0, maxChars);
533
+ if (combined3.endsWith(":")) {
534
+ return `${id}${line.line}:${line.col}`.substring(0, maxChars);
535
+ }
536
+
537
+ return combined2.substring(0, maxChars);
538
+ }
539
+
540
+ return `${id}${line.line}:${line.col}`.substring(0, maxChars);
541
+ }
542
+ catch (e) {
543
+ return `ERR_LID${Date.now()}`;
544
+ }
545
+ }
546
+
547
+
401
548
  /**
402
549
  * https://stackoverflow.com/questions/17575790/environment-detection-node-js-or-browser
403
550
  * @returns {string}
@@ -515,6 +662,12 @@ class ____AnaLogger
515
662
 
516
663
  originalFormatFunction;
517
664
 
665
+ static lidTable = {};
666
+ static lidTableOn = false;
667
+
668
+ forceLidOn = false;
669
+ resolveLineCall = false;
670
+ resolveErrorLineCall = false;
518
671
 
519
672
  constructor({name = "default"} = {})
520
673
  {
@@ -568,6 +721,79 @@ class ____AnaLogger
568
721
  return this.instanceId;
569
722
  }
570
723
 
724
+ /**
725
+ * For the logger to generate a lid when none is specified
726
+ * @param lidOn
727
+ */
728
+ forceLid(lidOn = true)
729
+ {
730
+ this.forceLidOn = !!lidOn;
731
+ }
732
+
733
+ forceResolveLineCall(resolveLineCall = true)
734
+ {
735
+ this.resolveLineCall = !!resolveLineCall;
736
+ }
737
+
738
+ forceResolveErrorLineCall(resolveErrorLineCall = true)
739
+ {
740
+ this.resolveErrorLineCall = !!resolveErrorLineCall;
741
+ }
742
+
743
+ importLids(lids)
744
+ {
745
+ for (let lid in lids)
746
+ {
747
+ const lidObj = lids[lid] || {};
748
+ lidObj.lid = lid;
749
+ lidObj.callCount = 0;
750
+ lidObj.callTimes = [];
751
+ ____AnaLogger.lidTable[lid] = lidObj;
752
+ }
753
+ ____AnaLogger.lidTableOn = true;
754
+ }
755
+
756
+ loadLids(lids)
757
+ {
758
+ lids = lids || {};
759
+ this.importLids(lids);
760
+ }
761
+
762
+ convertTimestampToDate(timestamp)
763
+ {
764
+ const date = new Date(timestamp); // Create a Date object from the timestamp
765
+
766
+ const year = date.getFullYear();
767
+ const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed
768
+ const day = String(date.getDate()).padStart(2, '0');
769
+ const hours = String(date.getHours()).padStart(2, '0');
770
+ const minutes = String(date.getMinutes()).padStart(2, '0');
771
+ const seconds = String(date.getSeconds()).padStart(2, '0');
772
+ const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
773
+
774
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
775
+ }
776
+
777
+ getLids()
778
+ {
779
+ const clone = {...__AnaLogger.lidTable };
780
+ for (let lid in clone)
781
+ {
782
+ const lidObj = clone[lid] || {};
783
+ if (lidObj.callTimes.length)
784
+ {
785
+ lidObj.dates = [];
786
+ for (let j = 0; j < lidObj.callTimes.length; ++j)
787
+ {
788
+ const callTime = lidObj.callTimes[j];
789
+ const readableTime = this.convertTimestampToDate(callTime);
790
+ lidObj.dates.push(readableTime);
791
+ }
792
+ }
793
+ }
794
+ return clone;
795
+ }
796
+
571
797
  keepLogHistory()
572
798
  {
573
799
  this.keepLog = true;
@@ -2007,18 +2233,74 @@ class ____AnaLogger
2007
2233
  }
2008
2234
  }
2009
2235
 
2236
+ isContextMessagePattern(str) {
2237
+ return /\{\{[^}]+}}/.test(str);
2238
+ }
2239
+
2240
+ transformContextMessage(template, data)
2241
+ {
2242
+ let result = template;
2243
+ for (const key in data)
2244
+ {
2245
+ const placeholder = `{{${key}}}`;
2246
+ const value = data[key];
2247
+ // Replace all occurrences of the placeholder with the value
2248
+ result = result.replaceAll(placeholder, value);
2249
+ }
2250
+ return result;
2251
+ }
2252
+
2010
2253
  /**
2011
2254
  * Display log following template
2012
2255
  * @param context
2256
+ * @param argsWithoutContext
2013
2257
  */
2014
- processOutput(context = {})
2258
+ processOutput(context = {}, ...argsWithoutContext)
2015
2259
  {
2016
2260
  try
2017
2261
  {
2018
2262
  let message = "";
2263
+
2264
+ if (____AnaLogger.lidTableOn && context.lid) {
2265
+ const lidObj = ____AnaLogger.lidTable[context.lid];
2266
+
2267
+ // If the lid is already in the table, we set the message if not existing
2268
+ if (lidObj)
2269
+ {
2270
+ context.message = context.message || lidObj.message;
2271
+ lidObj.callCount = lidObj.callCount || 0;
2272
+ ++lidObj.callCount;
2273
+ lidObj.callTimes.push(Date.now());
2274
+ }
2275
+ // If the lid is not already in the table, we register it
2276
+ else {
2277
+ ____AnaLogger.lidTable[context.lid] = {
2278
+ ...context,
2279
+ message: argsWithoutContext[0],
2280
+ lid: context.lid,
2281
+ callCount: 1,
2282
+ callTimes: [Date.now()]
2283
+ }
2284
+ }
2285
+ }
2286
+
2287
+ if (context.message) {
2288
+ // If the context message is a template
2289
+ if (this.isContextMessagePattern(context.message)) {
2290
+ // The context message second parameter should be an object that we will use to perform the replacement
2291
+ if (argsWithoutContext.length >= 1 && typeof argsWithoutContext[0] === "object") {
2292
+ context.message = this.transformContextMessage(context.message, argsWithoutContext[0]);
2293
+ // We remove the second parameter from the arguments
2294
+ argsWithoutContext.shift();
2295
+ }
2296
+ }
2297
+
2298
+ // We add the context message as an arguments
2299
+ argsWithoutContext.unshift(context.message);
2300
+ }
2019
2301
  this.applySymbolByName(context);
2020
2302
 
2021
- this.checkOnLogging(context, context, arguments, "onContext");
2303
+ this.checkOnLogging(context, context, argsWithoutContext, "onContext");
2022
2304
  if (!this.isTargetAllowed(context.target))
2023
2305
  {
2024
2306
  return;
@@ -2034,12 +2316,12 @@ class ____AnaLogger
2034
2316
  return;
2035
2317
  }
2036
2318
 
2037
- // Clone arguments without the context (= the first argument passed) to generate the message
2038
- const newMessages = this.checkOnLogging(context, arguments && arguments.length > 1? arguments[1] : arguments, arguments,"onMessage");
2319
+ const newMessages = this.checkOnLogging(context, argsWithoutContext[0], arguments,"onMessage");
2039
2320
  if (newMessages !== undefined) {
2040
2321
  arguments[1] = newMessages;
2041
2322
  }
2042
- let args = Array.prototype.slice.call(arguments, 1 /* => Ignore arguments[0] = context */);
2323
+
2324
+ let args = argsWithoutContext;
2043
2325
 
2044
2326
  message = this.convertArgumentsToText(args);
2045
2327
 
@@ -2146,6 +2428,12 @@ class ____AnaLogger
2146
2428
  options.hasOwnProperty("lid");
2147
2429
  }
2148
2430
 
2431
+ /**
2432
+ * Convert a string to an object by parsing the string
2433
+ * and identifying key-value pairs
2434
+ * @param str
2435
+ * @returns {{}|null}
2436
+ */
2149
2437
  stringToObject(str) {
2150
2438
  try {
2151
2439
  str = str.trim();
@@ -2210,39 +2498,39 @@ class ____AnaLogger
2210
2498
  /**
2211
2499
  * Convert a string into a Context object if possible
2212
2500
  * TODO: To implement in next version
2213
- * @param str
2214
2501
  * @returns {string}
2502
+ * @param input
2215
2503
  */
2216
- extractContextFromInput(str)
2504
+ extractContextFromInput(input)
2217
2505
  {
2218
- if (typeof str === "string" || str instanceof String)
2506
+ if (typeof input === "string" || input instanceof String)
2219
2507
  {
2220
- if (str.toLowerCase().indexOf("lid:") !== 0)
2508
+ if (input.toLowerCase().indexOf("lid:") !== 0)
2221
2509
  {
2222
- return str;
2510
+ return input;
2223
2511
  }
2224
2512
 
2225
- const obj = this.stringToObject(str);
2513
+ const obj = this.stringToObject(input);
2226
2514
  if (obj) {
2227
- str = obj;
2515
+ input = obj;
2228
2516
  }
2229
2517
  }
2230
2518
 
2231
- if (typeof str==="object" && !Array.isArray(str) && str!==null) {
2232
- if (this.isExtendedOptionsPassed(str)) {
2233
- if (str.contextName) {
2234
- const obj = this.#contexts[str.contextName];
2519
+ if (typeof input==="object" && !Array.isArray(input) && input!==null) {
2520
+ if (this.isExtendedOptionsPassed(input)) {
2521
+ if (input.contextName) {
2522
+ const obj = this.#contexts[input.contextName];
2235
2523
  if (obj) {
2236
- str = Object.assign({}, obj, str);
2524
+ input = Object.assign({}, obj, input);
2237
2525
  }
2238
2526
  }
2239
- if (!str.target) {
2240
- str.target = this.getActiveTarget();
2527
+ if (!input.target) {
2528
+ input.target = this.getActiveTarget();
2241
2529
  }
2242
2530
  }
2243
2531
  }
2244
2532
 
2245
- return str;
2533
+ return input;
2246
2534
  }
2247
2535
 
2248
2536
  listSymbols()
@@ -2304,13 +2592,25 @@ class ____AnaLogger
2304
2592
  log(options, ...args)
2305
2593
  {
2306
2594
  options = this.extractContextFromInput(options);
2595
+
2307
2596
  // If the first parameter is not of context format,
2308
2597
  // We use the default context
2309
2598
  if (!this.isExtendedOptionsPassed(options))
2310
2599
  {
2311
- const defaultContext = this.generateDefaultContext();
2312
- this.processOutput.apply(this, [defaultContext, options, ...args]);
2313
- return;
2600
+ if (!this.forceLidOn)
2601
+ {
2602
+ const defaultContext = this.generateDefaultContext();
2603
+ this.processOutput.apply(this, [defaultContext, options, ...args]);
2604
+ return;
2605
+ }
2606
+
2607
+ if (!args || !args.length)
2608
+ {
2609
+ args = [options];
2610
+ }
2611
+
2612
+ const newLid = generateLid(this.options.lidLenMax);
2613
+ options = {lid: newLid};
2314
2614
  }
2315
2615
 
2316
2616
  const someContext = this.generateDefaultContext();
@@ -2322,6 +2622,14 @@ class ____AnaLogger
2322
2622
  return;
2323
2623
  }
2324
2624
 
2625
+ if (this.resolveLineCall)
2626
+ {
2627
+ // If this.resolveErrorLineCall, the stack has already been set
2628
+ if (!this.resolveErrorLineCall)
2629
+ {
2630
+ context.stack = getInvocationLine();
2631
+ }
2632
+ }
2325
2633
  this.processOutput.apply(this, [context, ...args]);
2326
2634
  }
2327
2635
 
@@ -2338,16 +2646,25 @@ class ____AnaLogger
2338
2646
  // We use the error context and display
2339
2647
  if (!this.isExtendedOptionsPassed(options))
2340
2648
  {
2341
- const defaultContext = this.generateErrorContext();
2342
- this.processOutput.apply(this, [defaultContext, options, ...args]);
2343
- return;
2649
+ if (!this.forceLidOn)
2650
+ {
2651
+ const defaultContext = this.generateErrorContext();
2652
+ this.processOutput.apply(this, [defaultContext, options, ...args]);
2653
+ return;
2654
+ }
2655
+
2656
+ const newLid = generateLid(this.options.lidLenMax);
2657
+ options = {lid: newLid};
2344
2658
  }
2345
2659
 
2346
2660
  const errorContext = this.generateErrorContext();
2347
2661
  let context = this.convertToContext(options, errorContext);
2348
2662
 
2349
- let args0 = Array.prototype.slice.call(arguments, 1);
2350
- this.log(context, ...args0);
2663
+ if (this.resolveErrorLineCall)
2664
+ {
2665
+ context.stack = getInvocationLine();
2666
+ }
2667
+ this.log(context, ...args);
2351
2668
  }
2352
2669
 
2353
2670
  overrideError()