diff --git a/src/taoensso/telemere/impl.cljc b/src/taoensso/telemere/impl.cljc index 8656f8c..72b9f3b 100644 --- a/src/taoensso/telemere/impl.cljc +++ b/src/taoensso/telemere/impl.cljc @@ -225,6 +225,8 @@ (do (enc/def-print-impl [sig Signal] (str "#" `Signal (pr-str (into {} sig))))) #?(:clj (enc/def-print-dup [sig Signal] (str "#" `Signal (pr-str (into {} sig))))) ; NB intentionally verbose, to support extra keys +(def ^:no-doc standard-signal-keys "Private, don't use." (set (keys (map->Signal {:schema 0})))) + (comment (def s1 (with-signal (signal! {:level :info, :my-k1 :my-v1}))) (read-string (str (assoc s1 :my-k2 :my-v2))) diff --git a/src/taoensso/telemere/utils.cljc b/src/taoensso/telemere/utils.cljc index 11b2e1c..40472b0 100644 --- a/src/taoensso/telemere/utils.cljc +++ b/src/taoensso/telemere/utils.cljc @@ -560,39 +560,39 @@ "Experimental, subject to change. Returns a (fn pr [signal]) that: - Takes a Telemere signal (map). - - Returns a machine-readable (minified) signal string. + - Returns a machine-readable signal ?string. Options: `:pr-fn` - ∈ #{ :edn (default) :json (Cljs only)} - `:incl-thread?` - Include signal `:thread` info? (default false) - `:incl-kvs?` - Include signal `:kvs` info? (default false) - `:incl-newline?` - Include terminating system newline? (default true) + `:incl-kvs?` - Include signal's user-level kvs? (default false) + `:incl-nils?` - Include signal's keys with nil values? (default false) + `:incl-newline?` - Include terminating system newline? (default true) + `:incl-keys` - Subset of signal keys to retain from those otherwise + excluded by default: #{:location :kvs :file :thread} Examples: (pr-signal-fn {:pr-fn :edn ...}) ; Outputs edn (pr-signal-fn {:pr-fn :json ...}) ; Outputs JSON (Cljs only) To output JSON for Clj, you must provide an appropriate `:pr-fn`. - `jsonista` is one good option, Ref. : + `jsonista` offers one good option, Ref. : (require '[jsonista.core :as jsonista]) (pr-signal-fn {:pr-fn jsonista/write-value-as-string ...}) See also `format-signal-fn` for human-readable output." ([] (pr-signal-fn nil)) - ([{:keys [incl-thread? incl-kvs? incl-newline?, pr-fn prep-fn] + ([{:keys [pr-fn, incl-kvs? incl-nils? incl-newline? incl-keys] :as opts :or - {incl-newline? true - pr-fn :edn - prep-fn - (comp expand-signal-error - remove-signal-nils)}}] + {pr-fn :edn + incl-newline? true}}] (let [nl newline pr-fn (or - (case pr-fn - :edn pr-edn + (case pr-fn + :none nil ; Undocumented, used for unit tests + :edn pr-edn :json #?(:cljs pr-json :clj @@ -607,23 +607,61 @@ :param 'pr-fn :expected #?(:clj '#{:edn unary-fn} - :cljs '#{:edn :json unary-fn})}))))] + :cljs '#{:edn :json unary-fn})})))) + + assoc!* + (if-not incl-nils? + (fn [m k v] (if (nil? v) m (assoc! m k v))) ; As `remove-signal-nils` + (do assoc!)) + + incl-location? (contains? incl-keys :location) + incl-kvs-key? (contains? incl-keys :kvs) + incl-file? (contains? incl-keys :file) + incl-thread? (contains? incl-keys :thread)] (fn pr-signal [signal] - (let [not-map? (not (map? signal)) - signal (if (or incl-kvs? not-map?) signal (dissoc signal :kvs)) - signal (if (or incl-thread? not-map?) signal (dissoc signal :thread)) - signal (if not-map? signal (force-signal-msg signal)) - signal (if prep-fn (prep-fn signal) signal) - output (pr-fn signal)] + (when (map? signal) + (let [signal + (persistent! + (reduce-kv + (fn [m k v] + (enc/case-eval k + :msg_ (assoc!* m k (force v)) ; As force-signal-msg + :error (if-let [chain (enc/ex-chain :as-map v)] (assoc! m k chain) m) ; As expand-signal-error - (if incl-newline? - (str output nl) - (do output))))))) + ;;; Keys excluded by default + :location (if incl-location? (assoc!* m k v) m) + :kvs (if incl-kvs-key? (assoc!* m k v) m) + :file (if incl-file? (assoc!* m k v) m) + :thread (if incl-thread? (assoc!* m k v) m) + + (clojure.core/into () + (clojure.core/disj + taoensso.telemere.impl/standard-signal-keys + :msg_ :error :location :kvs :file :thread)) + (assoc!* m k v) + + (if incl-kvs? (assoc!* m k v) m) ; As remove-signal-kvs + )) + + (transient {}) signal))] + + (if-not pr-fn + signal + (let [output (pr-fn signal)] + (if incl-newline? + (str output nl) + (do output)))))))))) (comment - ((pr-signal-fn {:pr-fn :edn}) (tel/with-signal (tel/event! ::ev-id {:kvs {:k1 "v1"}}))) - ((pr-signal-fn {:pr-fn (fn [_] "str")}) (tel/with-signal (tel/event! ::ev-id {:kvs {:k1 "v1"}})))) + (def s1 (tel/with-signal (tel/event! ::ev-id {:kvs {:k1 "v1"}}))) + ((pr-signal-fn {:pr-fn :edn}) s1) + ((pr-signal-fn {:pr-fn (fn [_] "str")}) s1) + ((pr-signal-fn {:pr-fn :none}) s1) + + (let [pr-fn (pr-signal-fn {:pr-fn :none})] + (enc/qb 1e6 ; 817.78 + (pr-fn s1)))) (defn format-signal-fn "Experimental, subject to change. diff --git a/test/taoensso/telemere_tests.cljc b/test/taoensso/telemere_tests.cljc index fd7e209..4c3e623 100644 --- a/test/taoensso/telemere_tests.cljc +++ b/test/taoensso/telemere_tests.cljc @@ -33,6 +33,7 @@ (def ex-info-type (#'enc/ex-type (ex-info "" {}))) (def ex1 (ex-info "Ex1" {})) (def ex2 (ex-info "Ex2" {:k2 "v2"} (ex-info "Ex1" {:k1 "v1"}))) + (def ex2-chain (enc/ex-chain :as-map ex2)) (defn ex1! [] (throw ex1)) (defn ex1? [x] (= (enc/ex-root x) ex1)) @@ -645,9 +646,7 @@ [(is (= (utils/remove-signal-kvs {:a :A, :b :B, :kvs {:b :B}}) {:a :A})) (is (= (utils/remove-signal-nils {:a :A, :b nil}) {:a :A})) (is (= (utils/force-signal-msg {:a :A, :msg_ (delay "msg")}) {:a :A, :msg_ "msg"})) - (is (= (utils/expand-signal-error {:level :info, :error ex2}) - {:level :info, :error [{:type ex-info-type, :msg "Ex2", :data {:k2 "v2"}} - {:type ex-info-type, :msg "Ex1", :data {:k1 "v1"}}]}))]) + (is (= (utils/expand-signal-error {:level :info, :error ex2}) {:level :info, :error ex2-chain}))]) #?(:clj (testing "File writer" @@ -692,7 +691,7 @@ (testing "pr-signal-fn" (let [sig (with-sig :raw :trap (tel/event! ::ev-id {:inst t0, :msg ["a" "b"]}))] - [(testing ":edn" + [(testing ":edn pr-fn" (let [sig (update sig :inst enc/inst->udt) sig*1 (enc/read-edn ((tel/pr-signal-fn {:pr-fn :edn}) sig)) sig*2 (enc/read-edn ((tel/pr-signal-fn) sig))] @@ -708,7 +707,7 @@ :column pnat-int?}))])) #?(:cljs - (testing ":json" + (testing ":json pr-fn" (let [sig* (enc/read-json ((tel/pr-signal-fn {:pr-fn :json}) sig))] (is (enc/submap? sig* @@ -719,8 +718,27 @@ "line" pnat-int? "column" pnat-int?}))))) - (testing "user fn" - (is (= ((tel/pr-signal-fn {:pr-fn (fn [_] "str")}) sig) (str "str" utils/newline))))])) + (testing "User pr-fn" + (is (= ((tel/pr-signal-fn {:pr-fn (fn [_] "str")}) sig) (str "str" utils/newline)))) + + (testing "Other options" + (let [sig + {:msg_ (delay "msg") + :error ex2 + :id nil + :location "loc" + :kvs "kvs" + :file "file" + :thread "thread" + :user-key "user-val"}] + + [(is (= ((tel/pr-signal-fn {:pr-fn :none}) sig) {:msg_ "msg", :error ex2-chain})) + (is (= ((tel/pr-signal-fn {:pr-fn :none, :incl-kvs? true}) sig) {:msg_ "msg", :error ex2-chain, :user-key "user-val"})) + (is (= ((tel/pr-signal-fn {:pr-fn :none, :incl-nils? true}) sig) {:msg_ "msg", :error ex2-chain, :id nil})) + (is (= ((tel/pr-signal-fn {:pr-fn :none, :incl-keys #{:kvs}}) sig) {:msg_ "msg", :error ex2-chain, :kvs "kvs"})) + (is (= ((tel/pr-signal-fn {:pr-fn :none, :incl-keys + #{:location :kvs :file :thread}}) sig) {:msg_ "msg", :error ex2-chain, + :location "loc", :kvs "kvs", :file "file", :thread "thread"}))]))])) (testing "format-signal-fn" (let [sig (with-sig :raw :trap (tel/event! ::ev-id {:inst t0, :msg ["a" "b"]}))]