All files / tao-telemetry/src TaoLogger.js

100% Statements 35/35
100% Branches 33/33
100% Functions 14/14
100% Lines 29/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119                                                                                                      18x 18x           21x   15x   18x   32x 1x   31x 26x 26x 5x     5x   26x 26x 26x 26x 5x       1x     1x     9x 9x           12x   12x       1x     2x     7x   7x 12x   6x          
/**
 * Runtime control surface returned by {@link TaoLogger}. Every option is
 * live-switchable after construction.
 *
 * @typedef {Object} TaoLoggerControls
 * @property {function(Object, Object): void} handler - the signal handler to
 *           attach (full-wildcard intercept idiom); receives `(tao, data)`
 *           and always returns undefined — pure observation, never halts
 * @property {function(boolean): void} doLogging - toggle logging entirely
 * @property {function(boolean): void} verbose - toggle datagram logging
 * @property {function(?number): void} depth - set the inspection depth
 * @property {function(boolean): void} group - toggle grouped output
 * @property {function(Object): void} setLogger - swap the console-like target
 * @property {function(?function(*, number): *): void} setInspect - swap the
 *           object formatter
 */
 
/**
 * The classic live signal logger (moved here from `@tao.js/utils`): logs
 * each signal's trigram — and, when verbose or grouped, the t/a/o portions
 * of its datagram — as it dispatches. Attach the returned `handler` as a
 * full-wildcard intercept: `TAO.addInterceptHandler({}, logger.handler)`.
 *
 * Shows the sequence of signals; the `Tracer` shows their causality.
 *
 * @export
 * @param {boolean} [doLogging=true] - master switch; the handler is a no-op
 *        when false
 * @param {Object} [opts]
 * @param {boolean} [opts.verbose=false] - also log the datagram portions
 *        (grouped output always does)
 * @param {?number} [opts.depth=0] - depth handed to `inspect`; when unset the
 *        datagram portions are logged raw
 * @param {boolean} [opts.group=false] - wrap each signal in
 *        `logger.groupCollapsed` / `logger.groupEnd`
 * @param {{ info: function, groupCollapsed?: function, groupEnd?: function }} [opts.logger=console] -
 *        console-like target
 * @param {?function(*, number): *} [opts.inspect] - object formatter (e.g.
 *        `util.inspect`) applied to each datagram portion at `depth`
 * @returns {TaoLoggerControls}
 */
export function TaoLogger(
  doLogging = true,
  {
    verbose = false,
    depth = 0,
    group = false,
    logger = console,
    inspect = null,
  } = {},
) {
  let inspector = null;
  if (
    // Stryker disable next-line ConditionalExpression: equivalent - whenever inspect is null/undefined the typeof clause below is also true, so this clause alone never changes the outcome
    inspect == null ||
    typeof inspect != 'function' ||
    (!depth && depth != null)
  ) {
    inspector = (obj) => obj;
  } else {
    inspector = (obj) => inspect(obj, depth);
  }
  return {
    handler: (tao, data) => {
      if (!doLogging) {
        return;
      }
      if (!group) {
        logger.info(`☯{${tao.t}, ${tao.a}, ${tao.o}}:`);
        if (!verbose) {
          return;
        }
      } else {
        logger.groupCollapsed(`☯{${tao.t}, ${tao.a}, ${tao.o}}:`);
      }
      logger.info(`${tao.t}:\n`, inspector(data[tao.t]));
      logger.info(`${tao.a}:\n`, inspector(data[tao.a]));
      logger.info(`${tao.o}:\n`, inspector(data[tao.o]));
      if (group) {
        logger.groupEnd();
      }
    },
    doLogging: (v) => {
      doLogging = !!v;
    },
    verbose: (v) => {
      verbose = !!v;
    },
    depth: (v) => {
      depth = v;
      if (
        // Stryker disable next-line ConditionalExpression: equivalent - whenever inspect is null/undefined the typeof clause below is also true, so this clause alone never changes the outcome
        inspect == null ||
        typeof inspect != 'function' ||
        (!v && v == null)
      ) {
        inspector = (obj) => obj;
      } else {
        inspector = (obj) => inspect(obj, depth);
      }
    },
    group: (v) => {
      group = !!v;
    },
    setLogger: (l) => {
      logger = l;
    },
    setInspect: (i) => {
      inspect = i;
      // Stryker disable next-line ConditionalExpression: equivalent - whenever i is null/undefined the typeof clause is also true, so the clause1-alone mutant never changes the outcome (other clauses on this line remain covered by tests)
      if (i == null || typeof i != 'function' || (!depth && depth == null)) {
        inspector = (obj) => obj;
      } else {
        inspector = (obj) => inspect(obj, depth);
      }
    },
  };
}