web-dev-qa-db-fra.com

Écoutez tous les événements émis dans Node.js

Dans Node.js, existe-t-il un moyen d'écouter les événements tous émis par un objet EventEmitter?

par exemple, pouvez-vous faire quelque chose comme ...

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

L'idée est de saisir tous les événements générés par un serveur EventEmitter, JSON.stringify les données d'événement, de les envoyer via une connexion websockets, de les reformer côté client en tant qu'événement, puis d'agir sur l'événement le côté client.

60
Chris W.

Comme mentionné, ce comportement n’est pas dans le noyau node.js. Mais vous pouvez utiliser EventEmitter2 de hij1nx:

https://github.com/hij1nx/EventEmitter2

Cela ne cassera pas le code existant en utilisant EventEmitter, mais ajoutera un support pour les espaces de noms et les caractères génériques. Par exemple:

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

Je sais que c'est un peu vieux, mais bon sang, voici une autre solution que vous pourriez prendre.

Vous pouvez facilement patcher la fonction d’émission de l’émetteur pour lequel vous souhaitez intercepter tous les événements:

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);
  }
}

C'est un code assez simple et devrait fonctionner sur n'importe quel émetteur.

64
Martin

Avec les cours ES6, c'est très simple:

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

Sachez que toutes les solutions décrites ci-dessus qui pourraient fonctionner impliqueront une sorte de piratage pour la mise en œuvre interne de node.js EventEmitter.

La bonne réponse à cette question serait: l'implémentation par défaut de EventEmitter ne supporte pas cela, vous devez la modifier.

Si vous examinez le code source de node.js pour EventEmitter, vous verrez que lorsqu'aucun écouteur n'est attaché à un type d'événement spécifique, il sera simplement renvoyé sans autre action, car il tente de récupérer la fonction de rappel à partir d'un hachage. en fonction du type d'événement: 

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

C'est pourquoi quelque chose comme eventEmitter.on('*', ()=>...) ne peut pas fonctionner par défaut.

4
David Rissato Cruz

Je devais retracer tous les événements émis dans toutes les bibliothèques, alors j'ai exploité la variable prototype.

Cet exemple utilise un TypeScript signature, mais vous pouvez simplement le supprimer si vous n'êtes pas dans ce genre de bêtises.

Dans l'appel, this fait référence à l'objet qui émet. Il était très facile de suivre tout objet unique: émet dans mon projet.

  // 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);
  }

Vous pouvez très facilement étendre cela et filtrer en fonction du nom de l'événement ou du nom de la classe.

1
Steven Spungin

Ceci est basé sur la réponse fournie par Martin ci-dessus. Je suis un peu nouveau sur le noeud, donc je devais élaborer sa réponse pour moi-même. La méthode à la fin, logAllEmitterEvents est le bit important.

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);
    }
}
1
Bill Heitzeg

A couru dans le même problème aujourd'hui, voici une solution:

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);
  }
}));
0
Eladian

Vous voudrez peut-être examiner les modules RPC pour node.js. Si je ne me trompe pas, le module Dnode RPC a un exemple de serveur/client de discussion similaire à ce que vous essayez de faire. Vous pouvez donc utiliser leur module ou copier ce qu’ils font. 

En résumé, l'exemple montre un serveur qui, lors de la connexion, crée des écouteurs pour tous les événements de serveur du client connecté. Pour ce faire, il suffit de parcourir une liste de noms d’événements stockée.

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

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

Ce code est intelligent car il appelle automatiquement un appel de procédure à distance sur le client associé à l'événement lors de son émission.

0
Nixuz

Depuis Node.js v6.0.0, les nouveaux opérateurs de propagation de syntaxe et d'argument class sont entièrement pris en charge. Il est donc assez sûr et simple d'implémenter les fonctionnalités souhaitées avec un simple héritage et un remplacement de méthode:

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

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

Cette implémentation repose sur le fait que la méthode emit originale de EventEmitter renvoie true/false selon que l'événement a été géré par un écouteur ou non. Notez que le remplacement inclut une instruction return. Nous conservons donc ce comportement pour les autres consommateurs.

Ici, l’idée est d’utiliser l’événement star (*) pour créer des gestionnaires exécutés pour chaque événement (par exemple, à des fins de journalisation) et l’événement vide ('') pour un gestionnaire par défaut ou intercepter tout, qui s’exécute sans autre tâche. attrape cet événement.

Nous nous assurons d'appeler d'abord l'événement star (*), car dans le cas d'événements error sans aucun gestionnaire, le résultat est en réalité une exception générée. Pour plus de détails, jetez un coup d'œil à la mise en oeuvre de EventEmitter .

Par exemple:

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

Enfin, si une instance EventEmitter existe déjà mais que vous souhaitez ajuster cette instance spécifique au nouveau comportement, vous pouvez le faire facilement en appliquant un correctif à la méthode au moment de l'exécution, comme suit:

emitter.emit = MyEmitter.prototype.emit;
0
Victor Schröder

un patch de singe ajoute une méthode à EventEmitter.

il est utile de ne pouvoir surveiller que les événements d'un problème.

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)})
0
Shimon Doodkin