♻️ Refactor error reporting and logging context formatting

The prev approach uses clojure.pprint without any limit extensivelly
for format error context data and all this is done on the calling
thread. The clojure.pprint seems very inneficient in cpu and memory
usage on pprinting large data structures.

This is improved in the following way:

- All formatting and pretty printing is moved to logging thread,
  reducing unnecesary blocking and load on jetty http threads.
- Replace the clojure.pprint with fipp.edn that looks considerably
  faster than the first one.
- Add some safe limits on pretty printer for strip printing some
  data when the data structure is very large, very deep or both.
This commit is contained in:
Andrey Antukh 2022-02-09 17:45:45 +01:00 committed by Alonso Torres
parent 2c25dfcf1b
commit 827c2140b7
10 changed files with 84 additions and 75 deletions

View file

@ -9,6 +9,7 @@
[app.common.exceptions :as ex]
[clojure.pprint :refer [pprint]]
[cuerdas.core :as str]
[fipp.edn :as fpp]
#?(:clj [io.aviso.exception :as ie])
#?(:cljs [goog.log :as glog]))
#?(:cljs (:require-macros [app.common.logging])
@ -52,22 +53,16 @@
(defn stringify-data
[val]
(cond
(instance? clojure.lang.Named val)
(name val)
(instance? Throwable val)
(binding [ie/*app-frame-names* [#"app.*"]
ie/*fonts* nil
ie/*traditional* true]
(ie/format-exception val nil))
(string? val)
val
(instance? clojure.lang.Named val)
(name val)
(coll? val)
(binding [clojure.pprint/*print-right-margin* 200]
(-> (with-out-str (pprint val))
(simple-prune (* 1024 1024 3))))
(binding [*print-level* 5
*print-length* 20]
(with-out-str (fpp/pprint val {:width 200})))
:else
(str val))))
@ -163,13 +158,13 @@
(.isEnabled ^Logger logger ^Level level)))
(defmacro log
[& {:keys [level cause ::logger ::async ::raw] :or {async true} :as props}]
[& {:keys [level cause ::logger ::async ::raw ::context] :or {async true} :as props}]
(if (:ns &env) ; CLJS
`(write-log! ~(or logger (str *ns*))
~level
~cause
(or ~raw ~(dissoc props :level :cause ::logger ::raw)))
(let [props (dissoc props :level :cause ::logger ::async ::raw)
(or ~raw ~(dissoc props :level :cause ::logger ::raw ::context)))
(let [props (dissoc props :level :cause ::logger ::async ::raw ::context)
logger (or logger (str *ns*))
logger-sym (gensym "log")
level-sym (gensym "log")]
@ -180,7 +175,7 @@
`(->> (ThreadContext/getImmutableContext)
(send-off logging-agent
(fn [_# cdata#]
(with-context (into {} cdata#)
(with-context (-> {} (into cdata#) (into ~context))
(->> (or ~raw (build-map-message ~props))
(write-log! ~logger-sym ~level-sym ~cause))))))

View file

@ -19,7 +19,7 @@
[app.common.geom.point :as gpt]
[app.common.uuid :as uuid]
[cuerdas.core :as str]
[expound.alpha]))
[expound.alpha :as expound]))
(s/check-asserts true)
@ -270,3 +270,14 @@
(spec-assert* ~spec params# ~message mdata#)
(apply origf# params#)))))))
(defn pretty-explain
([data] (pretty-explain data nil))
([data {:keys [max-problems] :or {max-problems 10}}]
(when (and (::s/problems data)
(::s/value data)
(::s/spec data))
(binding [s/*explain-out* expound/printer]
(with-out-str
(s/explain-out (update data ::s/problems #(take max-problems %))))))))