aboutsummaryrefslogtreecommitdiff
path: root/src/clojure/contrib/javalog.clj
blob: 05c30d4bc54e0b001b4f21bb1d7131a4bbfe6c30 (plain)
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))))