5

不必为 TRACE 创建自定义记录器,例如遵循调用了哪些方法以及实例化了哪些类,是否有一种简单的方法可以让类下的所有方法都自己记录?这是针对 node.js 应用程序的。

class MyClass

  constructor: () ->
    console.log 'MyClass:constructor'

  doThat: () ->
    console.log 'MyClass:doThat'

exports.MyClass = MyClass

myClass = new MyClass()
myClass.doThat()

如果按照我的方式,您会看到4条日志消息而不是 2 条(因此必须编写更少的代码来跟踪正在发生的事情)。

4

2 回答 2

3

我最近需要实现这样的东西来跟踪一些复杂的面向对象递归的东西。基本上,我想让一种方法“可追溯”而不会造成太多污染;所以也许解决方案也可以在这里应用。

首先,添加一个使其他函数可追溯的函数:

Function::trace = do ->
  makeTracing = (ctorName, fnName, fn) ->
    (args...) ->
      console.log "#{ctorName}:#{fnName}"
      fn.apply @, args
  (arg) ->
    for own name, fn of arg 
      @prototype[name] = makeTracing @name, name, fn

然后,要使用它,只需@trace在要跟踪的每个方法之前添加一个:

class MyClass
  @trace methodA: ->
    @methodB 42

  @trace methodB: ->
    console.log "method b called with #{n}"

或者只添加一次@trace,然后将所有可跟踪的方法再缩进一层:

class MyClass
  @trace 
    methodA: ->
      @methodB 42

    methodB: (n) ->
      console.log "method b called with #{n}"

如您所见trace,这有点滥用 CoffeeScript 的语法。method: -> 'foo'a 内部class MyClass被解释为方法定义。但是@trace method: -> 'foo'被解释为调用trace函数MyClass(它是一个Function实例,我们已经向其中添加了trace函数)传递一个带有一个method键的文字对象。在 JavaScript 中,它类似于this.trace({method: function() {return 'foo';}}).

trace函数将只获取该对象并迭代它的键(方法名称)和值(方法),并将函数添加到MyClass记录其调用的原型中,然后调用原始方法。

无论如何,输出(new MyClass).methodA()将是:

MyClass:methodA
MyClass:methodB
method b called with 42

但是,此解决方案不适用于构造函数,因为它们不仅仅是普通方法。

你可以很喜欢这个。您还可以记录传递给每个方法的参数、返回值,如果您愿意,甚至可以为嵌套调用添加缩进(如果您需要调试复杂的问题 =D,生成的跟踪会非常有用)。


更新:作为一个更有趣的例子,这里有一个典型的复合图案例子的迷你版,几何图形和图形组:http: //jsfiddle.net/2YuE7/具有更有趣的跟踪功能。所有的数字都理解这个move方法。如果我们有这个复合图形并调用move它:

f = new Composite [
  new Rectangle 5, 10, 3, 4
  new Composite [
    new Circle 0, 0, 2
    new Circle 0, 0, 4
    new Circle 0, 0, 6
  ]
]

f.move 2, 3

跟踪输出为:

(Composite[Rectangle[5,10,3,4],Composite[Circle[0,0,2],Circle[0,0,4],Circle[0,0,6]]]).move(2, 3)
| (Rectangle[5,10,3,4]).move(2, 3)
| -> Rectangle[7,13,3,4]
| (Composite[Circle[0,0,2],Circle[0,0,4],Circle[0,0,6]]).move(2, 3)
| | (Circle[0,0,2]).move(2, 3)
| | -> Circle[2,3,2]
| | (Circle[0,0,4]).move(2, 3)
| | -> Circle[2,3,4]
| | (Circle[0,0,6]).move(2, 3)
| | -> Circle[2,3,6]
| -> Composite[Circle[2,3,2],Circle[2,3,4],Circle[2,3,6]]
-> Composite[Rectangle[7,13,3,4],Composite[Circle[2,3,2],Circle[2,3,4],Circle[2,3,6]]]
于 2012-08-15T17:46:23.570 回答
0

如果您想使用普通的旧 javascript 执行此操作,我创建了一个服务,您可以向该服务传递一个对象,该对象将返回一个对象,该对象具有记录每个方法的相同 API。请参阅http://jsfiddle.net/mcgraphix/pagkoLjb上应用的 polyfill 的完整示例

基本思想是:

var api = {
   notAMethod: "blah",
   foo: function() {
    console.log("in foo", arguments);
   },

   bar: function(arg) {
    this.foo(arg);
    return this.notAMethod;
   }
}; 

//wrap the api with a logging version
//to watch property changes and keep them in sync, you need the polyfill for Object.watch
//     from https://gist.github.com/eligrey/384583

var createLogger = function(api) {
     var loggingApi = {};

            for (var prop in api) {
                if (typeof api[prop] !== 'function') {
                   loggingApi[prop] = api[prop]; 
                   loggingApi.watch(prop, function(prop, oldVal, newVal){
                       api[prop] = newVal
                       return newVal;
                   });


                } else {

                    loggingApi[prop] = function() {
                        console.log(prop + "() called with args: ", arguments);
                        var returnVal = api[prop].apply(api, arguments);
                         console.log(prop + "() returned: " + returnVal);
                        return returnVal;
                    }
                }
            }
      return loggingApi;
 };
 api.foo('Shhhh... don\'t log me') //no logging
 createLogger(api).foo(); //with logging           
于 2014-12-12T16:31:54.143 回答