2010-07-27 9 views
17

ha appena iniziato a utilizzare log4j in uno dei miei progetti di casa e stavo proprio per rompere il mouse e tagliare e incollare (trace (str "entering: " function-name)) in ogni funzione in un modulo di grandi dimensioni. poi la voce della ragione è arrivata e ha detto "semplicemente deve esserci un modo migliore" ... Posso pensare di creare una macro che avvolge un intero blocco di funzioni e aggiunge le tracce a loro o qualcosa del genere? Qualche consiglio dai saggi clojuriani stracolmi di pila?clojure: aggiunta una traccia di debug ad ogni funzione in un namespace?

risposta

17

Non c'è bisogno di una macro:

(defn trace-ns 
    "ns should be a namespace object or a symbol." 
    [ns] 
    (doseq [s (keys (ns-interns ns)) 
      :let [v (ns-resolve ns s)] 
      :when (and (ifn? @v) (-> v meta :macro not))] 
    (intern ns 
      (with-meta s {:traced true :untraced @v}) 
      (let [f @v] (fn [& args] 
          (clojure.contrib.trace/trace (str "entering: " s)) 
          (apply f args)))))) 

(defn untrace-ns [ns] 
    (doseq [s (keys (ns-interns ns)) 
      :let [v (ns-resolve ns s)] 
      :when (:traced (meta v))] 
    (alter-meta! (intern ns s (:untraced (meta v))) 
       #(dissoc % :traced :untraced)))) 

... o qualcosa di simile. Il requisito aggiuntivo più probabile sarebbe quello di utilizzare filter in modo da non chiamare trace su cose che non sono ifn? s. Aggiornamento: modificato in una soluzione (anche gestendo macro). Aggiornamento 2: corretto alcuni bug importanti. Aggiornamento 4: aggiunta funzionalità di annullamento.

Update 3: Ecco un esempio dalla mia REPL:

user> (ns foo) 
nil 
foo> (defn foo [x] x) 
#'foo/foo 
foo> (defmacro bar [x] x) 
#'foo/bar 
foo> (ns user) 
nil 
user> (trace-ns 'foo) 
nil 
user> (foo/foo :foo) 
TRACE: "entering: foo" 
:foo 
user> (foo/bar :foo) 
:foo 
user> (untrace-ns 'foo) 
nil 
user> (foo/foo :foo) 
:foo 
+0

Sei semplicemente impressionante! potresti spiegare la parte di risoluzione dei problemi? –

+0

:-) 'ns-resolve' prende uno spazio dei nomi o un nome di namespace e un simbolo e tenta di trovare un Var internato nello spazio dei nomi specificato sotto il nome dato dal (secondo) simbolo. Il punto importante qui è che (1) è possibile cambiare il binding della radice del Var (c'è un certo numero di modi per farlo, ma 'intern' è qui particolarmente conveniente a causa del modo in cui gestisce anche i metadati extra;' trace-ns' usa il rebinding per sostituire una funzione avvolta per l'originale) e (2) è possibile cambiare i metadati del Var (è possibile che 'non-ns 'sia possibile). –

+0

Vedere http://gist.github.com/492764 per la versione più recente di sopra (con una docstring più utile!). –

Problemi correlati