Как я могу переопределить console.log так, чтобы у каждого вызова был UUID объекта? - PullRequest
0 голосов
/ 05 июня 2019

Я пишу класс в Node.js, который выполняет серию веб-запросов. Этот класс создается несколько раз для выполнения различных веб-запросов. Программа выполняется в терминале, и поэтому мне нужны специальные возможности ведения журнала, чтобы я мог отлаживать ошибки и наблюдать за успехом.

Я переопределил определения функций для каждого, чтобы я мог добавить UUID к каждому оператору журнала следующим образом:

[16:07:22.911] [LOG]    [54fccbc5-f6c8-4e0a-b42a-196e831df0e6]  hello from worker 1

В этом случае каждый UUID уникален для экземпляра класса.

Я использую другой модуль npm, console-stamp для добавления метки времени и метаданных уровня журнала.

require('console-stamp')(console, {
  pattern: 'HH:MM:ss.l',
  colors: {
    stamp: chalk.cyan,
  },
});

Чтобы сделать это переопределение, я создал метод, который присоединяет UUID рабочего класса к операторам журнала:

function addUuidToConsole(uuid) {
  if (console.log) {
    const old = console.log;
    console.log = function log(...args) {
      Array.prototype.unshift.call(args, `[${uuid}] `);
      old.apply(this, args);
    };
  }
  if (console.warn) {
    const old = console.warn;
    console.warn = function warn(...args) {
      Array.prototype.unshift.call(args, `[${chalk.red(uuid)}] `);
      old.apply(this, args);
    };
  }
  if (console.error) {
    const old = console.error;
    console.error = function error(...args) {
      Array.prototype.unshift.call(args, `[${chalk.red(uuid)}] `);
      old.apply(this, args);
    };
  }
}

Затем в моем конструкторе класса my я вызываю эту функцию с помощью uuid экземпляра.

class Worker {
  constructor(profile) {
    ...
    this.uuid = uuidv4();
    ...
    addUuidToConsole(this.uuid);
    ...
  }
  ...
}

Задача

Это решение подходит для случаев, когда я использую только один экземпляр Worker. Однако, когда я использую более 1 экземпляра, последующие UUID добавляются последовательно.

[16:45:47.606] [LOG]    [9ce5e2b8-d49d-40c9-bb9d-3ed9e83fb441]  hello from worker 1
[16:45:47.607] [LOG]    [9ce5e2b8-d49d-40c9-bb9d-3ed9e83fb441]  [ef5bab6c-31c2-4ad9-aea0-c435f1861989]  hello from worker 2

Кроме того, моя вторичная проблема заключается в том, что это переопределение отбрасывает мое использование console.time() и console.timeEnd(), которые я использую для измерения эффективности моих запросов.

Я использую UUID при вызове этих методов синхронизации, и после переопределения это стало очень уродливым. Когда я звоню console.timeEnd(), я получаю вывод примерно так:

[16:45:47.606] [LOG]    [9ce5e2b8-d49d-40c9-bb9d-3ed9e83fb441]  %s: %sms 9ce5e2b8-d49d-40c9-bb9d-3ed9e83fb441 3.860

Конечно, я хочу, чтобы журналы были видны отдельно, а не "накапливались". Я думаю, что эта ошибка связана с тем, что экземпляры Worker совместно используют один и тот же объект console, хотя я не уверен, как можно обойти это так, чтобы их выходные данные были похожи:

[16:45:47.606] [LOG]    [9ce5e2b8-d49d-40c9-bb9d-3ed9e83fb441]  hello from worker 1
[16:45:47.607] [LOG]    [ef5bab6c-31c2-4ad9-aea0-c435f1861989]  hello from worker 2

Одним из решений, которое я рассмотрел, было бы отказаться от переопределенных методов и использовать функцию formatMessage() внутри каждого из моих вызовов к console.log() и console.debug() и так далее.

Мой вопрос заключается в том, как мне разработать элегантное решение, чтобы можно было быстро отличить вывод журнала от разных Worker экземпляров. Я ценю ваши предложения и вклад.

1 Ответ

2 голосов
/ 18 июля 2019

Ваша проблема в том, что console является одноэлементным объектом. Каждый вызов addUuidToConsole оборачивает предыдущую версию функции журнала / предупреждения / ошибки в [еще одну] функцию, которая добавляет UUID.

То, что вы хотите, - это отдельный объект журнала для каждого из ваших работников, ни один из которых фактически не изменяет глобальный объект console. Вместо этого каждый объект должен предоставлять консольный API, который изменяет любые передаваемые ему аргументы по мере необходимости, прежде чем перенаправлять их в соответствующий метод console. Как оказалось, это отличный пример использования для класса Proxy.

Например:

/**
 * Function to create a console-like object that prefixes
 * any (well, okay, most) output with "[${tag}]".
 *
 * This will behave identical to the built-in `console`, except
 * where provide custom wrapper functions, below.
 *
 * @param {String} prefix string
 */
function taggedConsole(tag) {
  // Cache of wrapper functions
  const wraps = {};

  // Return a Proxy for the console object
  return new Proxy(console, {
    // Trap for when `console[prop]` is referenced
    get(target, prop) {
      // If we've already created a wrapper function, return it
      if (wraps[prop]) return wraps[prop];

      // Functions we wrap (to inject `[tag]` argument)
      switch (prop) {
        // Create a wrapper to insert `tag` as the first arg
        case "debug":
        case "log":
        case "info":
        case "warn":
        case "error":
          wraps[prop] = function(...args) {
            return console[prop](`[${tag}]`, ...args);
          };
          break;

        // Create a wrapper to prefix the first arg with `tag`
        case "count":
        case "countReset":
        case "group":    // Note: Takes a label arg, but 
        case "groupEnd": // groupEnd ignores label arg so... :-/
        case "time":
        case "timeEnd":
        case "timeLog":
        case "trace":
          wraps[prop] = function(...args) {
            args[0] = `[${tag}] ${args[0]}`;
            return console[prop](...args);
          };
          break;
      }

      // Return wrapper if defined, otherwise return the original function
      return wraps[prop] || target[prop];
    }
  });
}

// FOR EXAMPLE ...

// Create a couple example consoles
consoleFoo = taggedConsole("FOO");
consoleBar = taggedConsole("BAR");

// Log stuff (output prefixed with the tag)
consoleFoo.log("hello");
consoleBar.log("world");

// Other functions that provide tag as prefix
consoleFoo.time("fdsafd");
consoleFoo.timeEnd("fdsafd");
consoleFoo.trace("fdsafd");
...