Hören Sie sich alle ausgestrahlten Ereignisse in Node.js an


76

Gibt es in Node.js eine Möglichkeit, alle von einem EventEmitter-Objekt ausgegebenen Ereignisse abzuhören?

zB kannst du so etwas machen wie ...

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

Die Idee ist, dass ich alle Ereignisse, die von einer Serverseite ausgespuckt wurden EventEmitter, JSON.stringifydie Ereignisdaten erfassen, über eine Websockets-Verbindung senden, sie auf der Clientseite als Ereignis reformieren und dann auf das Ereignis auf der Clientseite reagieren möchte .


Das Attribut _events scheint von den Listenern abzuhängen, die für das Objekt definiert sind, sodass es nicht das tut, was die Frage stellt. Mit anderen Worten, wenn man einen Listener e.on ("foo", ...) definiert, wird "foo" in e._events angezeigt, selbst wenn e niemals "foo" ausgibt. Auf der anderen Seite könnte e einen "Balken" ausgeben, der, wenn er nicht angehört wird, nicht in e._events angezeigt wird. Insbesondere für das Debuggen wäre es schön, eine solche "Platzhalter" -Funktion zu haben, einen Listener der Form e.on ("*", ...), aber diese Funktion scheint nicht verfügbar zu sein.
teu

Antworten:


41

Wie bereits erwähnt, befindet sich dieses Verhalten nicht im Kern von node.js. Sie können jedoch EventEmitter2 von hij1nx verwenden:

https://github.com/hij1nx/EventEmitter2

Mit EventEmitter wird kein vorhandener Code beschädigt, es werden jedoch Namespaces und Platzhalter unterstützt. Zum Beispiel:

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

12
Es gibt auch eine emitter.onAny(listener)Methode, die einen Listener hinzufügt, der ausgelöst wird, wenn ein Ereignis ausgegeben wird.
Salman von Abbas

4
Dies hilft nicht bei vorhandenen internen Verwendungen der im Ereignisemitter gebackenen node.js, nach denen der Fragesteller sucht. Ich denke, stackoverflow.com/a/18087021/455556 beantwortet die Frage perfekt
John Culviner

2
@SalmanPK Wie werden onAnyEreignisnamen übergeben? Weil ich es versucht habe obj.onAny(function() {console.log(arguments);});und nur Ereignisparameter ohne Namen bekommen habe ...
Tomáš Zato - Reinstate Monica

77

Ich weiß, das ist ein bisschen alt, aber was solls, hier ist eine andere Lösung, die Sie nehmen könnten.

Sie können die Emit-Funktion des Emitters, mit dem Sie alle Ereignisse erfassen möchten, ganz einfach mit einem Affen-Patch versehen:

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

Dies ist ein ziemlich einfacher Code, der auf jedem Emitter funktionieren sollte.


Verwendete diese Methode mit mail-listener2, um zu vermeiden, dass alle Ereignisse abonniert werden müssen, nur um sie zu protokollieren. Dies ist für meine kleine Aufgabe einfacher.
Yzorg


Ich habe diese Methode auch bevorzugt. Keine zusätzlichen Deps und funktioniert ziemlich gut für Protokollierungszwecke, wie @yzorg sagte. Vielen Dank.
Seiyria

1
schlagen vor, die Zeile oldEmit.apply zu ändern (Emitter, Argumente); oldEmit.apply (Emitter, Argumente) zurückgeben;
Ofigenn

19

Mit ES6-Klassen ist es sehr einfach:

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

3
Kann dies in einer vorhandenen Instanz von EventEmitter erfolgen, die von einer anderen Person erstellt wurde?
Evgeny

mit diesem Ansatz können Sie nicht, aber Sie können instance.prototype.emitFunktion patchen
pravdomil

8

Beachten Sie, dass alle oben beschriebenen Lösungen, die möglicherweise funktionieren, eine Art Hacking für die interne Implementierung von node.js EventEmitter beinhalten.

Die richtige Antwort auf diese Frage wäre: Die Standard-EventEmitter-Implementierung unterstützt dies nicht. Sie müssen sie umgehen .

Wenn Sie sich den Quellcode von node.js für EventEmitter ansehen, werden Sie feststellen, dass ein Listener, der keinem bestimmten Ereignistyp zugeordnet ist, ohne weitere Aktion zurückgegeben wird, da versucht wird, die Rückruffunktion aus einem Hash abzurufen basierend auf dem Ereignistyp:

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

Deshalb eventEmitter.on('*', ()=>...)kann so etwas nicht standardmäßig funktionieren.


5

