diff options
author | Alexander Taggart <alex.taggart@gmail.com> | 2009-08-03 23:50:22 -0700 |
---|---|---|
committer | Tom Faulhaber <git_net@infolace.com> | 2009-08-04 10:35:01 -0700 |
commit | 6c95fe90829cc66f81345a011dc25fc487e4cf0b (patch) | |
tree | 0e350ee4b59630aba6fc24432ebe9ab06afe7dc8 /src/clojure | |
parent | dcec88467e840c0120e9f27f2020636857692800 (diff) |
Added level-specific convenience log macros. Improved documentation.
Diffstat (limited to 'src/clojure')
-rw-r--r-- | src/clojure/contrib/logging.clj | 170 |
1 files changed, 109 insertions, 61 deletions
diff --git a/src/clojure/contrib/logging.clj b/src/clojure/contrib/logging.clj index 18489cb0..520fd14a 100644 --- a/src/clojure/contrib/logging.clj +++ b/src/clojure/contrib/logging.clj @@ -13,43 +13,44 @@ (ns #^{:author "Alex Taggart, Timothy Pratley", - :doc "Logging macros which delegate to a specific logging - implementation. At runtime a specific implementation is selected from, in - order, Apache commons-logging, log4j, and finally java.util.logging. + :doc + "Logging macros which delegate to a specific logging implementation. At + macro-expansion-time a specific implementation is selected from, in order, + Apache commons-logging, log4j, and finally java.util.logging. - Logging levels are specified by clojure keywords corresponding to the + Logging levels are specified by clojure keywords corresponding to the values used in log4j/commons-logging: - :trace, :debug, :info, :warn, :error, :fatal + :trace, :debug, :info, :warn, :error, :fatal - Logging occurs with the log macro which writes either directly or via an - agent. By default direct logging is disabled, but can be enabled via the - *allow-direct-logging* boolean ref. If log is invoked within a transaction it - will always use an agent. + Logging occurs with the log macro, or the level-specific convenience macros, + which write either directly or via an agent. By default direct logging is + disabled, but can be enabled via the *allow-direct-logging* boolean atom. If + logging is invoked within a transaction it will always use an agent. - The log macro will not evaluate its 'message' unless the specific logging + The log macros will not evaluate their 'message' unless the specific logging level is in effect. - Alternately, you can use the spy function when you have code that needs to be - evaluated, and also want to output its result to the debug log. + Alternately, you can use the spy macro when you have code that needs to be + evaluated, and also want to output the code and its result to the debug log. Unless otherwise specified, the current namespace (as identified by *ns*) will - be used as the log-name (similar to how the java class name is usually used). + be used as the log-ns (similar to how the java class name is usually used). - Use the enabled? function to write conditional code against the logging level + Use the enabled? function to write conditional code against the logging level (beyond simply whether or not to call log, which is handled automatically). - You can redirect all java writes of System.out and System.err to the log - system by calling log-capture!. To rebind *out* and *err* to the log system - invoke with-logs. In both cases a log-name (e.g., \"com.example.captured\") + You can redirect all java writes of System.out and System.err to the log + system by calling log-capture!. To rebind *out* and *err* to the log system + invoke with-logs. In both cases a log-ns (e.g., \"com.example.captured\") needs to be specified to namespace the output."} clojure.contrib.logging) (defstruct #^{:doc - "A struct to abstract the functionality common to all implementations. + "A struct to abstract the functionality common to all logging implementations. The keys are as follows: :name ; the name of the logging system used - :get-log ; fn [name] to obtain a log by string name + :get-log ; fn [log-ns] to obtain a log by string namespace :enabled? ; fn [log lvl] to check if a particular level is emabled :write ; fn [log lvl msg ex] to a log a message"} log-system @@ -58,12 +59,12 @@ (defmacro commons-logging "Creates a log-system struct using the Apache commons-logging API, - if present; otherwise nil. End-users should not need to invoke this macro." + if present, otherwise nil. End-users should not need to invoke this macro." [] (try (import (org.apache.commons.logging LogFactory Log)) - `(letfn [(get-log# [name#] - (LogFactory/getLog name#)) + `(letfn [(get-log# [log-ns#] + (LogFactory/getLog log-ns#)) (enabled?# [log# level#] (condp = level# :trace (.isTraceEnabled log#) @@ -85,7 +86,7 @@ (defmacro log4j-logging - "Creates a log-system struct using the log4j API, if present; otherwise nil. + "Creates a log-system struct using the log4j API, if present, otherwise nil. End-users should not need to invoke this macro." [] (try @@ -96,9 +97,9 @@ :warn Level/WARN :error Level/ERROR :fatal Level/FATAL}] - (letfn [(get-log# [name#] - (Logger/getLogger name#)) - (enabled?# [log# level#] + (letfn [(get-log# [log-ns#] + (Logger/getLogger log-ns#)) + (enabled?# [log# level#] (.isEnabledFor log# (levels# level#))) (write# [log# level# msg# e#] (if-not e# @@ -120,8 +121,8 @@ :warn Level/WARNING :error Level/SEVERE :fatal Level/SEVERE}] - (letfn [(get-log# [name#] - (Logger/getLogger name#)) + (letfn [(get-log# [log-ns#] + (Logger/getLogger log-ns#)) (enabled?# [log# level#] (.isLoggable log# (levels# level#))) (write# [log# level# msg# e#] @@ -135,57 +136,61 @@ (defn do-log "Logs the message immediately if the specific logging level is enabled. Use the log macro in preference to this function." - [system-ref level message throwable log-name] + [system-ref level message throwable log-ns] (let [system @system-ref - log ((system :get-log) log-name)] + log ((system :get-log) log-ns)] (if ((system :enabled?) log level) (do ((system :write) log level (force message) throwable) system-ref)))) (def #^{:doc - "The default log-system initialized to the first implementation found from: - Apache commons-logging, log4j, java.util.logging."} + "An atom holding the default log-system initialized to the first + implementation found from: Apache commons-logging, log4j, java.util.logging."} *log-system* - (atom (some eval ['(commons-logging) - '(log4j-logging) - '(java-logging)]))) + (atom (or (commons-logging) + (log4j-logging) + (java-logging) + (throw ; this should never happen in 1.5+ + (RuntimeException. + "Valid logging implementation could not be found."))))) (def #^{:doc - "The default agent referecing *log-system*."} + "The default agent referencing *log-system*."} *log-system-agent* (agent *log-system*)) (def #^{:doc - "A flag indicating wether logging can be directly (as opposed to via an agent) - when not operating from within a transaction. Defaults to false."} + "A boolean atom indicating whether direct logging (as opposed to via an agent) + is allowed when not operating from within a transaction. Defaults to false."} *allow-direct-logging* (atom false)) (defmacro log - "Logs a message, either directly or via an agent." + "Logs a message, either directly or via an agent. See also the level-specific + convenience macros." ([level message] `(log ~level ~message nil)) ([level message throwable] `(log ~level ~message ~throwable (str *ns*))) - ([level message throwable log-name] + ([level message throwable log-ns] `(if (and @*allow-direct-logging* (not (clojure.lang.LockingTransaction/isRunning))) - (do-log *log-system* ~level (delay ~message) ~throwable ~log-name) + (do-log *log-system* ~level (delay ~message) ~throwable ~log-ns) (send-off *log-system-agent* - do-log ~level (delay ~message) ~throwable ~log-name)))) + do-log ~level (delay ~message) ~throwable ~log-ns)))) (defn enabled? - "Returns true if the specific logging level is enabled. This function should - only be necessary if one needs to execute alternate code paths beyond whether - the log should be written to." + "Returns true if the specific logging level is enabled. Use of this function + should only be necessary if one needs to execute alternate code paths beyond + whether the log should be written to." ([level] (enabled? level (str *ns*))) - ([level log-name] + ([level log-ns] (let [sys @*log-system*] - ((sys :enabled?) ((sys :get-log) log-name) level)))) + ((sys :enabled?) ((sys :get-log) log-ns) level)))) (defmacro spy @@ -198,7 +203,7 @@ (defn log-stream "Creates a PrintStream that will output to the log. End-users should not need to invoke this function." - [level log-name] + [level log-ns] (java.io.PrintStream. (proxy [java.io.ByteArrayOutputStream] [] (flush [] @@ -206,25 +211,25 @@ (let [s (.trim (.toString this))] (proxy-super reset) (if (> (.length s) 0) - (log level s nil log-name))))) + (log level s nil log-ns))))) true)) (def #^{:doc - "Used by log-capture! to maintain a reference to the original System.out and - System.err streams."} + "A ref used by log-capture! to maintain a reference to the original System.out + and System.err streams."} *old-std-streams* (ref nil)) (defn log-capture! "Captures System.out and System.err, redirecting all writes of those streams - to :info and :error logging, respectively. The specified log-name value will + to :info and :error logging, respectively. The specified log-ns value will be used to namespace all redirected logging. NOTE: this will not redirect output of *out* or *err*; for that, use with-logs." - [log-name] + [log-ns] (dosync - (let [new-out (log-stream :info log-name) - new-err (log-stream :error log-name)] + (let [new-out (log-stream :info log-ns) + new-err (log-stream :error log-ns)] ; don't overwrite the original values (if (nil? @*old-std-streams*) (ref-set *old-std-streams* {:out System/out :err System/err})) @@ -233,7 +238,7 @@ (defn log-uncapture! - "Restores System/out and System/err to their original values." + "Restores System.out and System.err to their original values." [] (dosync (when-let [{old-out :out old-err :err} @*old-std-streams*] @@ -244,12 +249,55 @@ (defmacro with-logs "Evaluates exprs in a context in which *out* and *err* are bound to :info and - :error logging, respectively. The specified log-name value will be used to + :error logging, respectively. The specified log-ns value will be used to namespace all redirected logging." - [log-name & body] - (if (and log-name (seq body)) + [log-ns & body] + (if (and log-ns (seq body)) `(binding [*out* (java.io.OutputStreamWriter. - (log-stream :info ~log-name)) + (log-stream :info ~log-ns)) *err* (java.io.OutputStreamWriter. - (log-stream :error ~log-name))] + (log-stream :error ~log-ns))] ~@body))) + +(defmacro trace + "Logs a message at the trace level." + ([message] + `(log :trace ~message)) + ([message throwable] + `(log :trace ~message ~throwable))) + +(defmacro debug + "Logs a message at the debug level." + ([message] + `(log :debug ~message)) + ([message throwable] + `(log :debug ~message ~throwable))) + +(defmacro info + "Logs a message at the info level." + ([message] + `(log :info ~message)) + ([message throwable] + `(log :info ~message ~throwable))) + +(defmacro warn + "Logs a message at the warn level." + ([message] + `(log :warn ~message)) + ([message throwable] + `(log :warn ~message ~throwable))) + +(defmacro error + "Logs a message at the error level." + ([message] + `(log :error ~message)) + ([message throwable] + `(log :error ~message ~throwable))) + +(defmacro fatal + "Logs a message at the fatal level." + ([message] + `(log :fatal ~message)) + ([message throwable] + `(log :fatal ~message ~throwable))) + |