🎉 Add logging framework to frontend.

This commit is contained in:
Andrey Antukh 2020-10-21 16:54:59 +02:00 committed by Hirunatan
parent ded42707ab
commit 21a6de02d8
7 changed files with 328 additions and 100 deletions

View file

@ -24,10 +24,15 @@
[app.util.storage :refer [storage]]
[app.util.theme :as theme]
[app.util.timers :as ts]
[app.util.logging :as log]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[rumext.alpha :as mf]))
(log/initialize!)
(log/set-level! :root :warn)
(log/set-level! :app :info)
(declare reinit)
(defn on-navigate
@ -78,4 +83,3 @@
(defn ^:dev/after-load after-load
[]
(reinit))

View file

@ -0,0 +1,39 @@
(ns app.util.logging)
(defn- log-expr [form level keyvals]
(let [keyvals-map (apply array-map keyvals)
formatter (::formatter keyvals-map 'identity)]
`(log ~(::logger keyvals-map (str *ns*))
~level
~(-> keyvals-map
(dissoc ::logger)
#_(assoc :line (:line (meta form))))
~(:err keyvals-map))))
(defmacro set-level!
([level]
`(set-level* ~(str *ns*) ~level))
([n level]
`(set-level* ~n ~level)))
(defmacro error [& keyvals]
(log-expr &form :error keyvals))
(defmacro warn [& keyvals]
(log-expr &form :warn keyvals))
(defmacro info [& keyvals]
(log-expr &form :info keyvals))
(defmacro debug [& keyvals]
(log-expr &form :debug keyvals))
(defmacro trace [& keyvals]
(log-expr &form :trace keyvals))
(defmacro spy [form]
(let [res (gensym)]
`(let [~res ~form]
~(log-expr &form :debug [:spy `'~form
:=> res])
~res)))

View file

@ -0,0 +1,200 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2020 UXBOX Labs SL
;;
;; This code is highly inspired on the lambdaisland/glogi library but
;; adapted and simplified to our needs. The adapted code shares the
;; same license. You can found the origianl source code here:
;; https://github.com/lambdaisland/glogi
(ns app.util.logging
(:require
[goog.log :as glog]
[goog.debug.Logger :as Logger]
[goog.debug.Logger.Level :as Level]
[goog.debug.Console :as Console]
[cuerdas.core :as str]
[goog.object :as gobj])
(:import
[goog.debug Logger Console LogRecord]
[goog.debug.Logger Level])
(:require-macros [app.util.logging]))
(defn- logger-name
[s]
(cond
(string? s) s
(= s :root) ""
(simple-ident? s) (name s)
(qualified-ident? s) (str (namespace s) "." (name s))
:else (str s)))
(defn get-logger
[n]
(if (instance? Logger n)
n
(glog/getLogger (logger-name n))))
(def root (get-logger :root))
(def levels
{:off Level/OFF
:error Level/SEVERE
:warn Level/WARNING
:warning Level/WARNING
:info Level/INFO
:debug Level/FINE
:trace Level/FINER
:fine Level/FINE
:finer Level/FINER
:all Level/ALL})
(def colors
{:gray3 "#8e908c"
:gray4 "#969896"
:gray5 "#4d4d4c"
:gray6 "#282a2e"
:black "#1d1f21"
:red "#c82829"
:blue "#4271ae"
:orange "#f5871f"})
(defn- get-level-value
[level]
(if (instance? Level level)
(.-value ^Level level)
(.-value ^Level (get levels level))))
(defn- level->color
[level]
(condp <= (get-level-value level)
(get-level-value :error) (get colors :red)
(get-level-value :warn) (get colors :orange)
(get-level-value :info) (get colors :blue)
(get-level-value :debug) (get colors :gray4)
(get-level-value :trace) (get colors :gray3)
(get colors :gray2)))
(defn- level->short-name
[l]
(case l
:fine "DBG"
:debug "DBG"
:finer "TRC"
:trace "TRC"
:info "INF"
:warn "WRN"
:warning "WRN"
:error "ERR"))
(defn- make-log-record
[level message name exception]
(let [record (LogRecord. level message name)]
(when exception
(.setException record exception))
record))
(defn log
"Output a log message to the given logger, optionally with an exception to be
logged."
([name lvl message]
(log name lvl message nil))
([name lvl message exception]
(when glog/ENABLED
(when-let [l (get-logger name)]
(.logRecord ^Logger l (make-log-record (get levels lvl) message name exception))))))
(defn set-level*
"Set the level (a keyword) of the given logger, identified by name."
[name lvl]
(assert (contains? levels lvl))
(when-let [l (get-logger name)]
(.setLevel ^Logger l (get levels lvl))))
(defn set-levels!
[lvls]
(doseq [[logger level] lvls
:let [level (if (string? level) (keyword level) level)]]
(set-level* logger level)))
(defn add-handler!
([handler-fn]
(add-handler! root handler-fn))
([logger-or-name handler-fn]
(when-let [l (get-logger logger-or-name)]
(letfn [(handler [^LogRecord record]
(handler-fn {:seqn (.-sequenceNumber_ record)
:time (.-time_ record)
:level (keyword (str/lower (.-name (.-level_ record))))
:message (.-msg_ record)
:logger-name (.-loggerName_ record)
:exception (.-exception_ record)}))]
(unchecked-set handler "handler-fn" handler-fn)
(.addHandler ^Logger l handler)))))
(defn add-handler-once!
([handler-fn]
(add-handler-once! root handler-fn))
([logger-or-name handler-fn]
(when-let [l (get-logger logger-or-name)]
(when-not (some (comp #{handler-fn} #(gobj/get % "handler-fn"))
(.-handlers_ l))
(add-handler! l handler-fn)))))
(defn- prepare-message
[message]
(loop [kvpairs (seq message)
message (array-map)
specials []]
(if (nil? kvpairs)
[message specials]
(let [[k v] (first kvpairs)]
(cond
(= k :err)
(recur (next kvpairs)
message
(conj specials [:error nil v]))
(and (qualified-ident? k)
(= "js" (namespace k)))
(recur (next kvpairs)
message
(conj specials [:js (name k) (if (object? v) v (clj->js v))]))
:else
(recur (next kvpairs)
(assoc message k v)
specials))))))
(defonce default-console-handler
(fn [{:keys [message exception level logger-name]}]
(let [header-styles (str "font-weight: 600; color: " (level->color level))
normal-styles (str "font-weight: 300; color: " (get colors :gray6))
level-name (level->short-name level)
header (str "%c" level-name " [" logger-name "] ")]
(if (string? message)
(let [message (str header "%c" message)]
(js/console.log message header-styles normal-styles))
(let [[message specials] (prepare-message message)]
(if (seq specials)
(let [message (str header "%c" (pr-str message))]
(js/console.group message header-styles normal-styles)
(doseq [[type n v] specials]
(case type
:js (js/console.log n v)
:error (if (instance? cljs.core.ExceptionInfo v)
(js/console.error (pr-str v))
(js/console.error v))))
(js/console.groupEnd message))
(let [message (str header "%c" (pr-str message))]
(js/console.log message header-styles normal-styles))))))))
(defn initialize!
[]
(when-let [instance Console/instance]
(.setCapturing ^Console instance false))
(add-handler-once! default-console-handler))