taoensso / timbre

Pure Clojure/Script logging library
https://www.taoensso.com/timbre
Eclipse Public License 1.0
1.44k stars 171 forks source link

Support ns-levels #310

Closed ivarref closed 4 years ago

ivarref commented 4 years ago

Hi

And thanks for another fine library!

I'd like to see support for something like :ns-levels. An example config could be like this:

{:level :warn
 :ns-levels {"my-app.*" :info
             "my-app.more-info-please" :debug
             "my-lib.*" :debug}}

The rule for determining the effective log level for a logger may be as following:

  1. Check fully qualified name (my-app.more-info-please).
  2. Check wildcards (my-app.*, my-lib.*).
  3. Otherwise use :level.

What do you think? Logback offers this functionality. It's helpful when dealing with noisy third party libraries.

Thanks and kind regards.

ptaoussanis commented 4 years ago

Hi Ivar, good to hear from you again. Idea sounds good to me! PR welcome.

ivarref commented 4 years ago

Good to hear from you as well Peter!

I've written some simple code for this:

(ns ns-levels
  (:require [clojure.string :as str]))

(defn verify-ns-match! [the-ns]
  (let [err-msg "wildcard ns match must contain a single star and this must be at the end. Was given: "]
    (when (str/includes? the-ns "*")
      (if-not (str/ends-with? the-ns "*")
        (throw (ex-info (str err-msg the-ns) {:value the-ns}))
        (let [minus-tail (subs the-ns 0 (dec (count the-ns)))]
          (when (str/includes? minus-tail "*")
            (throw (ex-info (str err-msg the-ns) {:value the-ns}))))))))

(defn levels-map->vector
  [m]
  (doseq [[the-ns _] m]
    (verify-ns-match! the-ns))
  (let [wildcard? (comp #(str/ends-with? % "*") first)
        exact-ns (->> (remove wildcard? m)
                      (map #(into [:exact] %)))
        wildcard-ns (->> (filter wildcard? m)
                         (mapv (fn [[the-ns level]]
                                 [:wildcard
                                  (subs the-ns 0 (dec (count the-ns)))
                                  level])))
        sort-ns (fn [n]
                  (->> n
                       ;(shuffle)
                       (sort-by (comp count second))
                       (reverse)))]
    (reduce into [] [(sort-ns exact-ns) (sort-ns wildcard-ns)])))

(defn ns->level [ns-levels the-ns]
  (reduce (fn [o [typ cand-ns lvl]]
            (case typ
              :exact (if (= cand-ns the-ns)
                       (reduced lvl)
                       o)
              :wildcard (if (str/starts-with? the-ns cand-ns)
                          (reduced lvl)
                          o)))
          nil
          ns-levels))

(comment
  (do
    (require '[clojure.test :refer [is]])
    (def m (levels-map->vector {"my-app*"                 :info
                                "my-app.more-info-please" :debug
                                "my-app.shorter"          :warn
                                "my-lib.bugged*"          :trace
                                "*"                       :error}))
    (is (= :info (ns->level m "my-app")))
    (is (= :debug (ns->level m "my-app.more-info-please")))
    (is (= :warn (ns->level m "my-app.shorter")))
    (is (= :trace (ns->level m "my-lib.bugged")))
    (is (= :error (ns->level m "error-level")))
    (is (nil? (ns->level (levels-map->vector {}) "nothing-found")))))

Hope to have the time for a proper pull request soon. Or feel free to copy this code of course if you find it good.

ivarref commented 4 years ago

Hi again @ptaoussanis (and @vemv and @yogsototh)

It seems this feature is on master already (!): :ns-log-level. https://github.com/ptaoussanis/timbre/blob/64ea5a3689f2171e3b38f80100cefd697b41ae91/src/taoensso/timbre.cljx#L121

It's available on 4.11.0-alpha1, and not on 4.10.0.

Example behaviour below:

(ns timbre-user.core
  (:require [taoensso.timbre :as timbre]))

(timbre/merge-config!
  {:level :warn
   :ns-log-level [["timbre-user.*" :info]]})

(timbre/info "info!")
(timbre/info "may-log? somelib :info =>" (timbre/may-log? :info "somelib" timbre/*config*))
(timbre/info "may-log? somelib :warn =>" (timbre/may-log? :warn "somelib" timbre/*config*))

;2020-07-19T19:48:59.764Z refseviken INFO [timbre-user.core:8] - info!
;2020-07-19T19:48:59.786Z refseviken INFO [timbre-user.core:9] - may-log? somelib :info => false
;2020-07-19T19:48:59.788Z refseviken INFO [timbre-user.core:10] - may-log? somelib :warn => true

This is pretty much exactly what I wanted.

This issue should be closed when this code lands in a stable release.

Thanks and kind regards.

ptaoussanis commented 4 years ago

Thanks Ivar! Apologies, had forgotten that this was already implemented 🤦

ptaoussanis commented 4 years ago

Ref. https://github.com/ptaoussanis/timbre/pull/255, will try cut a stable release w/in the next few days.