1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
;;; javalog.clj -- convenient access to java.util.logging in Clojure
;; by Stuart Sierra <mail@stuartsierra.com>
;; April 8, 2008
;; Copyright (c) 2008 Stuart Sierra. All rights reserved. The use and
;; distribution terms for this software are covered by the Common
;; Public License 1.0 (http://www.opensource.org/licenses/cpl1.0.php)
;; which can be found in the file CPL.TXT at the root of this
;; distribution. By using this software in any fashion, you are
;; agreeing to be bound by the terms of this license. You must not
;; remove this notice, or any other, from this software.
;; This file defines some convenience functions for using the Java
;; logging framework from Clojure. It is oriented towards simple
;; development and debugging rather than complex production
;; environments.
;; NOTES ADDED December 3, 2008
;;
;; This uses Logger.GLOBAL_LOGGER_NAME, which was introduced in
;; Java 6. This library will not work on Java 5.
;;
;; With improved syntax for calling Java from Clojure such as
;; "(.method object)" and "ClassName/staticMember", this library is
;; less useful than it once was. Consider it DEPRECATED. I now
;; recommend using the Java logging API directly instead of this
;; library. Better yet, use Apache Commons Logging,
;; <http://commons.apache.org/logging/>, which has a nicer API.
(ns clojure.contrib.javalog
(:import
(java.util.logging Logger Level ConsoleHandler
FileHandler SimpleFormatter)))
(def
#^{:tag Logger
:doc "The current java.util.logging.Logger. By default, the
global logger, modified by 'with-logger'."}
*logger*
(. Logger
(getLogger
(. Logger GLOBAL_LOGGER_NAME))))
(defmacro log-level
"Translates 'level' (a lower-case keyword) into a static field of
java.util.logging.Level, by name.
Example: (log-level :severe) => java.util.logging.Level.SEVERE
If 'level' is not a keyword, it is assumed to be a user-defined
instance of java.util.logging.Level and is returned unchanged."
[level]
(if (keyword? level)
`(. java.util.logging.Level
~(symbol (. (name level) (toUpperCase))))
level))
(defn root-logger
"Returns the root Logger instance."
([] (root-logger *logger*))
([logger] (let [parent (. logger (getParent))]
(if parent
(recur parent)
logger))))
(defn set-console-log-level
"Attempts to set the level of the current logger and the root
ConsoleHandler to 'level' (a java.util.logging.Level). Useful for
debugging at the REPL."
[level]
(let [console-handler
(some (fn [h] (if (instance? ConsoleHandler h) h))
(. (root-logger) (getHandlers)))]
(if console-handler
(do (. *logger* (setLevel level))
(. console-handler (setLevel level)))
(throw (new Exception "No ConsoleHandler on root logger.")))))
(defn add-log-file
"Attaches a log file, using SimpleFormatter, with the given level,
to the named logger. 'level' defaults to ALL. Note: multiple
invocations will create multiple log files, with numbers appended to
the names."
([logger-name filename]
(add-log-file logger-name filename (. Level ALL)))
([logger-name filename level]
(let [logger (. Logger (getLogger logger-name))
handler (new FileHandler filename)]
(. handler (setFormatter (new SimpleFormatter)))
(. handler (setLevel level))
(. logger (addHandler handler)))))
(defmacro with-logger
"Executes 'body' with *logger* bound to a logger with the given name
and level. 'level' is expanded with 'log-level'."
[logger-name level & body]
`(binding [*logger* (. Logger (getLogger ~logger-name))]
(. *logger* (setLevel (log-level ~level)))
~@body))
(defmacro log
"Logs a message to *logger*. 'level' is expanded with 'log-level'.
Example: (log :severe \"Bad argument: \" object)"
[level & strings]
`(. *logger* (log (log-level ~level) (str ~@strings))))
|