监听Node.js中的所有事件


76

在Node.js中,有什么方法可以监听EventEmitter对象发出的所有事件?

例如,你可以做...

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

我的想法是,我想获取服务器端发出的所有事件EventEmitterJSON.stringify事件数据,并通过websockets连接发送事件,在客户端将其重新构造为事件,然后在客户端对事件进行操作。


_events属性似乎取决于在对象上定义的侦听器,因此它不执行问题所要求的。换句话说,如果定义了侦听器e.on(“ foo”,...),则即使e从未实际发出“ foo”,“ foo”也会出现在e._events中。另一方面,e可能会发出“ bar”,如果不收听,它将不会出现在e._events中。特别是对于调试,最好具有这样的“通配符”功能,即e.on(“ *”,...)形式的侦听器,但是此功能似乎不可用。
teu11年

Answers:


41

如前所述,此行为不在node.js核心中。但是您可以使用hij1nx的EventEmitter2:

https://github.com/hij1nx/EventEmitter2

它不会使用EventEmitter破坏任何现有代码,但会增加对名称空间和通配符的支持。例如:

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

12
它还具有emitter.onAny(listener)添加侦听器的方法,该侦听器在发出任何事件时将被激发。
Salman von Abbas 2012年

4
这对事件发射器中烘焙的node.js的任何现有内部用法都无济于事,尽管这是问问者要寻找的。我认为stackoverflow.com/a/18087021/455556完美地回答了这个问题
John Culviner 2015年

2
@SalmanPK如何onAny传递事件名称?因为我想obj.onAny(function() {console.log(arguments);});,我只得到了事件参数没有名字...
托马什Zato -恢复莫妮卡

77

我知道这有点旧,但是该死,这是您可以采取的另一种解决方案。

您可以轻松地猴子想要捕获所有事件的发射器的emit函数:

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

这是非常简单的代码,可以在任何发射器上使用。


与mail-listener2一起使用此方法可以避免只订阅所有事件就可以记录它们,这对于我的小任务来说比较简单。
yzorg 2014年

1
子堆栈所使用。
timruffles 2014年

我也喜欢这种方法。如@yzorg所说,没有多余的深度并且可以很好地用于日志记录目的。谢谢。
Seiyria 2014年

1
建议更改行oldEmit.apply(emitter,arguments); 返回oldEmit.apply(发射器,参数);
Ofigenn '16

19

使用ES6类,非常简单:

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

3
是否可以在其他人创建的EventEmitter的现有实例中完成操作?
叶夫根尼(Evgeny)

使用这种方法,您不能,但是您可以修补instance.prototype.emit功能
pravdomil

8

请注意,上述所有可行的解决方案都将涉及对node.js EventEmitter内部实现的某种黑客攻击。

这个问题的正确答案是:默认的EventEmitter实现不支持该方法,您需要解决一下

如果您查看EventEmitter的node.js源代码,您会看到,当没有侦听器附加到特定事件类型时,它将返回而无需任何进一步的操作,因为它正在尝试从哈希中检索回调函数。根据事件类型:

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

这就是为什么类似的东西eventEmitter.on('*', ()=>...)默认情况下无法工作的原因。


5

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

此实现依赖于以下事实:return /的原始emit方法取决于事件是否由某些侦听器处理。注意,重写包含一条语句,因此我们将这种行为保留给其他使用者。EventEmittertruefalsereturn

这里的想法是使用star事件(*)创建在每个事件上执行的处理程序(例如,出于日志记录目的),而空事件('')创建默认的处理程序或捕获所有处理程序,如果没有其他处理程序捕获该处理程序,则执行该处理程序事件。

我们确保首先调用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;

1

这是基于马丁上面提供的答案。我对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);
    }
}

1

我需要跟踪所有库中所有发出的事件,因此我点击了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);
  }

您可以很容易地扩展它并根据事件名称或类名称进行过滤。


0

您可能需要研究node.js的RPC模块。如果我没有记错的话,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);
});

这段代码很聪明,因为它在发出事件时自动在与事件关联的客户端上调用远程过程调用。


0

今天遇到同样的问题,这里有一个解决方案:

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

如果您不能自己创建EventEmitter,则必须直接分配给原型。
Eladian

0

猴子补丁将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)})

-1

这是一个受马丁回答(https://stackoverflow.com/a/18087021/1264797)启发的调试工具。我现在刚刚使用它通过将所有事件记录到控制台中来找出一组流中出了什么问题。效果很好。如Martin所示,OP可以通过使用websocket发送器替换console.log()调用来使用它。

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

看起来你好像失踪了var util = require('util');
Pavel P

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.