(ns re-frame.trace "Tracing for re-frame. Alpha quality, subject to change/break at any time." #?(:cljs (:require-macros [net.cgrand.macrovich :as macros] [re-frame.trace :refer [finish-trace with-trace merge-trace!]])) (:require [re-frame.interop :as interop] #?(:clj [net.cgrand.macrovich :as macros]) [re-frame.loggers :refer [console]])) (def id (atom 0)) (def ^:dynamic *current-trace* nil) (defn reset-tracing! [] (reset! id 0)) #?(:cljs (goog-define trace-enabled? false) :clj (def ^boolean trace-enabled? false)) (defn ^boolean is-trace-enabled? "See https://groups.google.com/d/msg/clojurescript/jk43kmYiMhA/IHglVr_TPdgJ for more details" [] trace-enabled?) (def trace-cbs (atom {})) (defn register-trace-cb "Registers a tracing callback function which will receive a collection of one or more traces. Will replace an existing callback function if it shares the same key." [key f] (swap! trace-cbs assoc key f)) (defn remove-trace-cb [key] (swap! trace-cbs dissoc key) nil) (defn next-id [] (swap! id inc)) (defn start-trace [{:keys [operation op-type tags child-of]}] {:id (next-id) :operation operation :op-type op-type :tags tags :child-of (or child-of (:id *current-trace*)) :start (interop/now)}) (macros/deftime (defmacro finish-trace [trace] `(when (is-trace-enabled?) (let [end# (interop/now) duration# (- end# (:start ~trace))] (doseq [[k# cb#] @trace-cbs] (try (cb# [(assoc ~trace :duration duration# :end (interop/now))]) #?(:clj (catch Exception e# (console :error "Error thrown from trace cb" k# "while storing" ~trace e#))) #?(:cljs (catch :default e# (console :error "Error thrown from trace cb" k# "while storing" ~trace e#)))))))) (defmacro with-trace "Create a trace inside the scope of the with-trace macro Common keys for trace-opts :op-type - what kind of operation is this? e.g. :sub/create, :render. :operation - identifier for the operation, for an subscription it would be the subscription keyword tags - a map of arbitrary kv pairs" [{:keys [operation op-type tags child-of] :as trace-opts} & body] `(if (is-trace-enabled?) (binding [*current-trace* (start-trace ~trace-opts)] (try ~@body (finally (finish-trace *current-trace*)))) (do ~@body))) (defmacro merge-trace! [m] ;; Overwrite keys in tags, and all top level keys. `(when (is-trace-enabled?) (let [new-trace# (-> (update *current-trace* :tags merge (:tags ~m)) (merge (dissoc ~m :tags)))] (set! *current-trace* new-trace#)) nil)))