diff --git a/src/migratus/cli.clj b/src/migratus/cli.clj index d662cdf..518237e 100644 --- a/src/migratus/cli.clj +++ b/src/migratus/cli.clj @@ -6,11 +6,20 @@ [clojure.tools.logging :as log] [migratus.core :as migratus]) (:import [java.time ZoneOffset] - [java.util.logging ConsoleHandler Logger LogRecord - Formatter SimpleFormatter])) + [java.util.logging + ConsoleHandler + Formatter + Level + LogRecord + Logger + SimpleFormatter])) (def global-cli-options - [[nil "--config NAME" "Configuration file name" :default "migratus.edn"] + [[nil "--config NAME" "Configuration file name" ] + ["-v" nil "Verbosity level; may be specified multiple times to increase value" + :id :verbosity + :default 0 + :update-fn inc] ["-h" "--help"]]) (def migrate-cli-options @@ -56,6 +65,7 @@ (defn run-migrate [cfg [_ & args]] (let [{:keys [options arguments errors summary]} (parse-opts args migrate-cli-options :in-order true)] + (cond errors (error-msg errors) (:until-just-before options) @@ -87,6 +97,7 @@ (defn run-list [cfg [_ & args]] (let [{:keys [options _arguments errors summary]} (parse-opts args list-cli-options :in-order true)] (cond + errors (error-msg errors) (:applyed options) (log/info "listing applyed migrations") (:pending options) (do (log/info "listing pending migrations, configuration is: \n" cfg) @@ -119,31 +130,88 @@ logger (.getLoggerName record)] (core/format fmt date src logger level msg thr)))) +(defn verbose-log-level [v] + (case v + 0 java.util.logging.Level/INFO ;; :info + 1 java.util.logging.Level/FINE ;; :debug + java.util.logging.Level/FINEST)) ;; :trace + (defn set-logger-format "Configure JUL logger to use a custom log formatter. * formatter - instance of java.util.logging.Formatter" - ([] - (set-logger-format (simple-formatter format-log-record))) - ([^Formatter formatter] + ([verbosity] + (set-logger-format verbosity (simple-formatter format-log-record))) + ([verbosity ^Formatter formatter] (let [main-logger (doto (Logger/getLogger "") - (.setUseParentHandlers false)) + (.setUseParentHandlers false) + (.setLevel (verbose-log-level verbosity))) handler (doto (ConsoleHandler.) - (.setFormatter formatter)) + (.setFormatter formatter) + (.setLevel (verbose-log-level verbosity))) handlers (.getHandlers main-logger)] (doseq [h handlers] (.removeHandler main-logger h)) (.addHandler main-logger handler)))) +(defn simple-formatter + "Clojure bridge for java.util.logging.SimpleFormatter. + Can register a clojure fn as a logger formatter. + + * format-fn - clojure fn that receives the record to send to logging." + (^SimpleFormatter [format-fn] + (proxy [SimpleFormatter] [] + (format [record] + (format-fn record))))) + +(defn format-log-record + "Format jul logger record." + (^String [^LogRecord record] + (let [fmt "%5$s" + instant (.getInstant record) + date (-> instant (.atZone ZoneOffset/UTC)) + level (.getLevel record) + src (.getSourceClassName record) + msg (.getMessage record) + thr (.getThrown record) + logger (.getLoggerName record)] + (core/format fmt date src logger level msg thr)))) + +(defn set-logger-format + "Configure JUL logger to use a custom log formatter. + + * formatter - instance of java.util.logging.Formatter" + ([verbosity] + (set-logger-format verbosity (simple-formatter format-log-record))) + ([verbosity ^Formatter formatter] + (let [main-logger (doto (Logger/getLogger "") + (.setUseParentHandlers false) + (.setLevel (verbose-log-level verbosity))) + handler (doto (ConsoleHandler.) + (.setFormatter formatter) + (.setLevel (verbose-log-level verbosity))) + handlers (.getHandlers main-logger)] + (doseq [h handlers] + (.removeHandler main-logger h)) + (.addHandler main-logger handler)))) + +(defn load-config! + "Returns the content of config file as a clojure map datastructure" + [^String config] + (let [config-path (.getAbsolutePath (io/file config))] + (try + (read-string (slurp config-path)) + (catch java.io.FileNotFoundException e + (log/info "Missing config file" (.getMessage e) + "\nYou can use --config path_to_file to specify a path to config file"))))) + (defn -main [& args] (let [{:keys [options arguments _errors summary]} (parse-opts args global-cli-options :in-order true) config (:config options) - config-path (.getAbsolutePath (io/file config)) - cfg (read-string (slurp config-path)) + verbosity (:verbosity options) + cfg (load-config! config) action (first arguments)] - - (set-logger-format) - + (set-logger-format verbosity) (cond (:help options) (usage summary) (nil? (:config options)) (error-msg "No config provided \n --config [file-name]>") @@ -159,4 +227,3 @@ (no-match-message arguments summary))))) -