analogger 1.36.1 → 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.
@@ -144,6 +144,7 @@ const symbolNames = {
144
144
  lock : "🔒",
145
145
  male_sign : "♂",
146
146
  minus_sign : "➖",
147
+ money_bag : "💰",
147
148
  no_entry : "⛔",
148
149
  old_key : "🗝️",
149
150
  partly_sunny : "⛅",
@@ -397,6 +398,153 @@ function createTarGzArchiveSync(inputFile, archivePath, compressionLevel = 1) {
397
398
  }
398
399
  }
399
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
+
400
548
  /**
401
549
  * https://stackoverflow.com/questions/17575790/environment-detection-node-js-or-browser
402
550
  * @returns {string}
@@ -514,6 +662,12 @@ class ____AnaLogger
514
662
 
515
663
  originalFormatFunction;
516
664
 
665
+ static lidTable = {};
666
+ static lidTableOn = false;
667
+
668
+ forceLidOn = false;
669
+ resolveLineCall = false;
670
+ resolveErrorLineCall = false;
517
671
 
518
672
  constructor({name = "default"} = {})
519
673
  {
@@ -567,6 +721,79 @@ class ____AnaLogger
567
721
  return this.instanceId;
568
722
  }
569
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
+
570
797
  keepLogHistory()
571
798
  {
572
799
  this.keepLog = true;
@@ -2006,18 +2233,74 @@ class ____AnaLogger
2006
2233
  }
2007
2234
  }
2008
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
+
2009
2253
  /**
2010
2254
  * Display log following template
2011
2255
  * @param context
2256
+ * @param argsWithoutContext
2012
2257
  */
2013
- processOutput(context = {})
2258
+ processOutput(context = {}, ...argsWithoutContext)
2014
2259
  {
2015
2260
  try
2016
2261
  {
2017
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
+ }
2018
2301
  this.applySymbolByName(context);
2019
2302
 
2020
- this.checkOnLogging(context, context, arguments, "onContext");
2303
+ this.checkOnLogging(context, context, argsWithoutContext, "onContext");
2021
2304
  if (!this.isTargetAllowed(context.target))
2022
2305
  {
2023
2306
  return;
@@ -2033,12 +2316,12 @@ class ____AnaLogger
2033
2316
  return;
2034
2317
  }
2035
2318
 
2036
- // Clone arguments without the context (= the first argument passed) to generate the message
2037
- const newMessages = this.checkOnLogging(context, arguments && arguments.length > 1? arguments[1] : arguments, arguments,"onMessage");
2319
+ const newMessages = this.checkOnLogging(context, argsWithoutContext[0], arguments,"onMessage");
2038
2320
  if (newMessages !== undefined) {
2039
2321
  arguments[1] = newMessages;
2040
2322
  }
2041
- let args = Array.prototype.slice.call(arguments, 1 /* => Ignore arguments[0] = context */);
2323
+
2324
+ let args = argsWithoutContext;
2042
2325
 
2043
2326
  message = this.convertArgumentsToText(args);
2044
2327
 
@@ -2145,6 +2428,12 @@ class ____AnaLogger
2145
2428
  options.hasOwnProperty("lid");
2146
2429
  }
2147
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
+ */
2148
2437
  stringToObject(str) {
2149
2438
  try {
2150
2439
  str = str.trim();
@@ -2209,39 +2498,39 @@ class ____AnaLogger
2209
2498
  /**
2210
2499
  * Convert a string into a Context object if possible
2211
2500
  * TODO: To implement in next version
2212
- * @param str
2213
2501
  * @returns {string}
2502
+ * @param input
2214
2503
  */
2215
- extractContextFromInput(str)
2504
+ extractContextFromInput(input)
2216
2505
  {
2217
- if (typeof str === "string" || str instanceof String)
2506
+ if (typeof input === "string" || input instanceof String)
2218
2507
  {
2219
- if (str.toLowerCase().indexOf("lid:") !== 0)
2508
+ if (input.toLowerCase().indexOf("lid:") !== 0)
2220
2509
  {
2221
- return str;
2510
+ return input;
2222
2511
  }
2223
2512
 
2224
- const obj = this.stringToObject(str);
2513
+ const obj = this.stringToObject(input);
2225
2514
  if (obj) {
2226
- str = obj;
2515
+ input = obj;
2227
2516
  }
2228
2517
  }
2229
2518
 
2230
- if (typeof str==="object" && !Array.isArray(str) && str!==null) {
2231
- if (this.isExtendedOptionsPassed(str)) {
2232
- if (str.contextName) {
2233
- 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];
2234
2523
  if (obj) {
2235
- str = Object.assign({}, obj, str);
2524
+ input = Object.assign({}, obj, input);
2236
2525
  }
2237
2526
  }
2238
- if (!str.target) {
2239
- str.target = this.getActiveTarget();
2527
+ if (!input.target) {
2528
+ input.target = this.getActiveTarget();
2240
2529
  }
2241
2530
  }
2242
2531
  }
2243
2532
 
2244
- return str;
2533
+ return input;
2245
2534
  }
2246
2535
 
2247
2536
  listSymbols()
@@ -2303,13 +2592,25 @@ class ____AnaLogger
2303
2592
  log(options, ...args)
2304
2593
  {
2305
2594
  options = this.extractContextFromInput(options);
2595
+
2306
2596
  // If the first parameter is not of context format,
2307
2597
  // We use the default context
2308
2598
  if (!this.isExtendedOptionsPassed(options))
2309
2599
  {
2310
- const defaultContext = this.generateDefaultContext();
2311
- this.processOutput.apply(this, [defaultContext, options, ...args]);
2312
- 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};
2313
2614
  }
2314
2615
 
2315
2616
  const someContext = this.generateDefaultContext();
@@ -2321,6 +2622,14 @@ class ____AnaLogger
2321
2622
  return;
2322
2623
  }
2323
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
+ }
2324
2633
  this.processOutput.apply(this, [context, ...args]);
2325
2634
  }
2326
2635
 
@@ -2337,16 +2646,25 @@ class ____AnaLogger
2337
2646
  // We use the error context and display
2338
2647
  if (!this.isExtendedOptionsPassed(options))
2339
2648
  {
2340
- const defaultContext = this.generateErrorContext();
2341
- this.processOutput.apply(this, [defaultContext, options, ...args]);
2342
- 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};
2343
2658
  }
2344
2659
 
2345
2660
  const errorContext = this.generateErrorContext();
2346
2661
  let context = this.convertToContext(options, errorContext);
2347
2662
 
2348
- let args0 = Array.prototype.slice.call(arguments, 1);
2349
- this.log(context, ...args0);
2663
+ if (this.resolveErrorLineCall)
2664
+ {
2665
+ context.stack = getInvocationLine();
2666
+ }
2667
+ this.log(context, ...args);
2350
2668
  }
2351
2669
 
2352
2670
  overrideError()