Mercurial > lasercutter
comparison 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 |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 ;;; logging.clj -- delegated logging for Clojure | |
2 | |
3 ;; by Alex Taggart | |
4 ;; July 27, 2009 | |
5 | |
6 ;; Copyright (c) Alex Taggart, July 2009. All rights reserved. The use | |
7 ;; and distribution terms for this software are covered by the Eclipse | |
8 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) | |
9 ;; which can be found in the file epl-v10.html at the root of this | |
10 ;; distribution. By using this software in any fashion, you are | |
11 ;; agreeing to be bound by the terms of this license. You must not | |
12 ;; remove this notice, or any other, from this software. | |
13 (ns | |
14 ^{:author "Alex Taggart, Timothy Pratley", | |
15 :doc | |
16 "Logging macros which delegate to a specific logging implementation. At | |
17 runtime a specific implementation is selected from, in order, Apache | |
18 commons-logging, log4j, and finally java.util.logging. | |
19 | |
20 Logging levels are specified by clojure keywords corresponding to the | |
21 values used in log4j and commons-logging: | |
22 :trace, :debug, :info, :warn, :error, :fatal | |
23 | |
24 Logging occurs with the log macro, or the level-specific convenience macros, | |
25 which write either directly or via an agent. For performance reasons, direct | |
26 logging is enabled by default, but setting the *allow-direct-logging* boolean | |
27 atom to false will disable it. If logging is invoked within a transaction it | |
28 will always use an agent. | |
29 | |
30 The log macros will not evaluate their 'message' unless the specific logging | |
31 level is in effect. Alternately, you can use the spy macro when you have code | |
32 that needs to be evaluated, and also want to output the code and its result to | |
33 the debug log. | |
34 | |
35 Unless otherwise specified, the current namespace (as identified by *ns*) will | |
36 be used as the log-ns (similar to how the java class name is usually used). | |
37 Note: your log configuration should display the name that was passed to the | |
38 logging implementation, and not perform stack-inspection, otherwise you'll see | |
39 something like \"fn__72$impl_write_BANG__39__auto____81\" in your logs. | |
40 | |
41 Use the enabled? macro to write conditional code against the logging level | |
42 (beyond simply whether or not to call log, which is handled automatically). | |
43 | |
44 You can redirect all java writes of System.out and System.err to the log | |
45 system by calling log-capture!. To rebind *out* and *err* to the log system | |
46 invoke with-logs. In both cases a log-ns (e.g., \"com.example.captured\") | |
47 needs to be specified to namespace the output."} | |
48 clojure.contrib.logging) | |
49 | |
50 (declare *impl-name* impl-get-log impl-enabled? impl-write!) | |
51 | |
52 ;; Macros used so that implementation-specific functions all have the same meta. | |
53 | |
54 (defmacro def-impl-name | |
55 {:private true} [& body] | |
56 `(def | |
57 ^{:doc "The name of the logging implementation used."} | |
58 *impl-name* | |
59 ~@body)) | |
60 | |
61 (defmacro def-impl-get-log | |
62 {:private true} [& body] | |
63 `(def | |
64 ^{:doc | |
65 "Returns an implementation-specific log by string namespace. End-users should | |
66 not need to call this." | |
67 :arglist '([~'log-ns])} | |
68 impl-get-log | |
69 (memoize ~@body))) | |
70 | |
71 (defmacro def-impl-enabled? | |
72 {:private true} [& body] | |
73 `(def | |
74 ^{:doc | |
75 "Implementation-specific check if a particular level is enabled. End-users | |
76 should not need to call this." | |
77 :arglist '([~'log ~'level])} | |
78 impl-enabled? | |
79 ~@body)) | |
80 | |
81 (defmacro def-impl-write! | |
82 {:private true} [& body] | |
83 `(def | |
84 ^{:doc | |
85 "Implementation-specific write of a log message. End-users should not need to | |
86 call this." | |
87 :arglist '([~'log ~'level ~'message ~'throwable])} | |
88 impl-write! | |
89 ~@body)) | |
90 | |
91 (defn- commons-logging | |
92 "Defines the commons-logging-based implementations of the core logging | |
93 functions. End-users should never need to call this." | |
94 [] | |
95 (try | |
96 (import (org.apache.commons.logging LogFactory Log)) | |
97 (eval | |
98 `(do | |
99 (def-impl-name "org.apache.commons.logging") | |
100 (def-impl-get-log | |
101 (fn [log-ns#] | |
102 (org.apache.commons.logging.LogFactory/getLog ^String log-ns#))) | |
103 (def-impl-enabled? | |
104 (fn [^org.apache.commons.logging.Log log# level#] | |
105 (condp = level# | |
106 :trace (.isTraceEnabled log#) | |
107 :debug (.isDebugEnabled log#) | |
108 :info (.isInfoEnabled log#) | |
109 :warn (.isWarnEnabled log#) | |
110 :error (.isErrorEnabled log#) | |
111 :fatal (.isFatalEnabled log#)))) | |
112 (def-impl-write! | |
113 (fn [^org.apache.commons.logging.Log log# level# msg# e#] | |
114 (condp = level# | |
115 :trace (.trace log# msg# e#) | |
116 :debug (.debug log# msg# e#) | |
117 :info (.info log# msg# e#) | |
118 :warn (.warn log# msg# e#) | |
119 :error (.error log# msg# e#) | |
120 :fatal (.fatal log# msg# e#)))) | |
121 true)) | |
122 (catch Exception e nil))) | |
123 | |
124 | |
125 (defn- log4j-logging | |
126 "Defines the log4j-based implementations of the core logging functions. | |
127 End-users should never need to call this." | |
128 [] | |
129 (try | |
130 (import (org.apache.log4j Logger Level)) | |
131 (eval | |
132 '(do | |
133 (def-impl-name "org.apache.log4j") | |
134 (def-impl-get-log | |
135 (fn [log-ns#] | |
136 (org.apache.log4j.Logger/getLogger ^String log-ns#))) | |
137 (let [levels# {:trace org.apache.log4j.Level/TRACE | |
138 :debug org.apache.log4j.Level/DEBUG | |
139 :info org.apache.log4j.Level/INFO | |
140 :warn org.apache.log4j.Level/WARN | |
141 :error org.apache.log4j.Level/ERROR | |
142 :fatal org.apache.log4j.Level/FATAL}] | |
143 (def-impl-enabled? | |
144 (fn [^org.apache.log4j.Logger log# level#] | |
145 (.isEnabledFor log# (levels# level#)))) | |
146 (def-impl-write! | |
147 (fn [^org.apache.log4j.Logger log# level# msg# e#] | |
148 (if-not e# | |
149 (.log log# (levels# level#) msg#) | |
150 (.log log# (levels# level#) msg# e#))))) | |
151 true)) | |
152 (catch Exception e nil))) | |
153 | |
154 | |
155 (defn- java-logging | |
156 "Defines the java-logging-based implementations of the core logging | |
157 functions. End-users should never need to call this." | |
158 [] | |
159 (try | |
160 (import (java.util.logging Logger Level)) | |
161 (eval | |
162 `(do | |
163 (def-impl-name "java.util.logging") | |
164 (def-impl-get-log | |
165 (fn [log-ns#] | |
166 (java.util.logging.Logger/getLogger log-ns#))) | |
167 (let [levels# {:trace java.util.logging.Level/FINEST | |
168 :debug java.util.logging.Level/FINE | |
169 :info java.util.logging.Level/INFO | |
170 :warn java.util.logging.Level/WARNING | |
171 :error java.util.logging.Level/SEVERE | |
172 :fatal java.util.logging.Level/SEVERE}] | |
173 (def-impl-enabled? | |
174 (fn [^java.util.logging.Logger log# level#] | |
175 (.isLoggable log# (levels# level#)))) | |
176 (def-impl-write! | |
177 (fn [^java.util.logging.Logger log# level# msg# e#] | |
178 (if-not e# | |
179 (.log log# ^java.util.logging.Level (levels# level#) | |
180 ^String (str msg#)) | |
181 (.log log# ^java.util.logging.Level (levels# level#) | |
182 ^String (str msg#) ^Throwable e#))))) | |
183 true)) | |
184 (catch Exception e nil))) | |
185 | |
186 | |
187 ;; Initialize implementation-specific functions | |
188 (or (commons-logging) | |
189 (log4j-logging) | |
190 (java-logging) | |
191 (throw ; this should never happen in 1.5+ | |
192 (RuntimeException. | |
193 "Valid logging implementation could not be found."))) | |
194 | |
195 | |
196 (def ^{:doc | |
197 "The default agent used for performing logging durng a transaction or when | |
198 direct logging is disabled."} | |
199 *logging-agent* (agent nil)) | |
200 | |
201 | |
202 (def ^{:doc | |
203 "A boolean indicating whether direct logging (as opposed to via an agent) is | |
204 allowed when not operating from within a transaction. Defaults to true."} | |
205 *allow-direct-logging* (atom true)) | |
206 | |
207 | |
208 (defmacro log | |
209 "Logs a message, either directly or via an agent. Also see the level-specific | |
210 convenience macros." | |
211 ([level message] | |
212 `(log ~level ~message nil)) | |
213 ([level message throwable] | |
214 `(log ~level ~message ~throwable ~(str *ns*))) | |
215 ([level message throwable log-ns] | |
216 `(let [log# (impl-get-log ~log-ns)] | |
217 (if (impl-enabled? log# ~level) | |
218 (if (and @*allow-direct-logging* | |
219 (not (clojure.lang.LockingTransaction/isRunning))) | |
220 (impl-write! log# ~level ~message ~throwable) | |
221 (send-off *logging-agent* | |
222 (fn [_# l# v# m# t#] (impl-write! l# v# m# t#)) | |
223 log# ~level ~message ~throwable)))))) | |
224 | |
225 | |
226 (defmacro enabled? | |
227 "Returns true if the specific logging level is enabled. Use of this function | |
228 should only be necessary if one needs to execute alternate code paths beyond | |
229 whether the log should be written to." | |
230 ([level] | |
231 `(enabled? ~level ~(str *ns*))) | |
232 ([level log-ns] | |
233 `(impl-enabled? (impl-get-log ~log-ns) ~level))) | |
234 | |
235 | |
236 (defmacro spy | |
237 "Evaluates expr and outputs the form and its result to the debug log; returns | |
238 the result of expr." | |
239 [expr] | |
240 `(let [a# ~expr] (log :debug (str '~expr " => " a#)) a#)) | |
241 | |
242 | |
243 (defn log-stream | |
244 "Creates a PrintStream that will output to the log. End-users should not need | |
245 to invoke this." | |
246 [level log-ns] | |
247 (java.io.PrintStream. | |
248 (proxy [java.io.ByteArrayOutputStream] [] | |
249 (flush [] | |
250 (proxy-super flush) | |
251 (let [s (.trim (.toString ^java.io.ByteArrayOutputStream this))] | |
252 (proxy-super reset) | |
253 (if (> (.length s) 0) | |
254 (log level s nil log-ns))))) | |
255 true)) | |
256 | |
257 | |
258 (def ^{:doc | |
259 "A ref used by log-capture! to maintain a reference to the original System.out | |
260 and System.err streams." | |
261 :private true} | |
262 *old-std-streams* (ref nil)) | |
263 | |
264 | |
265 (defn log-capture! | |
266 "Captures System.out and System.err, redirecting all writes of those streams | |
267 to :info and :error logging, respectively. The specified log-ns value will | |
268 be used to namespace all redirected logging. NOTE: this will not redirect | |
269 output of *out* or *err*; for that, use with-logs." | |
270 [log-ns] | |
271 (dosync | |
272 (let [new-out (log-stream :info log-ns) | |
273 new-err (log-stream :error log-ns)] | |
274 ; don't overwrite the original values | |
275 (if (nil? @*old-std-streams*) | |
276 (ref-set *old-std-streams* {:out System/out :err System/err})) | |
277 (System/setOut new-out) | |
278 (System/setErr new-err)))) | |
279 | |
280 | |
281 (defn log-uncapture! | |
282 "Restores System.out and System.err to their original values." | |
283 [] | |
284 (dosync | |
285 (when-let [{old-out :out old-err :err} @*old-std-streams*] | |
286 (ref-set *old-std-streams* nil) | |
287 (System/setOut old-out) | |
288 (System/setErr old-err)))) | |
289 | |
290 | |
291 (defmacro with-logs | |
292 "Evaluates exprs in a context in which *out* and *err* are bound to :info and | |
293 :error logging, respectively. The specified log-ns value will be used to | |
294 namespace all redirected logging." | |
295 [log-ns & body] | |
296 (if (and log-ns (seq body)) | |
297 `(binding [*out* (java.io.OutputStreamWriter. | |
298 (log-stream :info ~log-ns)) | |
299 *err* (java.io.OutputStreamWriter. | |
300 (log-stream :error ~log-ns))] | |
301 ~@body))) | |
302 | |
303 (defmacro trace | |
304 "Logs a message at the trace level." | |
305 ([message] | |
306 `(log :trace ~message)) | |
307 ([message throwable] | |
308 `(log :trace ~message ~throwable))) | |
309 | |
310 (defmacro debug | |
311 "Logs a message at the debug level." | |
312 ([message] | |
313 `(log :debug ~message)) | |
314 ([message throwable] | |
315 `(log :debug ~message ~throwable))) | |
316 | |
317 (defmacro info | |
318 "Logs a message at the info level." | |
319 ([message] | |
320 `(log :info ~message)) | |
321 ([message throwable] | |
322 `(log :info ~message ~throwable))) | |
323 | |
324 (defmacro warn | |
325 "Logs a message at the warn level." | |
326 ([message] | |
327 `(log :warn ~message)) | |
328 ([message throwable] | |
329 `(log :warn ~message ~throwable))) | |
330 | |
331 (defmacro error | |
332 "Logs a message at the error level." | |
333 ([message] | |
334 `(log :error ~message)) | |
335 ([message throwable] | |
336 `(log :error ~message ~throwable))) | |
337 | |
338 (defmacro fatal | |
339 "Logs a message at the fatal level." | |
340 ([message] | |
341 `(log :fatal ~message)) | |
342 ([message throwable] | |
343 `(log :fatal ~message ~throwable))) |