Seit Node.js v6.0.0 wird der neue classSyntax- und Argumentverbreitungsoperator vollständig unterstützt, sodass es ziemlich sicher und ziemlich einfach ist, die gewünschte Funktionalität mit einfacher Vererbung und Methodenüberschreibung zu implementieren:

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

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

Diese Implementierung basiert auf der Tatsache, dass die ursprüngliche emitMethode der EventEmitterRückgabe true/ falseabhängig davon ist, ob das Ereignis von einem Listener behandelt wurde oder nicht. Beachten Sie, dass die Überschreibung eine returnAnweisung enthält, sodass wir dieses Verhalten für andere Verbraucher beibehalten.

Hier besteht die Idee darin, mit dem star event ( *) Handler zu erstellen, die für jedes einzelne Ereignis (z. B. zu Protokollierungszwecken) und für das leere Ereignis (z.'' ) für einen Standard- oder catch all-Handler, der ausgeführt wird, wenn nichts anderes dies abfängt Veranstaltung.

Wir stellen sicher, dass das *Ereignis star ( ) zuerst aufgerufen wird , da bei errorEreignissen ohne Handler das Ergebnis tatsächlich eine Ausnahme ist, die ausgelöst wird. Weitere Informationen finden Sie in der Implementierung desEventEmitter .

Zum Beispiel:

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

Wenn bereits eine EventEmitter-Instanz vorhanden ist, Sie diese bestimmte Instanz jedoch an das neue Verhalten anpassen möchten, können Sie dies einfach tun, indem Sie die Methode zur Laufzeit wie folgt patchen:

emitter.emit = MyEmitter.prototype.emit;

1

Dies basiert auf der Antwort, die Martin oben gegeben hat. Ich bin ein bisschen neu im Knoten, also musste ich seine Antwort für mich selbst herausfinden. Die Methode am Ende, logAllEmitterEvents, ist das wichtige Bit.

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

Ich musste alle ausgegebenen Ereignisse in allen Bibliotheken verfolgen, also tippte ich auf die prototype.

In diesem Beispiel wird a verwendet Typescript signature, aber Sie können es einfach entfernen, wenn Sie sich nicht für diese Art von Unsinn interessieren.

thisBezieht sich innerhalb des Aufrufs auf das Objekt, das emittiert. Es war sehr einfach, alle eindeutigen Objekte zu verfolgen: Emits in meinem Projekt.

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

Sie können dies sehr einfach erweitern und basierend auf dem Ereignisnamen oder Klassennamen filtern.


0

Möglicherweise möchten Sie RPC-Module für node.js untersuchen. Wenn ich mich nicht irre, verfügt das Dnode-RPC-Modul über ein Chat-Server / Client-Beispiel, das dem ähnelt, was Sie versuchen. Sie können also entweder ihr Modul verwenden oder kopieren, was sie tun.

In Kürze zeigt das Beispiel einen Server, der bei Verbindung Listener für alle Serverereignisse vom verbundenen Client erstellt. Dazu wird einfach eine gespeicherte Liste von Ereignisnamen durchlaufen.

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

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

Dieser Code ist clever, da er beim Auslösen des Ereignisses automatisch einen Remoteprozeduraufruf auf dem dem Ereignis zugeordneten Client aufruft.


0

Ist heute auf dasselbe Problem gestoßen, hier ist eine Lösung:

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

Wenn Sie den EventEmitter nicht selbst erstellen können, müssen Sie ihn direkt dem Prototyp zuweisen.
Eladian

0

Ein Affen-Patch fügt EventEmitter eine beliebige Methode hinzu.

Es ist nützlich, nur Ereignisse eines Problems überwachen zu können.

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

-1

Hier ist ein Debug-Tool, das von Martins Antwort inspiriert wurde ( https://stackoverflow.com/a/18087021/1264797 ). Ich habe dies gerade verwendet, um herauszufinden, was in einer Reihe von Streams falsch lief, indem ich alle Ereignisse auf der Konsole protokollierte. Funktioniert super. Wie Martin zeigt, könnte OP es verwenden, indem der Aufruf von console.log () durch einen Websocket-Absender ersetzt wird.

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

sieht aus wie Sie vermisstvar util = require('util');
Pavel P

-2

Sie können auch eine andere Event-Emitter-Implementierung wie https://github.com/ozantunca/DispatcherJS verwenden . Die Implementierung wäre wie folgt:

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

DispatcherJS unterstützt auch Namespaces und sogar Abhängigkeiten, um zu bestimmen, welche Rückrufe zuerst aufgerufen werden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.