Слушайте все выпущенные события в Node.js

В Node.js есть ли способ прослушивать все события, испущенные объектом EventEmitter?

например, вы можете сделать что-то вроде...

event_emitter.on('',function(event[, arg1][, arg2]...) {}

Идея состоит в том, что я хочу захватить все события, которые выплевываются на стороне сервера EventEmitter, JSON.stringify данные о событиях, отправлять их через соединение с веб-серверами, реорганизовывать их на стороне клиента как событие и затем действовать на событие на стороне клиента.

Ответ 1

Как уже упоминалось, это поведение не находится в ядре node.js. Но вы можете использовать hij1nx EventEmitter2:

https://github.com/hij1nx/EventEmitter2

Он не разрушит существующий код с помощью EventEmitter, но добавит поддержку пространств имен и подстановочных знаков. Например:

server.on('foo.*', function(value1, value2) {
  console.log(this.event, value1, value2);
});

Ответ 2

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

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

function patchEmitter(emitter, websocket) {
  var oldEmit = emitter.emit;

  emitter.emit = function() {
      var emitArgs = arguments;
      // serialize arguments in some way.
      ...
      // send them through the websocket received as a parameter
      ...
      oldEmit.apply(emitter, arguments);
  }
}

Это довольно простой код и должен работать на любом эмиттере.

Ответ 3

С классами ES6 это очень просто:

class Emitter extends require('events') {
    emit(type, ...args) {
        console.log(e + " emitted")
        super.emit(type, ...args)
    }
}

Ответ 4

Имейте в виду, что все описанные выше решения, которые могут работать, будут включать в себя некоторый вид взлома внутренней реализации Event.mitter для node.js.

Правильный ответ на этот вопрос будет следующим: реализация EventEmitter по умолчанию не поддерживает это, вам нужно взломать его.

Если вы посмотрите на исходный код node.js для EventEmitter, то увидите, что когда к определенному типу события не подключен прослушиватель, он просто возвращается без каких-либо дополнительных действий, поскольку пытается извлечь функцию обратного вызова из хеша. в зависимости от типа события:

https://github.com/nodejs/node/blob/98819dfa5853d7c8355d70aa1aa7783677c391e5/lib/events.js#L176-L179

Вот почему что-то вроде eventEmitter.on('*',()=>...) не может работать по умолчанию.

Ответ 5

Атрибут _events, по-видимому, зависит от слушателей, которые определены на объекте, поэтому он не выполняет то, что задает вопрос. Другими словами, если определить слушателя e.on( "foo" ,...), то "foo" появляется в e._events, даже если e никогда не испускает "foo" . С другой стороны, e может испускать "бар", который, если его не прослушать, не будет отображаться в e._events.

Для отладки, в частности, было бы неплохо иметь такую ​​ "подстановочную" возможность, слушатель формы e.on( "*",...), но эта функция, похоже, недоступна.

Ответ 6

Это основано на ответе, приведенном Мартином выше. Я немного новичок в node, поэтому мне нужно было решить свой ответ для себя. Метод в конце, logAllEmitterEvents - важный бит.

var events = require('events');
var hungryAnimalEventEmitter = new events.EventEmitter();

function emitHungryAnimalEvents()
{
    hungryAnimalEventEmitter.emit("HungryCat");
    hungryAnimalEventEmitter.emit("HungryDog");
    hungryAnimalEventEmitter.emit("Fed");
}

var meow = function meow()
{
  console.log('meow meow meow');
}

hungryAnimalEventEmitter.on('HungryCat', meow);

logAllEmitterEvents(hungryAnimalEventEmitter);

emitHungryAnimalEvents();

function logAllEmitterEvents(eventEmitter)
{
    var emitToLog = eventEmitter.emit;

    eventEmitter.emit = function () {
        var event = arguments[0];
        console.log("event emitted: " + event);
        emitToLog.apply(eventEmitter, arguments);
    }
}

Ответ 7

Мне нужно было отслеживать все испускаемые события во всех библиотеках, поэтому я подключился к prototype.

В этом примере используется Typescript signature, но вы можете просто удалить ее, если вы не в такой ерунде.

В рамках вызова this относится к объекту, который излучает. Было очень легко отследить весь уникальный объект: выбрасывает в мой проект.

  // For my example I use a 'set' to track unique emits.
  const items = new Set()

  const originalEmit = EventEmitter.prototype.emit;
  EventEmitter.prototype.emit = function (event: String | Symbol, ...args: any[]): boolean {

    // Do what you want here
    const id = this.constructor.name + ":" + event;
    if (!items.has(id)) {
      items.add(id);
      console.log(id);
    }

    // And then call the original
    return originalEmit.call(event, ...args);
  }

Вы можете очень легко расширить это и фильтровать на основе имени события или имени класса.

Ответ 8

Возможно, вы захотите изучить модули RPC для node.js. Если я не ошибаюсь, модуль Dnode RPC имеет чат-сервер/клиентский пример, аналогичный тому, что вы пытаетесь сделать. Таким образом, вы можете либо использовать свой модуль, либо скопировать то, что они делают.

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

var evNames = [ 'joined', 'said', 'parted' ];

con.on('ready', function () {
    evNames.forEach(function (name) {
        emitter.on(name, client[name]);
    });
    emitter.emit('joined', client.name);
});

Этот код является умным, поскольку он автоматически вызывает удаленный вызов процедуры на клиенте, связанном с событием, когда событие выдается.

Ответ 9

Столкнулся с той же проблемой сегодня, вот решение:

Object.create(Object.assign({},EventEmitter.prototype, {
  _onAnyListeners:[],
  emit:function(...args){
    //Emit event on every other server

    if(this._fireOnAny && typeof this._fireOnAny === 'function'){
      this._fireOnAny.apply(this,args)
    }

    EventEmitter.prototype.emit.apply(this,args)
  },
  _fireOnAny:function(...args){
    this._onAnyListeners.forEach((listener)=>listener.apply(this,args))
  },
  onAny:function(func){
    if(typeof func !== 'function'){
      throw new Error('Invalid type');
    }
    this._onAnyListeners.push(func);
  },
  removeOnAny:function(func){
    const index = this._onAnyListeners.indexOf(func);
    if(index === -1){
      return;
    }
    this._onAnyListeners.splice(index,1);
  }
}));

Ответ 10

Начиная с Node.js v6.0.0, полностью поддерживается новый синтаксис class и оператор распространения аргументов, поэтому довольно безопасно и довольно легко реализовать желаемую функциональность с помощью простого наследования и переопределения метода:

'use strict';
var EventEmitter = require('events');

class MyEmitter extends EventEmitter {
  emit(type, ...args) {
    super.emit('*', ...args);
    return super.emit(type, ...args) || super.emit('', ...args);
  }
}

Эта реализация основывается на том факте, что оригинальный emit метод EventEmitter возвращает true/false в зависимости, если событие было обработано каким - то слушателю или нет. Обратите внимание, что переопределение включает инструкцию return, поэтому мы сохраняем это поведение для других потребителей.

Здесь идея состоит в том, чтобы использовать событие звезды (*) для создания обработчиков, которые выполняются для каждого отдельного события (скажем, для целей ведения журнала), и пустого события ('') для значения по умолчанию или перехватывать весь обработчик, который выполняется, если ничего не происходит. еще ловит это событие.

Мы обязательно сначала вызываем событие star (*), потому что в случае событий error без каких-либо обработчиков результат фактически является генерируемым исключением. Для более подробной информации взгляните на реализацию EventEmitter.

Например:

var emitter = new MyEmitter();

emitter.on('foo', () => console.log('foo event triggered'));
emitter.on('*', () => console.log('star event triggered'));
emitter.on('', () => console.log('catch all event triggered'));

emitter.emit('foo');
    // Prints:
    //   star event triggered
    //   foo event triggered

emitter.emit('bar');
    // Prints:
    //   star event triggered
    //   catch all event triggered

Наконец, если экземпляр EventEmitter уже существует, но вы хотите настроить этот конкретный экземпляр на новое поведение, это можно легко сделать, исправив метод во время выполнения следующим образом:

emitter.emit = MyEmitter.prototype.emit;

Ответ 11

патч обезьяны добавить метод onAny в EventEmitter.

полезно иметь возможность отслеживать только события одной проблемы.

var EventEmitter=require('events')
var origemit=EventEmitter.prototype.emit;
Object.assign( EventEmitter.prototype, {
  emit:function(){
    if(this._onAnyListeners){
        this._onAnyListeners.forEach((listener)=>listener.apply(this,arguments))
    }
    return origemit.apply(this,arguments)
  },
  onAny:function(func){
    if(typeof func !== 'function'){
      throw new Error('Invalid type');
    }
    if(!this._onAnyListeners)this._onAnyListeners=[];
    this._onAnyListeners.push(func);
  },
  removeOnAny:function(func){
    const index = this._onAnyListeners.indexOf(func);
    if(index === -1){
      return;
    }
    this._onAnyListeners.splice(index,1);
  }
});
// usage example
//gzip.onAny(function(a){console.log(a)})

Ответ 12

Вы также можете использовать другую реализацию эмиттера событий, например https://github.com/ozantunca/DispatcherJS. Реализация будет выглядеть так:

dispatcher.on('*', function () {});

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

Ответ 13

Здесь инструмент отладки, вдохновленный Мартином, отвечает (fooobar.com/info/114016/...). Я только что использовал это, чтобы выяснить, что происходит в наборе потоков, записав все их события на консоль. Прекрасно работает. Как показывает Мартин, OP может использовать его, заменив вызов console.log() на отправителя websocket.

function debug_emitter(emitter, name) {
    var orig_emit = emitter.emit;
    emitter.emit = function() {
        var emitArgs = arguments;
        console.log("emitter " + name + " " + util.inspect(emitArgs));
        orig_emit.apply(emitter, arguments);
    }
}