Ведение журнала Winston с помощью Mongoose Docs

Недавно я только что переключился на Winston для ведения журнала и заметил проблему при регистрации документов мангуста после выполнения команды exec.

Пример:

Model.find().exec(function (err, docs) {
    console.log(docs) // Prints the collection fine
    winston.info(docs) // Prints a ton on mongoose stuff, related to the query
});

Итак, как мне заставить ведение журнала Winston печатать так же, как вы получаете из console.log? Я предполагаю, что это должно быть сериализовано перед регистрацией с помощью вызова toJSON().

Должен ли я каждый раз вручную вызывать .toJSON() или люди сделали что-то еще, чтобы это работало автоматически?


person iQ.    schedule 10.09.2014    source источник
comment
У меня такая же проблема и пока не нашел решения. вы решили это наконец?   -  person Jehof    schedule 16.10.2014
comment
к сожалению, нет, но я поиграю с некоторыми из предложенных ответов ниже. Я предполагаю, что winston никогда не предназначался для регистрации красивых объектов.   -  person iQ.    schedule 18.10.2014


Ответы (3)


Предупреждение

Я думаю, что предполагаемое использование winston состоит в том, чтобы в первую очередь регистрировать строковые сообщения и (при необходимости) дополнительную метаинформацию. Более того, я не совсем понимаю, почему вы хотите регистрировать всю коллекцию, возвращенную из монго, а не, скажем, только _ids (при условии, что docs может быть довольно большим).

вступление

Я просмотрел исходный код winston и вот соответствующие части:

winston/logger.js

Logger.prototype.log = function (level) {
  var self = this,
      args = Array.prototype.slice.call(arguments, 1);

  ...

  var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null,
      meta     = typeof args[args.length - 1] === 'object' ? args.pop() : {},
      msg      = util.format.apply(null, args);

  ...

}

По сути, один аргумент типа object интерпретируется как мета. Консольный транспортный уровень (по умолчанию) в основном определяется в winston/common.js, и вот как обрабатывается мета:

 ... if (Object.keys(meta).length > 0) {
      output += ' ' + (
        options.prettyPrint
          ? ('\n' + util.inspect(meta, false, null, options.colorize))
          : exports.serialize(meta)
      );
    }

Метод serialize перебирает (рекурсивно) все ключи объекта для формирования конечной строки (вместо вызова .toString или аналогичного).

Предлагаемые решения

Оба решения вынуждают winston интерпретировать единственный аргумент объекта не как метаданные, а как строку сообщения.

Если вы хотите поддерживать разные транспортные уровни, их необходимо изменить.

Изменить исходный код winston

Просто разветвите репозиторий и внесите соответствующие изменения в исходный код. Существует множество способов добиться этого. Одним из уродливых может быть:

 meta     = args.length === 1 ? {} :
          (typeof args[args.length - 1] === 'object' ? args.pop() : {}),

Но гораздо лучше было бы добавить особый случай в метод .serialize, сделать специальную обработку, если объект является моделью мангуса, очень наивной и неправильной:

 else if ('_doc' in obj && 'save' in obj){
        var result = [];
        msg += '{'
        for(var key in obj['_doc']){
            result.push (key + ':' + obj['_doc'][key]);
        }
        msg += result.join(', ');
        msg += '}';
    }

(К сожалению, с этим подходом есть проблема, так как winston копирует метаданные, и все методы, определенные выше в цепочке прототипов, теряются — в противном случае это было бы так же просто, как вызвать obj.toJSON, и наверняка это было бы самое элегантное и надежное решение)

Переопределить поведение winston по умолчанию

var original = winston.log;
winston.log = function(){
    if(arguments.length === 2){
        original.call(winston, arguments[0], arguments[1], {});
    }
    else {
        original.apply(winston, arguments);
    }
}

Объяснение: arguments[0] определяет уровень, поэтому arguments[1] является фактическим объектом для регистрации.

person artur grzesiak    schedule 17.10.2014
comment
Я попробую этот подход. Дело в том, что в целом он не регистрирует объекты, а также console.log. Также в некоторых случаях я хотел бы выводить, даже если это просто для отладки, чтобы распечатать один объект из моей коллекции документов, которая у меня есть пока прибегнуть к console.log. - person iQ.; 18.10.2014
comment
спасибо за ваш подробный ответ. Я ожидал, что winston работает как console.log. - person Jehof; 23.10.2014
comment
@Jehof рад, что это помогло. - person artur grzesiak; 24.10.2014

Я объединил идеи из предыдущих ответов, чтобы предоставить довольно надежный метод выхода из мета-объектов, который я использовал в течение многих месяцев без каких-либо проблем.

Общая идея состоит в том, чтобы переопределить transport.log и преобразовать метаобъект в строку JSON и обратно. Это гарантирует, что мета-объект сохраняется как объект, и, следовательно, можно использовать преимущества winston для мета-объектов, такие как prettyPrint.

Вот код для создания нового регистратора с параметром prettyPrint:

var transport = new (winston.transports.Console)({
  prettyPrint: function(meta) {
    return util.format('%j', meta);
  }
});

var originalLog = transport.log;
transport.log = function(level, msg, meta, callback) {
  if(meta) {
    if(meta instanceof Error) {
      // Errors cannot be stringified.
      meta = meta.toString();

    } else {
      // Keep meta as an object so that winston formats it
      // nicely, but ensure we don't have any weird ObjectIds
      meta = JSON.parse(JSON.stringify(meta));
    }
  }
  return originalLog.call(transport, level, msg, meta, callback);
};

var logger = new (winston.Logger)({
  transports: [transport]
});

Теперь вы можете использовать регистратор следующим образом:

logger.debug('My Mongoose document:', doc);

Что выведет что-то вроде:

debug: My Mongoose document: {"_id":"56f0598b130b3cfb16d76b3d","name":"Bob"}

person Tom Spencer    schedule 25.03.2016

Простым решением было бы преобразовать объект модели Mongoose в строку JSON и передать ее функции winston. Для массива вам может потребоваться вызвать функцию в цикле.

winston.info(JSON.stringify(doc));
person Arpit    schedule 28.01.2016