bump package version to 3.0.3
[squeep-logger-json-console] / lib / logger.js
index 3f336ab4c7121d17fbeaca6eacfee45bc678c043..32d9d0503331330612db821e5219b700dbd9f690 100644 (file)
@@ -3,27 +3,36 @@
 const jsonReplacers = require('./json-replacers');
 const dataSanitizers = require('./data-sanitizers');
 
-const nop = () => { /**/ };
+const nop = () => undefined;
 
 class Logger {
   /**
-   * @typedef {Object} ConsoleLike
-   * @property {Function(...):void} error
-   * @property {Function(...):void} warn
-   * @property {Function(...):void} info
-   * @property {Function(...):void} log
-   * @property {Function(...):void} debug
+   * @callback LogFn
+   * @param {any} ...args values to log
+   * @returns {void} nothing
    */
   /**
-   * @typedef {Object} LoggerOptions
-   * @property {String} ignoreBelowLevel - minimum level to log, e.g. 'info'
+   * @typedef {object} ConsoleLike
+   * @property {LogFn} error error level
+   * @property {LogFn} warn warn level
+   * @property {LogFn} info info level
+   * @property {LogFn} log log level
+   * @property {LogFn} debug debug level
+   */
+  /**
+   * @typedef AsyncLocalStorage
+   * @property {Function} getStore local storage
+   */
+  /**
+   * @typedef {object} LoggerOptions
+   * @property {string} ignoreBelowLevel minimum level to log, e.g. 'info'
    */
   /**
    * Wrap backend calls with payload normalization and metadata.
-   * @param {LoggerOptions} options
-   * @param {Object} commonObject - object to be merged into logged json
-   * @param {AsyncLocalStorage} asyncLocalStorage async storage for an object to be merged into logged json
-   * @param {ConsoleLike} backend default is console
+   * @param {LoggerOptions} options options
+   * @param {object} commonObject object to be merged into logged json
+   * @param {AsyncLocalStorage} asyncLocalStorage async storage for an object to be merged into logged json
+   * @param {ConsoleLike} backend default is console
    */
   constructor(options, commonObject, asyncLocalStorage, backend) {
     const logLevels = Object.keys(Logger.nullLogger);
@@ -58,6 +67,7 @@ class Logger {
   /**
    * All the expected Console levels, but do nothing.
    * Ordered from highest priority to lowest.
+   * @returns {object} no-op backend
    */
   static get nullLogger() {
     return {
@@ -73,6 +83,7 @@ class Logger {
   /**
    * Default of empty object when no asyncLocalStorage is defined.
    * Overridden on instance when asyncLocalStorage is defined.
+   * @returns {object} empty
    */
   // eslint-disable-next-line class-methods-use-this
   get asyncLogObject() {
@@ -82,19 +93,23 @@ class Logger {
 
   /**
    * Structure all expected log data into JSON string.
-   * @param {String} level
-   * @param {String} scope
-   * @param {String} message
-   * @param {Object} data
-   * @param  {...any} other
-   * @returns {String} JSON string
+   * @param {string} level log level
+   * @param {string} scope scope
+   * @param {string} message message
+   * @param {object} data data
+   * @param  {...any} other more data merged into payload
+   * @returns {string} JSON string
    */
   payload(level, scope, message, data, ...other) {
     const replacer = this.getReplacer();
 
     if (this.sanitizationNeeded(data)) {
       // Create copy of data so we are not changing anything important.
-      data = structuredClone(data);
+      try {
+        data = structuredClone(data);
+      } catch (e) { // eslint-disable-line no-unused-vars
+        data = JSON.parse(JSON.stringify(data, replacer));
+      }
       this.sanitize(data);
     }
 
@@ -116,8 +131,8 @@ class Logger {
 
   /**
    * Determine if data needs sanitizing.
-   * @param {Object} data
-   * @returns {Boolean}
+   * @param {object} data data
+   * @returns {boolean} needs sanitization
    */
   sanitizationNeeded(data) {
     return this.dataSanitizers.some((sanitizer) => sanitizer(data, false));
@@ -126,7 +141,7 @@ class Logger {
 
   /**
    * Mogrify data.
-   * @param {Object} data
+   * @param {object} data data
    */
   sanitize(data) {
     this.dataSanitizers.forEach((sanitizer) => sanitizer(data));
@@ -135,6 +150,7 @@ class Logger {
 
   /**
    * Return a replacer function which does de-cycling, as well as the rest of our replacers.
+   * @returns {Function} replacer
    */
   getReplacer() {
     const ancestors = [];
@@ -150,7 +166,7 @@ class Logger {
         while (ancestors.length > 0 && ancestors.at(-1) !== this) {
           ancestors.pop();
         }
-        if (ancestors.includes(value)) { // eslint-disable-line security/detect-object-injection
+        if (ancestors.includes(value)) {
           return '[Circular]';
         } else {
           ancestors.push(value);
@@ -158,7 +174,7 @@ class Logger {
       }
 
       return value;
-    }
+    };
   }
 
 }