clojure-germany

javahippie 2021-05-29T11:28:19.001800Z

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?

javahippie 2021-05-29T11:28:30.001900Z

javahippie 2021-05-29T11:30:45.003500Z

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.

javahippie 2021-05-29T11:56:00.003700Z

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

RAMart 2021-05-29T13:32:23.007300Z

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

javahippie 2021-05-29T13:33:13.007500Z

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

javahippie 2021-05-29T13:34:40.007700Z

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.

RAMart 2021-05-29T13:38:55.010800Z

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…

javahippie 2021-05-29T13:40:09.011Z

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 Ebbinghaus 2021-05-29T13:42:28.012600Z

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

RAMart 2021-05-29T15:48:55.014400Z

@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)

javahippie 2021-05-30T07:58:34.023200Z

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

henrik42 2021-05-30T08:26:07.023400Z

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?

javahippie 2021-05-30T08:30:19.023600Z

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.

henrik42 2021-05-30T08:45:56.023800Z

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

javahippie 2021-05-30T11:35:49.031700Z

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

beders 2021-05-29T18:32:05.015700Z

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

beders 2021-05-29T18:35:46.018400Z

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.

beders 2021-05-29T18:37:04.019700Z

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

beders 2021-05-30T18:13:00.034800Z

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

beders 2021-05-29T18:37:40.020100Z

WIP

RAMart 2021-05-29T19:27:43.021600Z

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