diff --git a/doc/02-debugging.md b/doc/02-debugging.md index 291ed3d..f660a22 100644 --- a/doc/02-debugging.md +++ b/doc/02-debugging.md @@ -63,8 +63,8 @@ Providing an options map we can add / remove some logging detail: - `:stats?` Default `false`. Include basic timed statistics for the event execution. - `:target` Default `:target/console`. Logging target, one of - `:target/console` - Standard out - - `:target/dev-tools` - (Experimental) Flutter [DevTools logging view](https://docs.flutter.dev/tools/devtools/logging), with Kind `re-dash` - - `:target/everywhere` - Log to both console and Flutter DevTools (experimental) + - `:target/dev-tools` - Send to the [re-dash-inspector](https://github.com/htihospitality/re-dash-inspector) + - `:target/everywhere` - Send to both console and the [re-dash-inspector](https://github.com/htihospitality/re-dash-inspector) With all the options enabled: @@ -114,3 +114,11 @@ Add this somewhere early on in your app's startup, like in `main` (rd/reg-global-interceptor (rd/debug)) ``` + +### Performance + +The `debug` interceptor will only do anything when the app is running in debug mode, ie. [kDebugMode](https://api.flutter.dev/flutter/foundation/kDebugMode-constant.html) has to be true. + +When the target is `:target/dev-tools` the [re-dash-inspector](https://github.com/htihospitality/re-dash-inspector) has to have an active connection to re-dash before events will be sent. + +If neither of the above is true, the `debug` interceptor is effectively a no-op & will have no impact on app performance. diff --git a/src/hti/re_dash/debugging.cljd b/src/hti/re_dash/debugging.cljd index 2850d61..9bdc485 100644 --- a/src/hti/re_dash/debugging.cljd +++ b/src/hti/re_dash/debugging.cljd @@ -1,13 +1,34 @@ (ns hti.re-dash.debugging (:require ["dart:developer" :as dev] ["dart:convert" :as c] + ["package:flutter/foundation.dart" :as ff] [clojure.data :as data] [clojure.string :as string] - [hti.re-dash.interceptors :as interceptors])) + [hti.re-dash.interceptors :as interceptors] + [clojure.walk :as w])) +(defonce debug-state (atom {})) + +(defn sanitize + "Transforms all un-serializable map values with placeholders." + [m] + (w/postwalk + (fn [element] + (if (map-entry? element) + (let [[key val] element] + [key + + (cond + + ;; Functions not de-serializable by cljd.reader + (fn? val) 'closure + + :else val)]) + element)) + m)) (defn- log-console! - [{:keys [event-id started finished duration event-args diff + [{:keys [event-id started finished duration event-args db-before db-after diff? stats? args?] :or {diff? true stats? false @@ -30,12 +51,13 @@ "### Event Args:" event-args]) (when diff? - ["-----------------------------------------------" - "### DB Diff Before (Removed):" - (or (first diff) {}) - "-----------------------------------------------" - "### DB Diff After (Added):" - (or (second diff) {})]) + (let [diff (when db-after (data/diff db-before db-after))] + ["-----------------------------------------------" + "### DB Diff Before (Removed):" + (or (first diff) {}) + "-----------------------------------------------" + "### DB Diff After (Added):" + (or (second diff) {})])) ["###############################################" "\r\n"]) flatten @@ -44,23 +66,34 @@ (str "Event: " event-id)))) +(defn- inspect-dev-tools! + "Sends a serialized db value to the re-dash-inspector DevTools extension" + [{:keys [event-id db-after]}] -(defn- log-dev-tools! - [{:keys [event-id diff event-args started finished duration - diff? stats? args?] - :or {diff? true - stats? false - args? false}}] + (when (and ff/kDebugMode (:re-dash/inspector-connected? @debug-state)) + (dev/postEvent "re-dash" + {"event-id" (pr-str event-id) + "db" (-> db-after + sanitize + pr-str)}))) + +(defn- register-inspector + "Registers the re-dash-inspector listener to + react to connect/disconnect events" + [target] + (when (and (#{:target/dev-tools :target/everywhere} target) + (not (:re-dash/inspector-registered? @debug-state))) + + (dev/registerExtension + "ext.re-dash-inspector.updateConnectionState" + (fn [_method {:strs [action]}] + (condp = action + "connect" (do (swap! debug-state assoc :re-dash/inspector-connected? true) + (Future.value (dev/ServiceExtensionResponse.result "connected"))) + "disconnect" (do (swap! debug-state assoc :re-dash/inspector-connected? false) + (Future.value (dev/ServiceExtensionResponse.result "disconnected")))))) - (dev/log (str "Event: " event-id) - .name "re-dash" - .error (cond-> {} - diff? (assoc :db {:before (or (first diff) {}) - :after (or (second diff) {})}) - args? (assoc :args event-args) - stats? (assoc :stats {:started started - :finished finished - :duration duration})))) + (swap! debug-state assoc :re-dash/inspector-registered? true))) (defn debug @@ -68,43 +101,46 @@ :or {target :target/console} :as options}] - (interceptors/->interceptor - - :re-dash/debug - - (fn before [context] - (assoc context :debug {:started (DateTime/now) - :db (-> context :coeffects :db)})) - - (fn after [context] - (let [event-id (-> context :coeffects :event first) - event-args (-> context :coeffects :event rest) - started (-> context :debug :started) - finished (DateTime/now) - duration (.difference finished started) - db-before (-> context :debug :db) - db-after (-> context :effects :db) - diff (when db-after - (data/diff db-before db-after)) - log-args (merge - {:event-id event-id - :started started - :finished finished - :duration duration - :event-args event-args - :diff diff} - options)] - - (condp = target - - :target/console - (log-console! log-args) - - :target/dev-tools - (log-dev-tools! log-args) - - :target/everywhere - (do (log-console! log-args) - (log-dev-tools! log-args)))) - - context))) + (when ff/kDebugMode + + (register-inspector target) + + (interceptors/->interceptor + + :re-dash/debug + + (fn before [context] + (assoc context :debug {:started (DateTime/now) + :db (-> context :coeffects :db)})) + + (fn after [context] + (let [event-id (-> context :coeffects :event first) + event-args (-> context :coeffects :event rest) + started (-> context :debug :started) + finished (DateTime/now) + duration (.difference finished started) + db-before (-> context :debug :db) + db-after (-> context :effects :db) + log-args (merge + {:event-id event-id + :started started + :finished finished + :duration duration + :event-args event-args + :db-before db-before + :db-after db-after} + options)] + + (condp = target + + :target/console + (log-console! log-args) + + :target/dev-tools + (inspect-dev-tools! log-args) + + :target/everywhere + (do (log-console! log-args) + (inspect-dev-tools! log-args)))) + + context))))