Fork me on GitHub
#clojure-germany
<
2021-05-29
>
javahippie11:05:19

Moin! Im letzten Call hatten wir ja kurz über Möglichkeiten gesprochen, ein Clojure Programm um Auto-Tracing zu erweitern. @ramart hatte alter-var-root ins Spiel gebracht, und ich hatte heute mal ein wenig Zeit und Muße um damit rumzuspielen. Seht ihr hier noch Verbesserungspotenzial?

javahippie11:05:45

find-traced-fn gibt alle Funktionen in allen Namespaces zurück, die {:traced true} als Metainformation beinhalten. Mit wrap-fn wird die übergebene Funktion in der Trace-Logik gewrappt, hier könnte man dann z.B. auch Seiteneffekte in Richtung des Tracing-Anbieters auslösen.

javahippie11:05:00

Das Hauptproblem was ich damit immer noch nicht gelöst habe: Übergreifende Spans bei verschachtelten Calls

RAMart13:05:23

Wie würden die denn erkannt? Vor der Berechnung des Results eine Art „Begin Span Event“ und danach ein End? Jeweils mit ID?

javahippie13:05:13

Du gibst den event einen Parent Trace mit, und der identifiziert dann, was hierarchisch zusammengehört.

javahippie13:05:40

Habe jetzt noch ein bisschen mit with-meta herumgespielt und mit optionalen Parametern am Ende der Parameterliste. Beide werden nicht 1:1 durchgehoben, da muss dann glaube ich manueller Aufwand hinzukommen.

RAMart13:05:55

Es gibt kein Problem. was nicht durch eine weitere Abstraktion gelöst werden kann. „Doppelt wrappen mit UUIDs“ schießt mir spontan durch den Kopf. Bin aber gerade unterwegs…

javahippie13:05:09

Ich werd jetzt auch erst mal das Wetter genießen und noch ein bisschen damit rumspielen 😉 Danke schon mal, auch für den ursprünglichen Vorschlag!

Björn Ebbinghaus13:05:28

Kennt ihr https://github.com/gnl/ghostwheel ? Neben einer schöneren Art Funktionen zu specen kann das auch tracing und side-effect detection.

RAMart15:05:55

@mroerni Danke für den Tipp, schaue ich mir mal an! Aber ein bisschen selbst rumschrauben macht ja auch Spaß. 🙃 @javahippie ein kleiner PoC (basierend auf Deiner Vorarbeit):

(def trace-log (partial println "root"))

(defn trace-fn
  [function]
  (alter-var-root
   function
   (fn [f]
     (fn [& n]
       (with-redefs [trace-log (partial println (java.util.UUID/randomUUID))]
         (let [start (System/currentTimeMillis)
               result (apply f n)
               duration (-(System/currentTimeMillis) start)]
           (trace-log (format "This took %s millis" duration))
           result))))))

(defn find-traced-fn []
  (->>
   (all-ns)
   (mapcat ns-publics)
   (vals)
   (filter #(:traced (meta %)))))

(defn innermost {:traced true}
  [a b]
  (trace-log "innermost")
  (+ a b))

(defn inner {:traced true}
  [arg]
  (trace-log "inner")
  (innermost 47 11))

(defn outer {:traced true}
  [arg]
  (trace-log "outer")
  [(inner arg)
   (inner arg)])

;; (map trace-fn (find-traced-fn))

(outer :ignored)

javahippie07:05:34

Sehr cool! Ich muss jetzt erst mal bei nem Kaffee durchsteigen und ein bisschen Doku nachlesen, vielen Dank schon mal

henrik4208:05:07

Moin. Wie geht die Geschichte in der REPL weiter? Wenn man die fn neu def't muss ich sie erneut var-rooten - oder? Aber man könnte ein defnT Macro zufügen, das erkennt, das die Funktion ge-trace-t ist und dann die neue Funktion auch automatisch tracen. Macht das Sinn?

javahippie08:05:19

Mit der REPL hast du Recht. Ich würde das Tracing hauptsächlich benutzen um laufende Umgebungen zu überwachen und Performance-Verläufe zu sehen, in der REPL fände ich es nicht schlimm, wenn das Tracing aus wäre.

henrik4208:05:56

Ah ok. Meinst du, per SocketREPL rein, dann mit {:traced true} redefn, dann altern? Am Ende würde man dann vielleicht gerne die ungetracte Funktion wieder einstellen? Entweder durch ein erneutes defn oder durch eine Funktion, die das wieder für den ganzen Namespace macht. Vgl. https://github.com/technomancy/robert-hooke/blob/master/src/robert/hooke.clj#L116

javahippie11:05:49

Ich würde das Tracing eher per Konfig steuern, mit der REPL Funktionen in Produktion redefn ist mir immer noch ein bisschen zu gruselig 😄

beders18:05:05

Lustigerweise arbeite ich auch an einer tracing Lösung, mit Fokus auf der Verarbeitung von Traces für "später" (TM).

beders18:05:46

Traces sind versioniert (auf Wunsch) und lassen sich z.B. in RocksDB speichern und sind vergleichbar usw. Auch laesst sich einstellen wie viel traces man denn gerne im Speicher mitschleppen wuerde. Die Idee ist dass ich etwa alle mein hug-sql Funktionen tracen lassen und mir dann immer mal reinschauen kann. Traces sind navigierbar mit datafy und lassen sich so etwa in Reveal anschauen und navigieren.

beders18:05:04

Und man kann z.B. einstellen, dass man nach 5 Traces einer fn dieselbe wieder in den Ursprungszustand versetzt

RAMart19:05:43

Interessant! Worauf bezieht sich die Versionierung eines Traces? :thinking_face:

beders18:05:00

Einstellbar. ZB: git commit ID oder normale versions Nummer oder hash der Funktion. Bei letzterem muss ich noch ein bisschen forschen.