2012-08-15 2 views
5

Au lieu de devoir créer un enregistreur personnalisé pour TRACE, par ex. Après quelles méthodes ont été appelées et quelles classes ont été instanciées, y a-t-il un moyen facile de faire toutes les méthodes sous un journal de classe? C'est pour une application node.js.journal chaque méthode?

class MyClass 

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

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

exports.MyClass = MyClass 

myClass = new MyClass() 
myClass.doThat() 

Si je devais mon chemin, vous verriez messages du journal au lieu de 2 (ainsi avoir à écrire moins de code pour tracer ce qui se passe).

Répondre

3

J'ai récemment eu besoin d'implémenter quelque chose comme ça pour tracer des trucs OO-récursifs compliqués. Fondamentalement, je voulais rendre une méthode "traçable" sans trop la polluer; alors peut-être que la solution pourrait être appliquée ici aussi.

D'abord, ajouter une fonction qui fait d'autres fonctions traçable:

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 

Ensuite, pour l'utiliser, il suffit d'ajouter un @trace avant chaque méthode que vous voulez tracer:

class MyClass 
    @trace methodA: -> 
    @methodB 42 

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

Ou ajouter @ Tracez une seule fois puis indentez toutes les méthodes traçables à un niveau de plus:

class MyClass 
    @trace 
    methodA: -> 
     @methodB 42 

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

Comme vous pouvez le voir, trace abuse un peu de la syntaxe de CoffeeScript. method: -> 'foo' à l'intérieur d'un class MyClass est interprétée une définition de méthode. Mais @trace method: -> 'foo' est interprété comme appelant la fonction trace de MyClass (qui est une instance Function, à laquelle nous avons ajouté la fonction trace) en lui passant un objet littéral avec une clé method. En JavaScript ce serait quelque chose comme this.trace({method: function() {return 'foo';}}). La fonction trace prendra juste cet objet et itérera ses clés (les noms de méthodes) et les valeurs (les méthodes) et ajoutera des fonctions au prototype MyClass qui enregistre leurs appels et appelle les méthodes originales.

Quoi qu'il en soit, la sortie de (new MyClass).methodA() sera:

MyClass:methodA 
MyClass:methodB 
method b called with 42 

Cette solution ne fonctionne pas pour les constructeurs mais, comme ils ne sont pas seulement des méthodes normales.

Vous pouvez obtenir assez chic avec ceci. Vous pouvez également consigner les arguments passés à chaque méthode, la valeur de retour, et même ajouter une indentation pour les appels imbriqués si vous le souhaitez (les traces résultantes peuvent alors être très utiles si vous devez déboguer un problème complexe = D).


Mise à jour: comme un exemple plus intéressant, voici une mini-version de l'exemple de motif composite typique, des figures géométriques et des groupes de figures: http://jsfiddle.net/2YuE7/ avec une fonction de traçage plus intéressant. Toutes les figures comprennent la méthode move.Si nous avons ce chiffre composite et appelons move sur elle:

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 

La sortie de trace est:

(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]]] 
+0

Ceci est très cool. La capacité de consigner les arguments était aussi quelque chose à laquelle je pensais. Merci! –

+0

@FrankLoVecchio Cool. J'ai mis à jour la réponse avec un exemple qui consigne les paramètres, renvoie les valeurs et met les journaux en retrait en fonction du niveau de récursion = D – epidemian

+0

Cela va être une question StackOverflow très utile :) –

0

Si vous voulez faire quelque chose avec javascript ancienne plaine, j'ai créé un service auquel vous peut passer un objet qui retournera un objet avec la même API qui enregistre chaque méthode. Voir l'exemple complet avec le polyfill appliqué à http://jsfiddle.net/mcgraphix/pagkoLjb

L'idée de base est:

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   
Questions connexes