Mercurial > lasercutter
diff src/clojure/contrib/logging.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/clojure/contrib/logging.clj Sat Aug 21 06:25:44 2010 -0400 1.3 @@ -0,0 +1,343 @@ 1.4 +;;; logging.clj -- delegated logging for Clojure 1.5 + 1.6 +;; by Alex Taggart 1.7 +;; July 27, 2009 1.8 + 1.9 +;; Copyright (c) Alex Taggart, July 2009. All rights reserved. The use 1.10 +;; and distribution terms for this software are covered by the Eclipse 1.11 +;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 1.12 +;; which can be found in the file epl-v10.html at the root of this 1.13 +;; distribution. By using this software in any fashion, you are 1.14 +;; agreeing to be bound by the terms of this license. You must not 1.15 +;; remove this notice, or any other, from this software. 1.16 +(ns 1.17 + ^{:author "Alex Taggart, Timothy Pratley", 1.18 + :doc 1.19 + "Logging macros which delegate to a specific logging implementation. At 1.20 + runtime a specific implementation is selected from, in order, Apache 1.21 + commons-logging, log4j, and finally java.util.logging. 1.22 + 1.23 + Logging levels are specified by clojure keywords corresponding to the 1.24 + values used in log4j and commons-logging: 1.25 + :trace, :debug, :info, :warn, :error, :fatal 1.26 + 1.27 + Logging occurs with the log macro, or the level-specific convenience macros, 1.28 + which write either directly or via an agent. For performance reasons, direct 1.29 + logging is enabled by default, but setting the *allow-direct-logging* boolean 1.30 + atom to false will disable it. If logging is invoked within a transaction it 1.31 + will always use an agent. 1.32 + 1.33 + The log macros will not evaluate their 'message' unless the specific logging 1.34 + level is in effect. Alternately, you can use the spy macro when you have code 1.35 + that needs to be evaluated, and also want to output the code and its result to 1.36 + the debug log. 1.37 + 1.38 + Unless otherwise specified, the current namespace (as identified by *ns*) will 1.39 + be used as the log-ns (similar to how the java class name is usually used). 1.40 + Note: your log configuration should display the name that was passed to the 1.41 + logging implementation, and not perform stack-inspection, otherwise you'll see 1.42 + something like \"fn__72$impl_write_BANG__39__auto____81\" in your logs. 1.43 + 1.44 + Use the enabled? macro to write conditional code against the logging level 1.45 + (beyond simply whether or not to call log, which is handled automatically). 1.46 + 1.47 + You can redirect all java writes of System.out and System.err to the log 1.48 + system by calling log-capture!. To rebind *out* and *err* to the log system 1.49 + invoke with-logs. In both cases a log-ns (e.g., \"com.example.captured\") 1.50 + needs to be specified to namespace the output."} 1.51 + clojure.contrib.logging) 1.52 + 1.53 +(declare *impl-name* impl-get-log impl-enabled? impl-write!) 1.54 + 1.55 +;; Macros used so that implementation-specific functions all have the same meta. 1.56 + 1.57 +(defmacro def-impl-name 1.58 + {:private true} [& body] 1.59 + `(def 1.60 + ^{:doc "The name of the logging implementation used."} 1.61 + *impl-name* 1.62 + ~@body)) 1.63 + 1.64 +(defmacro def-impl-get-log 1.65 + {:private true} [& body] 1.66 + `(def 1.67 + ^{:doc 1.68 + "Returns an implementation-specific log by string namespace. End-users should 1.69 + not need to call this." 1.70 + :arglist '([~'log-ns])} 1.71 + impl-get-log 1.72 + (memoize ~@body))) 1.73 + 1.74 +(defmacro def-impl-enabled? 1.75 + {:private true} [& body] 1.76 + `(def 1.77 + ^{:doc 1.78 + "Implementation-specific check if a particular level is enabled. End-users 1.79 + should not need to call this." 1.80 + :arglist '([~'log ~'level])} 1.81 + impl-enabled? 1.82 + ~@body)) 1.83 + 1.84 +(defmacro def-impl-write! 1.85 + {:private true} [& body] 1.86 + `(def 1.87 + ^{:doc 1.88 + "Implementation-specific write of a log message. End-users should not need to 1.89 + call this." 1.90 + :arglist '([~'log ~'level ~'message ~'throwable])} 1.91 + impl-write! 1.92 + ~@body)) 1.93 + 1.94 +(defn- commons-logging 1.95 + "Defines the commons-logging-based implementations of the core logging 1.96 + functions. End-users should never need to call this." 1.97 + [] 1.98 + (try 1.99 + (import (org.apache.commons.logging LogFactory Log)) 1.100 + (eval 1.101 + `(do 1.102 + (def-impl-name "org.apache.commons.logging") 1.103 + (def-impl-get-log 1.104 + (fn [log-ns#] 1.105 + (org.apache.commons.logging.LogFactory/getLog ^String log-ns#))) 1.106 + (def-impl-enabled? 1.107 + (fn [^org.apache.commons.logging.Log log# level#] 1.108 + (condp = level# 1.109 + :trace (.isTraceEnabled log#) 1.110 + :debug (.isDebugEnabled log#) 1.111 + :info (.isInfoEnabled log#) 1.112 + :warn (.isWarnEnabled log#) 1.113 + :error (.isErrorEnabled log#) 1.114 + :fatal (.isFatalEnabled log#)))) 1.115 + (def-impl-write! 1.116 + (fn [^org.apache.commons.logging.Log log# level# msg# e#] 1.117 + (condp = level# 1.118 + :trace (.trace log# msg# e#) 1.119 + :debug (.debug log# msg# e#) 1.120 + :info (.info log# msg# e#) 1.121 + :warn (.warn log# msg# e#) 1.122 + :error (.error log# msg# e#) 1.123 + :fatal (.fatal log# msg# e#)))) 1.124 + true)) 1.125 + (catch Exception e nil))) 1.126 + 1.127 + 1.128 +(defn- log4j-logging 1.129 + "Defines the log4j-based implementations of the core logging functions. 1.130 + End-users should never need to call this." 1.131 + [] 1.132 + (try 1.133 + (import (org.apache.log4j Logger Level)) 1.134 + (eval 1.135 + '(do 1.136 + (def-impl-name "org.apache.log4j") 1.137 + (def-impl-get-log 1.138 + (fn [log-ns#] 1.139 + (org.apache.log4j.Logger/getLogger ^String log-ns#))) 1.140 + (let [levels# {:trace org.apache.log4j.Level/TRACE 1.141 + :debug org.apache.log4j.Level/DEBUG 1.142 + :info org.apache.log4j.Level/INFO 1.143 + :warn org.apache.log4j.Level/WARN 1.144 + :error org.apache.log4j.Level/ERROR 1.145 + :fatal org.apache.log4j.Level/FATAL}] 1.146 + (def-impl-enabled? 1.147 + (fn [^org.apache.log4j.Logger log# level#] 1.148 + (.isEnabledFor log# (levels# level#)))) 1.149 + (def-impl-write! 1.150 + (fn [^org.apache.log4j.Logger log# level# msg# e#] 1.151 + (if-not e# 1.152 + (.log log# (levels# level#) msg#) 1.153 + (.log log# (levels# level#) msg# e#))))) 1.154 + true)) 1.155 + (catch Exception e nil))) 1.156 + 1.157 + 1.158 +(defn- java-logging 1.159 + "Defines the java-logging-based implementations of the core logging 1.160 + functions. End-users should never need to call this." 1.161 + [] 1.162 + (try 1.163 + (import (java.util.logging Logger Level)) 1.164 + (eval 1.165 + `(do 1.166 + (def-impl-name "java.util.logging") 1.167 + (def-impl-get-log 1.168 + (fn [log-ns#] 1.169 + (java.util.logging.Logger/getLogger log-ns#))) 1.170 + (let [levels# {:trace java.util.logging.Level/FINEST 1.171 + :debug java.util.logging.Level/FINE 1.172 + :info java.util.logging.Level/INFO 1.173 + :warn java.util.logging.Level/WARNING 1.174 + :error java.util.logging.Level/SEVERE 1.175 + :fatal java.util.logging.Level/SEVERE}] 1.176 + (def-impl-enabled? 1.177 + (fn [^java.util.logging.Logger log# level#] 1.178 + (.isLoggable log# (levels# level#)))) 1.179 + (def-impl-write! 1.180 + (fn [^java.util.logging.Logger log# level# msg# e#] 1.181 + (if-not e# 1.182 + (.log log# ^java.util.logging.Level (levels# level#) 1.183 + ^String (str msg#)) 1.184 + (.log log# ^java.util.logging.Level (levels# level#) 1.185 + ^String (str msg#) ^Throwable e#))))) 1.186 + true)) 1.187 + (catch Exception e nil))) 1.188 + 1.189 + 1.190 +;; Initialize implementation-specific functions 1.191 +(or (commons-logging) 1.192 + (log4j-logging) 1.193 + (java-logging) 1.194 + (throw ; this should never happen in 1.5+ 1.195 + (RuntimeException. 1.196 + "Valid logging implementation could not be found."))) 1.197 + 1.198 + 1.199 +(def ^{:doc 1.200 + "The default agent used for performing logging durng a transaction or when 1.201 + direct logging is disabled."} 1.202 + *logging-agent* (agent nil)) 1.203 + 1.204 + 1.205 +(def ^{:doc 1.206 + "A boolean indicating whether direct logging (as opposed to via an agent) is 1.207 + allowed when not operating from within a transaction. Defaults to true."} 1.208 + *allow-direct-logging* (atom true)) 1.209 + 1.210 + 1.211 +(defmacro log 1.212 + "Logs a message, either directly or via an agent. Also see the level-specific 1.213 + convenience macros." 1.214 + ([level message] 1.215 + `(log ~level ~message nil)) 1.216 + ([level message throwable] 1.217 + `(log ~level ~message ~throwable ~(str *ns*))) 1.218 + ([level message throwable log-ns] 1.219 + `(let [log# (impl-get-log ~log-ns)] 1.220 + (if (impl-enabled? log# ~level) 1.221 + (if (and @*allow-direct-logging* 1.222 + (not (clojure.lang.LockingTransaction/isRunning))) 1.223 + (impl-write! log# ~level ~message ~throwable) 1.224 + (send-off *logging-agent* 1.225 + (fn [_# l# v# m# t#] (impl-write! l# v# m# t#)) 1.226 + log# ~level ~message ~throwable)))))) 1.227 + 1.228 + 1.229 +(defmacro enabled? 1.230 + "Returns true if the specific logging level is enabled. Use of this function 1.231 + should only be necessary if one needs to execute alternate code paths beyond 1.232 + whether the log should be written to." 1.233 + ([level] 1.234 + `(enabled? ~level ~(str *ns*))) 1.235 + ([level log-ns] 1.236 + `(impl-enabled? (impl-get-log ~log-ns) ~level))) 1.237 + 1.238 + 1.239 +(defmacro spy 1.240 + "Evaluates expr and outputs the form and its result to the debug log; returns 1.241 + the result of expr." 1.242 + [expr] 1.243 + `(let [a# ~expr] (log :debug (str '~expr " => " a#)) a#)) 1.244 + 1.245 + 1.246 +(defn log-stream 1.247 + "Creates a PrintStream that will output to the log. End-users should not need 1.248 + to invoke this." 1.249 + [level log-ns] 1.250 + (java.io.PrintStream. 1.251 + (proxy [java.io.ByteArrayOutputStream] [] 1.252 + (flush [] 1.253 + (proxy-super flush) 1.254 + (let [s (.trim (.toString ^java.io.ByteArrayOutputStream this))] 1.255 + (proxy-super reset) 1.256 + (if (> (.length s) 0) 1.257 + (log level s nil log-ns))))) 1.258 + true)) 1.259 + 1.260 + 1.261 +(def ^{:doc 1.262 + "A ref used by log-capture! to maintain a reference to the original System.out 1.263 + and System.err streams." 1.264 + :private true} 1.265 + *old-std-streams* (ref nil)) 1.266 + 1.267 + 1.268 +(defn log-capture! 1.269 + "Captures System.out and System.err, redirecting all writes of those streams 1.270 + to :info and :error logging, respectively. The specified log-ns value will 1.271 + be used to namespace all redirected logging. NOTE: this will not redirect 1.272 + output of *out* or *err*; for that, use with-logs." 1.273 + [log-ns] 1.274 + (dosync 1.275 + (let [new-out (log-stream :info log-ns) 1.276 + new-err (log-stream :error log-ns)] 1.277 + ; don't overwrite the original values 1.278 + (if (nil? @*old-std-streams*) 1.279 + (ref-set *old-std-streams* {:out System/out :err System/err})) 1.280 + (System/setOut new-out) 1.281 + (System/setErr new-err)))) 1.282 + 1.283 + 1.284 +(defn log-uncapture! 1.285 + "Restores System.out and System.err to their original values." 1.286 + [] 1.287 + (dosync 1.288 + (when-let [{old-out :out old-err :err} @*old-std-streams*] 1.289 + (ref-set *old-std-streams* nil) 1.290 + (System/setOut old-out) 1.291 + (System/setErr old-err)))) 1.292 + 1.293 + 1.294 +(defmacro with-logs 1.295 + "Evaluates exprs in a context in which *out* and *err* are bound to :info and 1.296 + :error logging, respectively. The specified log-ns value will be used to 1.297 + namespace all redirected logging." 1.298 + [log-ns & body] 1.299 + (if (and log-ns (seq body)) 1.300 + `(binding [*out* (java.io.OutputStreamWriter. 1.301 + (log-stream :info ~log-ns)) 1.302 + *err* (java.io.OutputStreamWriter. 1.303 + (log-stream :error ~log-ns))] 1.304 + ~@body))) 1.305 + 1.306 +(defmacro trace 1.307 + "Logs a message at the trace level." 1.308 + ([message] 1.309 + `(log :trace ~message)) 1.310 + ([message throwable] 1.311 + `(log :trace ~message ~throwable))) 1.312 + 1.313 +(defmacro debug 1.314 + "Logs a message at the debug level." 1.315 + ([message] 1.316 + `(log :debug ~message)) 1.317 + ([message throwable] 1.318 + `(log :debug ~message ~throwable))) 1.319 + 1.320 +(defmacro info 1.321 + "Logs a message at the info level." 1.322 + ([message] 1.323 + `(log :info ~message)) 1.324 + ([message throwable] 1.325 + `(log :info ~message ~throwable))) 1.326 + 1.327 +(defmacro warn 1.328 + "Logs a message at the warn level." 1.329 + ([message] 1.330 + `(log :warn ~message)) 1.331 + ([message throwable] 1.332 + `(log :warn ~message ~throwable))) 1.333 + 1.334 +(defmacro error 1.335 + "Logs a message at the error level." 1.336 + ([message] 1.337 + `(log :error ~message)) 1.338 + ([message throwable] 1.339 + `(log :error ~message ~throwable))) 1.340 + 1.341 +(defmacro fatal 1.342 + "Logs a message at the fatal level." 1.343 + ([message] 1.344 + `(log :fatal ~message)) 1.345 + ([message throwable] 1.346 + `(log :fatal ~message ~throwable)))