Skip to content

Commit 47be5ad

Browse files
Alexander Taggartataggart
authored andcommitted
Change how to find classes, now 100% test coverage
Use the context class loader of the current thread. Clojure internals seem to use this approach, and it allows for testing the impl factory functions.
1 parent 7c6a836 commit 47be5ad

4 files changed

Lines changed: 131 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99
### Added
1010
- Add support for testing logs in `clojure.tools.logging.test`
1111

12+
### Changed
13+
- Now passes the context classloader of current thread to `Class/forName` when
14+
determining whether logging implementation classes are available on the
15+
classpath. This was done to allow testing of the various `impl/*-factory`
16+
functions, and seems consistent with clojure internals.
17+
1218
## [0.4.1] - 2018-05-07
1319
### Fixed
1420
- Fix inadvertent reflection when using log4j2.

project.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
:source-paths ["src/main/clojure"]
77
:test-paths ["src/test/clojure"]
88
:dependencies [[org.clojure/clojure "1.3.0"]]
9-
:aliases {"cloverage" ["with-profile" "dev,cloverage" "cloverage"]}
9+
:aliases {"cloverage" ["with-profile" "dev,cloverage" "cloverage" "--fail-threshold" "100"]}
1010
:profiles {:cloverage {:plugins [[lein-cloverage "1.0.9"]]}
1111
:dev {:dependencies [[org.clojure/clojure "1.8.0"]
1212
[org.clojure/test.check "0.9.0"]

src/main/clojure/clojure/tools/logging/impl.clj

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,22 @@
4444
(name [_] "disabled")
4545
(get-logger [_ _] disabled-logger)))
4646

47+
(defn class-found?
48+
"Returns true if the Class associated with the given classname can be found
49+
using the context ClassLoader for the current thread."
50+
[name]
51+
(try
52+
(Class/forName name true (.. Thread currentThread getContextClassLoader))
53+
true
54+
(catch ClassNotFoundException _
55+
false)))
56+
57+
4758
(defn slf4j-factory
4859
"Returns a SLF4J-based implementation of the LoggerFactory protocol, or nil if
4960
not available."
5061
[]
51-
(try
52-
(Class/forName "org.slf4j.Logger")
62+
(when (class-found? "org.slf4j.Logger")
5363
(eval
5464
`(do
5565
(extend org.slf4j.Logger
@@ -88,15 +98,13 @@
8898
(name [_#]
8999
"org.slf4j")
90100
(get-logger [_# logger-ns#]
91-
(org.slf4j.LoggerFactory/getLogger ^String (str logger-ns#))))))
92-
(catch Exception e nil)))
101+
(org.slf4j.LoggerFactory/getLogger ^String (str logger-ns#))))))))
93102

94103
(defn cl-factory
95104
"Returns a Commons Logging-based implementation of the LoggerFactory protocol, or
96105
nil if not available."
97106
[]
98-
(try
99-
(Class/forName "org.apache.commons.logging.Log")
107+
(when (class-found? "org.apache.commons.logging.Log")
100108
(eval
101109
`(do
102110
(extend org.apache.commons.logging.Log
@@ -134,15 +142,13 @@
134142
(name [_#]
135143
"org.apache.commons.logging")
136144
(get-logger [_# logger-ns#]
137-
(org.apache.commons.logging.LogFactory/getLog (str logger-ns#))))))
138-
(catch Exception e nil)))
145+
(org.apache.commons.logging.LogFactory/getLog (str logger-ns#))))))))
139146

140147
(defn log4j-factory
141148
"Returns a Log4j-based implementation of the LoggerFactory protocol, or nil if
142149
not available."
143150
[]
144-
(try
145-
(Class/forName "org.apache.log4j.Logger")
151+
(when (class-found? "org.apache.log4j.Logger")
146152
(eval
147153
`(let [levels# {:trace org.apache.log4j.Level/TRACE
148154
:debug org.apache.log4j.Level/DEBUG
@@ -165,15 +171,13 @@
165171
(name [_#]
166172
"org.apache.log4j")
167173
(get-logger [_# logger-ns#]
168-
(org.apache.log4j.Logger/getLogger ^String (str logger-ns#))))))
169-
(catch Exception e nil)))
174+
(org.apache.log4j.Logger/getLogger ^String (str logger-ns#))))))))
170175

171176
(defn log4j2-factory
172177
"Returns a Log4j2-based implementation of the LoggerFactory protocol, or nil if
173178
not available."
174179
[]
175-
(try
176-
(Class/forName "org.apache.logging.log4j.Logger")
180+
(when (class-found? "org.apache.logging.log4j.Logger")
177181
(eval
178182
`(let [levels# {:trace org.apache.logging.log4j.Level/TRACE
179183
:debug org.apache.logging.log4j.Level/DEBUG
@@ -202,15 +206,13 @@
202206
(name [_#]
203207
"org.apache.logging.log4j")
204208
(get-logger [_# logger-ns#]
205-
(org.apache.logging.log4j.LogManager/getLogger ^String (str logger-ns#))))))
206-
(catch Exception e nil)))
209+
(org.apache.logging.log4j.LogManager/getLogger ^String (str logger-ns#))))))))
207210

208211
(defn jul-factory
209212
"Returns a java.util.logging-based implementation of the LoggerFactory protocol,
210213
or nil if not available."
211214
[]
212-
(try
213-
(Class/forName "java.util.logging.Logger")
215+
(when (class-found? "java.util.logging.Logger")
214216
(eval
215217
`(let [levels# {:trace java.util.logging.Level/FINEST
216218
:debug java.util.logging.Level/FINE
@@ -234,8 +236,7 @@
234236
(name [_#]
235237
"java.util.logging")
236238
(get-logger [_# logger-ns#]
237-
(java.util.logging.Logger/getLogger (str logger-ns#))))))
238-
(catch Exception e nil)))
239+
(java.util.logging.Logger/getLogger (str logger-ns#))))))))
239240

240241
(defn find-factory
241242
"Returns the first non-nil value from slf4j-factory, cl-factory,

src/test/clojure/clojure/tools/logging/test_impl.clj

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,109 @@
22
(:use clojure.test)
33
(:require [clojure.tools.logging.impl :as impl]))
44

5+
6+
(deftest test-disabled-logger-factory
7+
(is (= "disabled" (impl/name impl/disabled-logger-factory)))
8+
(is (identical? impl/disabled-logger (impl/get-logger impl/disabled-logger-factory nil))))
9+
10+
(deftest test-disabled-logger
11+
(is (= false (impl/enabled? impl/disabled-logger :fatal))))
12+
13+
(defn blacklist-loader [blacklist loader]
14+
(proxy [ClassLoader] []
15+
(loadClass [name]
16+
(if (contains? (set blacklist) name)
17+
(throw (ClassNotFoundException. name))
18+
(.loadClass loader name)))))
19+
20+
(deftest test-blacklist-loader
21+
(let [white "org.slf4j.Logger"
22+
black-1 "java.util.logging.Logger"
23+
black-2 "org.apache.log4j.Logger"
24+
orig-loader (.. Thread currentThread getContextClassLoader)
25+
blacklist-loader (blacklist-loader #{black-1 black-2} orig-loader)]
26+
(Class/forName white true orig-loader)
27+
(Class/forName black-1 true orig-loader)
28+
(Class/forName black-2 true orig-loader)
29+
(Class/forName white true blacklist-loader)
30+
(is (thrown? ClassNotFoundException
31+
(Class/forName black-1 true blacklist-loader)))
32+
(is (thrown? ClassNotFoundException
33+
(Class/forName black-2 true blacklist-loader)))))
34+
35+
(defmacro with-blacklist-loader [forbidden-set & body]
36+
`(let [forbidden-set# ~forbidden-set
37+
orig-loader# (.. Thread currentThread getContextClassLoader)
38+
this-loader# (blacklist-loader forbidden-set# orig-loader#)]
39+
(try
40+
(.. Thread currentThread (setContextClassLoader this-loader#))
41+
~@body
42+
(finally
43+
(.. Thread currentThread (setContextClassLoader orig-loader#))))))
44+
45+
(defn thread-loader []
46+
(.. Thread currentThread getContextClassLoader))
47+
48+
(deftest test-with-blacklist-loader
49+
(with-blacklist-loader #{"org.apache.log4j.Logger"}
50+
(Class/forName "org.slf4j.Logger" true (thread-loader))
51+
(is (thrown? ClassNotFoundException
52+
(Class/forName "org.apache.log4j.Logger" true (thread-loader)))))
53+
(Class/forName "org.apache.log4j.Logger" true (thread-loader)))
54+
55+
56+
(deftest test-class-found?
57+
(is (= true (impl/class-found? "org.apache.log4j.Logger")))
58+
(is (= true (impl/class-found? "org.slf4j.Logger")))
59+
(with-blacklist-loader #{"org.apache.log4j.Logger"}
60+
(is (= true (impl/class-found? "org.slf4j.Logger")))
61+
(is (= false (impl/class-found? "org.apache.log4j.Logger")))))
62+
63+
64+
(deftest test-find-factory
65+
(testing "has all loggers"
66+
(are [classname] (impl/class-found? classname)
67+
"org.slf4j.Logger"
68+
"org.apache.commons.logging.Log"
69+
"org.apache.logging.log4j.Logger"
70+
"org.apache.log4j.Logger"
71+
"java.util.logging.Logger"))
72+
73+
(testing "finds slf4j"
74+
(with-blacklist-loader []
75+
(is (= "org.slf4j" (impl/name (impl/find-factory))))))
76+
77+
(testing "finds cl"
78+
(with-blacklist-loader ["org.slf4j.Logger"]
79+
(is (= "org.apache.commons.logging" (impl/name (impl/find-factory))))))
80+
81+
(testing "finds log4j2"
82+
(with-blacklist-loader ["org.slf4j.Logger"
83+
"org.apache.commons.logging.Log"]
84+
(is (= "org.apache.logging.log4j" (impl/name (impl/find-factory))))))
85+
86+
(testing "finds log4j"
87+
(with-blacklist-loader ["org.slf4j.Logger"
88+
"org.apache.commons.logging.Log"
89+
"org.apache.logging.log4j.Logger"]
90+
(is (= "org.apache.log4j" (impl/name (impl/find-factory))))))
91+
92+
(testing "finds jul"
93+
(with-blacklist-loader ["org.slf4j.Logger"
94+
"org.apache.commons.logging.Log"
95+
"org.apache.logging.log4j.Logger"
96+
"org.apache.log4j.Logger"]
97+
(is (= "java.util.logging" (impl/name (impl/find-factory))))))
98+
99+
(testing "finds none"
100+
(with-blacklist-loader ["org.slf4j.Logger"
101+
"org.apache.commons.logging.Log"
102+
"org.apache.logging.log4j.Logger"
103+
"org.apache.log4j.Logger"
104+
"java.util.logging.Logger"]
105+
(is (thrown? RuntimeException (impl/find-factory))))))
106+
107+
5108
(let [[factory level] (try
6109
[(impl/log4j2-factory) (eval 'org.apache.logging.log4j.Level/FATAL)]
7110
(catch UnsupportedClassVersionError _

0 commit comments

Comments
 (0)